前端面試題系列之-原生js篇

隱式類型轉(zhuǎn)換相關(guān)

字符串連接符與算數(shù)運算符

  • 字符串連接符+:會把其他數(shù)據(jù)類型調(diào)用String()方法轉(zhuǎn)換成字符串然后拼接
  • 算數(shù)運算符+:會把其他數(shù)據(jù)類型調(diào)用Number()方法轉(zhuǎn)換成數(shù)字然后做加法計算

關(guān)系運算符

關(guān)系運算符會把其他數(shù)據(jù)轉(zhuǎn)換成number之后再比較

  • 一個為字符串另一個為數(shù)字,則將字符串轉(zhuǎn)換成數(shù)字比較
  • 兩個都為字符串則依次比較兩個的charCodeAt()值
  • undefined與null互相比較為相等,與其他類型比較為不等
  • NaN與任何數(shù)據(jù)都不等
  • 復(fù)雜數(shù)據(jù)類型會先調(diào)用valueOf()再調(diào)用toString()轉(zhuǎn)換成字符串,然后再轉(zhuǎn)換成number

邏輯非運算符

  • 有邏輯非運算符的會將數(shù)據(jù)轉(zhuǎn)換成布爾類型
  • 除了0、-0、NaN、undefined、null、""、false以外都為true

各類型隱式轉(zhuǎn)換規(guī)則

  • String:變量 + 字符串
  • Number:變量 + 自增自減運算符,算術(shù)運算符,關(guān)系運算符
  • Boolean:變量 + 邏輯非運算符

字符串類型轉(zhuǎn)為數(shù)值類型:

  • 字符串任意位置出現(xiàn)任意非數(shù)字、非空格的字符,均轉(zhuǎn)為NaN
  • 數(shù)字中間存在空格,轉(zhuǎn)為NaN

閉包相關(guān)

什么是閉包

閉包就是一個函數(shù),這個函數(shù)能夠訪問其他函數(shù)的作用域中的變量。

優(yōu)點

  • 封裝變量,模仿塊級作用域

缺點

  • this指向問題
  • 內(nèi)存泄漏問題
  • 變量發(fā)生變化問題

作用域和變量提升相關(guān)

作用域

作用域分為全局作用域、函數(shù)作用域及塊級作用域

變量提升

js在執(zhí)行一個作用域的代碼之前,會先將變量聲明及函數(shù)聲明提到整個作用域頂部,隨后依次執(zhí)行代碼,其中若變量與函數(shù)同名,函數(shù)優(yōu)先級要高于變量。
即:

  • 將變量聲明與函數(shù)聲明提到作用域頂部
  • 函數(shù)聲明優(yōu)先級高于變量聲明
  • 依次執(zhí)行代碼

作用域鏈

如果當(dāng)前作用域中找不到變量,則會想上層作用域查找,依次往上查找,形成作用域鏈

NaN是什么?有什么特別之處?

NaN 是 not a number 的縮寫,代表非數(shù)字值的特殊值,該屬性用于指示某個值不是數(shù)字,是一個全局對象的屬性
特別之處:

  • NaN 屬性的初始值就是 NaN,和 Number.NaN 的值一樣
  • 在現(xiàn)代瀏覽器中(ES5),NaN是一個不可配置(non-configurable),不可寫(non-writable)的屬性.但是在ES3中,這個屬性的值是可以更改的,但是也應(yīng)該避免覆蓋
  • 編碼中很少直接用到 NaN ,通常都是計算失敗的時候,作為Math的某個方法的返回值出現(xiàn)(例如:Math.sqrt(-1)),或者嘗試將一個字符串解析成為數(shù)字,但是失敗了的時候(parseInt("blabla"))
  • NaN和任何值比較,都不相等,包括它自己NaN是number類型 typeof NaN //'number'

== 和 === 的區(qū)別

兩等是值判斷,會調(diào)用隱式類型轉(zhuǎn)換,不判斷類型
三等是值判斷加類型判斷,判斷兩個值是否嚴(yán)格相等

typeof和instanceof的區(qū)別

typeof

用于判斷數(shù)據(jù)類型,返回值為6個字符串,分別為string、Boolean、number、function、object、undefined。其中null會返回object結(jié)果。

instanceof

instanceof用來判斷對象,判斷方法是根據(jù)對象的原型鏈依次向下查詢

js的數(shù)據(jù)類型有哪些

JS原生有5種簡單數(shù)據(jù)類型:Undefined,Null,Boolean,NumberString。還有一個復(fù)雜數(shù)據(jù)類型Object。 函數(shù)在JS中是對象,而不是一種數(shù)據(jù)類型。

undefined和null的區(qū)別

Undefined類型為變量聲明了但是沒有初始化(var box),而Null表示一個空對象引用(var box = null)。注意:未初始化的變量和賦值為 null 的 變量會相等,可通過typeof或者使用三等進(jìn)行判斷

call、apply、bind的區(qū)別

call 和 apply 是為了改變函數(shù)體內(nèi)部 this 的指向。對于 apply、call 二者而言,作用完全一樣,只是接受參數(shù)的方式不太一樣。bind()最簡單的用法是創(chuàng)建一個函數(shù),使這個函數(shù)不論怎么調(diào)用都有同樣的this值。

示例:

function func(arg1, arg2) {};

func.call(this, arg1, arg2);
func.apply(this, [arg1, arg2])
var obj = {
    a: 123,
};
 
var foo = {
    getV: function(k) {
        return this.a + k;
    }
}
 
console.log(foo.getV.bind(obj, 1)());  //124
console.log(foo.getV.call(obj, 1));    //124
console.log(foo.getV.apply(obj, [1]));   //124

區(qū)別:

  • 傳參不同,調(diào)用不同。bind 是返回對應(yīng)函數(shù),便于稍后調(diào)用;apply 、call 則是立即調(diào)用 。

js的假值有哪些

0、-0、NaNundefined、null、""、false

js面向?qū)ο笈c原型、原型鏈相關(guān)

參考鏈接

原型(對象屬性)

Javascript規(guī)定,每個函數(shù)都有一個prototype對象屬性。只有函數(shù)才有prototype屬性。指向另一個對象(原型鏈上面的)。prototype(對象屬性)的所有屬性和方法,都會被構(gòu)造的實例繼承。這意味著,我們可以把那些不變的屬性和方法,直接定義在prototype對象屬性上。

prototype就是調(diào)用構(gòu)造函數(shù)所創(chuàng)建的那個實例對象的原型(proto)。

prototype可以讓所有對象實例共享它所包含的屬性和方法。也就是說,不必在構(gòu)造函數(shù)中定義對象信息,而是可以直接將這些信息添加到原型中。

原型鏈 (JS原型與原型鏈繼承)

實例對象與原型之間的連接,叫做原型鏈。proto( 隱式連接 )
JS在創(chuàng)建對象的時候,都有一個叫做proto的內(nèi)置屬性,用于指向創(chuàng)建它的函數(shù)對象的原型對象prototype。
內(nèi)部原型(proto)和構(gòu)造器的原型(prototype)
1、每個對象都有一個proto屬性,原型鏈上的對象正是依靠這個屬性連結(jié)在一起
2、作為一個對象,當(dāng)你訪問其中的一個屬性或方法的時候,如果這個對象中沒有這個 方法或?qū)傩?,那么Javascript引擎將會訪問這個對象的proto屬性所指向上一個對 象,并在那個對象中查找指定的方法或?qū)傩?,如果不能找到,那就會繼續(xù)通過那個對象 的proto屬性指向的對象進(jìn)行向上查找,直到這個鏈表結(jié)束。

淺談constructor

在 Javascript 語言中,constructor 屬性是專門為 function 而設(shè)計的,它存在于每一個 function 的prototype 屬性中。這個 constructor 保存了指向 function 的一個引用。

__proto__

JS 在創(chuàng)建對象(不論是普通對象還是函數(shù)對象)的時候,都有一個叫做 __proto__ 的內(nèi)置屬性,用于指向創(chuàng)建它的構(gòu)造函數(shù)的原型對象。

    var obj = []
    console.log(obj.__proto__ === Array.prototype)  //true

示例題

    function Foo() {}
    var f1 = new Foo()

請回答以下問題:

  1. f1.__proto__ ===
  2. f1.constructor ===
  3. f1.prototype ===
  4. Foo.__proto__ ===
  5. Foo.prototype ===
  6. Foo.constructor ===
  7. Foo.prototype.constructor ===
  8. Foo.prototype.__proto__ ===

繼承的幾種寫法及優(yōu)缺點

JS繼承的實現(xiàn)方式

this指向相關(guān)

  • this 永遠(yuǎn)指向函數(shù)運行時所在的對象,而不是函數(shù)被創(chuàng)建時所在的對象。
  • this的指向在函數(shù)定義的時候是確定不了的,只有函數(shù)執(zhí)行的時候才能確定this到底指向誰,實際上this的最終指向的是那個調(diào)用它的對象

js new一個對象都經(jīng)過了什么

new對象:

function Person(name, age) {
  this.name = name;
  this.age = age;
}
var person = new Person("Alice", 23);

new一個對象的四個過程:1、創(chuàng)建一個空對象?

var obj = new Object();

2、讓Person中的this指向obj,并執(zhí)行Person的函數(shù)體?

var result = Person.call(obj);

3、設(shè)置原型鏈,將obj的proto成員指向了Person函數(shù)對象的prototype成員對象?

obj.__proto__ = Person.prototype;

4、判斷Person的返回值類型,如果是值類型,返回obj。如果是引用類型,就返回這個引用類型的對象。

if (typeof(result) == "object")
  person = result;
else
  person = obj;

宏任務(wù)和微任務(wù)

  • 宿主環(huán)境提供的叫宏任務(wù),宿主環(huán)境包括瀏覽器和node
  • 宏任務(wù)有:
    • script
    • setTimeout
    • setInterval
    • setImmediate
    • requestAnimationFrame
    • I/O
    • UI渲染
  • 由語言標(biāo)準(zhǔn)提供的叫微任務(wù),即由語言標(biāo)準(zhǔn)(ECMA)提供的就是微任務(wù),比如ES6提供的promise。
  • 微任務(wù)有
    • process.nextTick
    • MutationObserver
    • Promise

執(zhí)行過程

  • js引擎將代碼按照類別分到宏任務(wù)和微任務(wù)隊列中
  • 執(zhí)行宏任務(wù),執(zhí)行完成,清空微任務(wù)隊列
  • 執(zhí)行下一個宏任務(wù),執(zhí)行完成后,清空微任務(wù)隊列
  • 依次執(zhí)行宏任務(wù)隊列的下一個宏任務(wù),執(zhí)行完成后,清空微任務(wù)隊列

怎樣理解setTimeout 執(zhí)行誤差

如果當(dāng)前 執(zhí)行棧 所花費的時間大于 定時器 時間,那么定時器的回調(diào)在 宏任務(wù)(macrotask) 里,來不及去調(diào)用,所以這個時間會有誤差。

如何實現(xiàn)深淺拷貝

淺拷貝

// 1. ...實現(xiàn)
let copy1 = {...{x:1}}
// 2. Object.assign實現(xiàn)
let copy2 = Object.assign({}, {x:1})
// 數(shù)組采用slice、concat方法

深拷貝

// 1. JOSN.stringify()/JSON.parse()
let obj = {a: 1, b: {x: 3}}
JSON.parse(JSON.stringify(obj))

// 2. 遞歸拷貝
function deepClone(obj) {
  let copy = obj instanceof Array ? [] : {}
  for (let i in obj) {
    if (obj.hasOwnProperty(i)) {
      copy[i] = typeof obj[i] === 'object' ? deepClone(obj[i]) : obj[i]
    }
  }
  return copy
}

JSON.stringify()的缺點

  • 會忽略 undefined
  • 會忽略 symbol
  • 不能序列化函數(shù)
  • 不能解決循環(huán)引用的對象
  • 不能正確處理new Date()
  • 不能處理正則

數(shù)組去重的幾種寫法

let list = [1, 2, 3, 4, 5, 2, 2, 2, 7, 6, 4, 8, 1];
// 創(chuàng)建新數(shù)組,判斷數(shù)據(jù)是否在新數(shù)組中
function arrToRepeat1(arr) {
    let repeatList = [];
    list.forEach(value => {
        if (!repeatList.includes(value)) repeatList.push(value);
    })
    return repeatList;
}
console.log(arrToRepeat1(list));
// 創(chuàng)建新數(shù)組,通過下標(biāo)判斷是否是重復(fù)的值
function arrToRepeat2(arr) {
    let repeatList = [];
    list.forEach((value, index) => {
        // 左側(cè)的為第一個值的索引,右側(cè)為重復(fù)的當(dāng)前的值的索引
        if (arr.indexOf(value) === index) repeatList.push(value);
    })
    return repeatList;
}
console.log(arrToRepeat2(list));
// 使用set集合
console.log([...new Set(list)]);

數(shù)組排序的問題

默認(rèn)數(shù)組排數(shù)函數(shù)sort有問題,不指定排序方法時,元素會按照轉(zhuǎn)換為的字符串的諸個字符的Unicode位點進(jìn)行排序

常用解決方法


list.sort((a, b) => a - b) // 從小到大
list.sort((a, b) => b - a) // 從大到小

數(shù)組降維

array = array.reduce((acc, val) => acc.concat(val), []);  // 一層扁平化
array = [].concat(...array);  // 一層扁平化
array = array.flat();  // 一層扁平化

手寫防抖和節(jié)流

js垃圾回收機制,怎么理解js中的內(nèi)存泄露

垃圾回收機制

垃圾回收是釋放已經(jīng)不再需要的變量等,來減少系統(tǒng)所占用的內(nèi)存。

標(biāo)記清除法

工作原理:
當(dāng)變量進(jìn)入環(huán)境時(例如在函數(shù)中聲明一個變量),將這個變量標(biāo)記為“進(jìn)入環(huán)境”,當(dāng)變量離開環(huán)境時,則將其標(biāo)記為“離開環(huán)境”。標(biāo)記“離開環(huán)境”的就回收內(nèi)存。
工作流程:

  • 垃圾收集器會在運行的時候會給存儲在內(nèi)存中的所有變量都加上標(biāo)記。
  • 去掉環(huán)境中的變量以及被環(huán)境中的變量引用的變量的標(biāo)記。
  • 那些還存在標(biāo)記的變量被視為準(zhǔn)備刪除的變量。
  • 最后垃圾收集器會執(zhí)行最后一步內(nèi)存清除的工作,銷毀那些帶標(biāo)記的值并回收它們所占用的內(nèi)存空間。
引用計數(shù)法

跟蹤記錄每個值的引用次數(shù)(值)
流程:

  • 聲明一個變量,并給這個變量賦值,這個值的引用次數(shù)就是1
  • 同一個值被賦給另一個變量,這個值的引用次數(shù)+1
  • 變量的值被更改了 那原本那個值的引用次數(shù)-1
  • 引用次數(shù)為0時,說明沒辦法訪問這個值了
  • 垃圾收集器下一次運行時,會釋放引用次數(shù)為0的值所占的內(nèi)存

缺點:循環(huán)引用將不能被自動回收。

內(nèi)存泄漏

內(nèi)存泄漏是由于疏忽或錯誤造成程序未能釋放那些已經(jīng)不再使用的內(nèi)存,造成內(nèi)存的浪費。

引起內(nèi)存泄漏的情況
  • 意外的全局變量
  • 被遺忘的定時器和定時器的回調(diào)函數(shù)
  • 閉包
  • 循環(huán)引用問題
  • 沒有清理的DOM元素引用

DOM事件模型是什么

事件處理的幾種寫法

移動端的touch事件

事件委托/事件代理

target、currentTarget的區(qū)別?

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容