字節(jié)跳動(dòng)最愛(ài)考的前端面試題:JavaScript 基礎(chǔ)
注意:每道題前面出現(xiàn)的 (xx) 數(shù)字代表這道題出現(xiàn)的頻次,此 JS 基礎(chǔ)是基于 30+ 篇前端面經(jīng)整理出的問(wèn)題和對(duì)應(yīng)的回答、參考鏈接等。
(2)問(wèn):0.1 + 0.2 === 0.3 嘛?為什么?
JavaScirpt 使用 Number 類型來(lái)表示數(shù)字(整數(shù)或浮點(diǎn)數(shù)),遵循 IEEE 754 標(biāo)準(zhǔn),通過(guò) 64 位來(lái)表示一個(gè)數(shù)字(1 + 11 + 52)
1 符號(hào)位,0 表示正數(shù),1 表示負(fù)數(shù) s 11 指數(shù)位(e) 52 尾數(shù),小數(shù)部分(即有效數(shù)字)
最大安全數(shù)字:Number.MAX_SAFE_INTEGER = Math.pow(2, 53) - 1,轉(zhuǎn)換成整數(shù)就是 16 位,所以 0.1 === 0.1,是因?yàn)橥ㄟ^(guò) toPrecision(16) 去有效位之后,兩者是相等的。
在兩數(shù)相加時(shí),會(huì)先轉(zhuǎn)換成二進(jìn)制,0.1 和 0.2 轉(zhuǎn)換成二進(jìn)制的時(shí)候尾數(shù)會(huì)發(fā)生無(wú)限循環(huán),然后進(jìn)行對(duì)階運(yùn)算,JS 引擎對(duì)二進(jìn)制進(jìn)行截?cái)啵栽斐删葋G失。
所以總結(jié):精度丟失可能出現(xiàn)在進(jìn)制轉(zhuǎn)換和對(duì)階運(yùn)算中
參考鏈接
https://juejin.im/post/5b90e00e6fb9a05cf9080dff
(4)問(wèn):JS 數(shù)據(jù)類型
基本類型:Number、Boolean、String、null、undefined、symbol(ES6 新增的),BigInt(ES2020) 引用類型:Object,對(duì)象子類型(Array,F(xiàn)unction)
參考鏈接
https://juejin.im/post/5b2b0a6051882574de4f3d96 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Data_structures
問(wèn):JS 整數(shù)是怎么表示的?
通過(guò) Number 類型來(lái)表示,遵循 IEEE754 標(biāo)準(zhǔn),通過(guò) 64 位來(lái)表示一個(gè)數(shù)字,(1 + 11 + 52),最大安全數(shù)字是 Math.pow(2, 53) - 1,對(duì)于 16 位十進(jìn)制。(符號(hào)位 + 指數(shù)位 + 小數(shù)部分有效位)
問(wèn):Number() 的存儲(chǔ)空間是多大?如果后臺(tái)發(fā)送了一個(gè)超過(guò)最大自己的數(shù)字怎么辦
Math.pow(2, 53) ,53 為有效數(shù)字,會(huì)發(fā)生截?cái)啵扔?JS 能支持的最大數(shù)字。
(4)寫(xiě)代碼:實(shí)現(xiàn)函數(shù)能夠深度克隆基本類型
淺克?。?/p>
function?shallowClone(obj)?{
??let?cloneObj?=?{};
??
??for?(let?i?in?obj)?{
????cloneObj[i]?=?obj[i];
??}
??
??return?cloneObj;
}
深克隆:
考慮基礎(chǔ)類型 引用類型 RegExp、Date、函數(shù) 不是 JSON 安全的 會(huì)丟失 constructor,所有的構(gòu)造函數(shù)都指向 Object 破解循環(huán)引用
function?deepCopy(obj)?{
??if?(typeof?obj?===?'object')?{
????var?result?=?obj.constructor?===?Array???[]?:?{};
????
????for?(var?i?in?obj)?{
??????result[i]?=?typeof?obj[i]?===?'object'???deepCopy(obj[i])?:?obj[i];
????}
??}?else?{
????var?result?=?obj;
??}
??
??return?result;
}
問(wèn):事件流
事件流是網(wǎng)頁(yè)元素接收事件的順序,"DOM2級(jí)事件"規(guī)定的事件流包括三個(gè)階段:事件捕獲階段、處于目標(biāo)階段、事件冒泡階段。首先發(fā)生的事件捕獲,為截獲事件提供機(jī)會(huì)。然后是實(shí)際的目標(biāo)接受事件。最后一個(gè)階段是時(shí)間冒泡階段,可以在這個(gè)階段對(duì)事件做出響應(yīng)。雖然捕獲階段在規(guī)范中規(guī)定不允許響應(yīng)事件,但是實(shí)際上還是會(huì)執(zhí)行,所以有兩次機(jī)會(huì)獲取到目標(biāo)對(duì)象。
html>
<html?lang="en">
<head>
????<meta?charset="UTF-8">
????<title>事件冒泡title>
head>
<body>
????<div>
????????<p?id="parEle">我是父元素????<span?id="sonEle">我是子元素span>p>
????div>
body>
html>
<script?type="text/javascript">
var?sonEle?=?document.getElementById('sonEle');
var?parEle?=?document.getElementById('parEle');
parEle.addEventListener('click',?function?()?{
????alert('父級(jí)?冒泡');
},?false);
parEle.addEventListener('click',?function?()?{
????alert('父級(jí)?捕獲');
},?true);
sonEle.addEventListener('click',?function?()?{
????alert('子級(jí)冒泡');
},?false);
sonEle.addEventListener('click',?function?()?{
????alert('子級(jí)捕獲');
},?true);
script>
當(dāng)容器元素及嵌套元素,即在捕獲階段又在冒泡階段調(diào)用事件處理程序時(shí):事件按DOM事件流的順序執(zhí)行事件處理程序:
父級(jí)捕獲 子級(jí)冒泡 子級(jí)捕獲 父級(jí)冒泡
且當(dāng)事件處于目標(biāo)階段時(shí),事件調(diào)用順序決定于綁定事件的書(shū)寫(xiě)順序,按上面的例子為,先調(diào)用冒泡階段的事件處理程序,再調(diào)用捕獲階段的事件處理程序。依次alert出“子集冒泡”,“子集捕獲”。
IE 兼容
attchEvent('on' + type, handler) detachEvent('on' + type, handler)
參考鏈接
https://juejin.im/entry/5826ba9d0ce4630056f85e07
問(wèn):事件是如何實(shí)現(xiàn)的?
基于發(fā)布訂閱模式,就是在瀏覽器加載的時(shí)候會(huì)讀取事件相關(guān)的代碼,但是只有實(shí)際等到具體的事件觸發(fā)的時(shí)候才會(huì)執(zhí)行。
比如點(diǎn)擊按鈕,這是個(gè)事件(Event),而負(fù)責(zé)處理事件的代碼段通常被稱為事件處理程序(Event Handler),也就是「啟動(dòng)對(duì)話框的顯示」這個(gè)動(dòng)作。
在 Web 端,我們常見(jiàn)的就是 DOM 事件:
DOM0 級(jí)事件,直接在 html 元素上綁定 on-event,比如 onclick,取消的話,dom.onclick = null,同一個(gè)事件只能有一個(gè)處理程序,后面的會(huì)覆蓋前面的。 DOM2 級(jí)事件,通過(guò) addEventListener 注冊(cè)事件,通過(guò) removeEventListener 來(lái)刪除事件,一個(gè)事件可以有多個(gè)事件處理程序,按順序執(zhí)行,捕獲事件和冒泡事件 DOM3級(jí)事件,增加了事件類型,比如 UI 事件,焦點(diǎn)事件,鼠標(biāo)事件
參考鏈接
https://zhuanlan.zhihu.com/p/73091706
問(wèn):new 一個(gè)函數(shù)發(fā)生了什么
構(gòu)造調(diào)用:
創(chuàng)造一個(gè)全新的對(duì)象 這個(gè)對(duì)象會(huì)被執(zhí)行 [[Prototype]] 連接,將這個(gè)新對(duì)象的 [[Prototype]] 鏈接到這個(gè)構(gòu)造函數(shù).prototype 所指向的對(duì)象 這個(gè)新對(duì)象會(huì)綁定到函數(shù)調(diào)用的 this 如果函數(shù)沒(méi)有返回其他對(duì)象,那么 new 表達(dá)式中的函數(shù)調(diào)用會(huì)自動(dòng)返回這個(gè)新對(duì)象
問(wèn):new 一個(gè)構(gòu)造函數(shù),如果函數(shù)返回 return {} 、 return null , return 1 , return true 會(huì)發(fā)生什么情況?
如果函數(shù)返回一個(gè)對(duì)象,那么new 這個(gè)函數(shù)調(diào)用返回這個(gè)函數(shù)的返回對(duì)象,否則返回 new 創(chuàng)建的新對(duì)象
問(wèn):symbol?有什么用處
可以用來(lái)表示一個(gè)獨(dú)一無(wú)二的變量防止命名沖突。但是面試官問(wèn)還有嗎?我沒(méi)想出其他的用處就直接答我不知道了,還可以利用?symbol?不會(huì)被常規(guī)的方法(除了?Object.getOwnPropertySymbols?外)遍歷到,所以可以用來(lái)模擬私有變量。
主要用來(lái)提供遍歷接口,布置了?symbol.iterator?的對(duì)象才可以使用?for···of?循環(huán),可以統(tǒng)一處理數(shù)據(jù)結(jié)構(gòu)。調(diào)用之后回返回一個(gè)遍歷器對(duì)象,包含有一個(gè) next 方法,使用 next 方法后有兩個(gè)返回值 value 和 done 分別表示函數(shù)當(dāng)前執(zhí)行位置的值和是否遍歷完畢。
Symbol.for() 可以在全局訪問(wèn) symbol
(3)問(wèn):閉包是什么?
閉包是指有權(quán)訪問(wèn)另外一個(gè)函數(shù)作用域中的變量的函數(shù)
JavaScript代碼的整個(gè)執(zhí)行過(guò)程,分為兩個(gè)階段,代碼編譯階段與代碼執(zhí)行階段。編譯階段由編譯器完成,將代碼翻譯成可執(zhí)行代碼,這個(gè)階段作用域規(guī)則會(huì)確定。執(zhí)行階段由引擎完成,主要任務(wù)是執(zhí)行可執(zhí)行代碼,執(zhí)行上下文在這個(gè)階段創(chuàng)建。

什么是作業(yè)域?
ES5 中只存在兩種作用域:全局作用域和函數(shù)作用域。在 JavaScript 中,我們將作用域定義為一套規(guī)則,這套規(guī)則用來(lái)管理引擎如何在當(dāng)前作用域以及嵌套子作用域中根據(jù)標(biāo)識(shí)符名稱進(jìn)行變量(變量名或者函數(shù)名)查找
什么是作用域鏈?
首先要了解作用域鏈,當(dāng)訪問(wèn)一個(gè)變量時(shí),編譯器在執(zhí)行這段代碼時(shí),會(huì)首先從當(dāng)前的作用域中查找是否有這個(gè)標(biāo)識(shí)符,如果沒(méi)有找到,就會(huì)去父作用域查找,如果父作用域還沒(méi)找到繼續(xù)向上查找,直到全局作用域?yàn)橹?,而作用域鏈,就是有當(dāng)前作用域與上層作用域的一系列變量對(duì)象組成,它保證了當(dāng)前執(zhí)行的作用域?qū)Ψ显L問(wèn)權(quán)限的變量和函數(shù)的有序訪問(wèn)。
閉包產(chǎn)生的本質(zhì)
當(dāng)前環(huán)境中存在指向父級(jí)作用域的引用
什么是閉包
閉包是一種特殊的對(duì)象,它由兩部分組成:執(zhí)行上下文(代號(hào) A),以及在該執(zhí)行上下文中創(chuàng)建的函數(shù) (代號(hào) B),當(dāng) B 執(zhí)行時(shí),如果訪問(wèn)了 A 中變量對(duì)象的值,那么閉包就會(huì)產(chǎn)生,且在 Chrome 中使用這個(gè)執(zhí)行上下文 A 的函數(shù)名代指閉包。
一般如何產(chǎn)生閉包
返回函數(shù) 函數(shù)當(dāng)做參數(shù)傳遞
閉包的應(yīng)用場(chǎng)景
柯里化 bind 模塊
參考文章
https://segmentfault.com/a/1190000012646221
問(wèn):NaN 是什么,用 typeof 會(huì)輸出什么?
Not a Number,表示非數(shù)字,typeof NaN === 'number'
(2)問(wèn):JS 隱式轉(zhuǎn)換,顯示轉(zhuǎn)換
一般非基礎(chǔ)類型進(jìn)行轉(zhuǎn)換時(shí)會(huì)先調(diào)用 valueOf,如果 valueOf 無(wú)法返回基本類型值,就會(huì)調(diào)用 toString
字符串和數(shù)字
"+" 操作符,如果有一個(gè)為字符串,那么都轉(zhuǎn)化到字符串然后執(zhí)行字符串拼接 "-" 操作符,轉(zhuǎn)換為數(shù)字,相減 (-a, a * 1 a/1) 都能進(jìn)行隱式強(qiáng)制類型轉(zhuǎn)換
[]?+?{}?和?{}?+?[]
布爾值到數(shù)字
1 + true = 2 1 + false = 1
轉(zhuǎn)換為布爾值
for 中第二個(gè) while if 三元表達(dá)式 || (邏輯或) && (邏輯與)左邊的操作數(shù)
符號(hào)
不能被轉(zhuǎn)換為數(shù)字 能被轉(zhuǎn)換為布爾值(都是 true) 可以被轉(zhuǎn)換成字符串 "Symbol(cool)"
寬松相等和嚴(yán)格相等
寬松相等允許進(jìn)行強(qiáng)制類型轉(zhuǎn)換,而嚴(yán)格相等不允許
字符串與數(shù)字
轉(zhuǎn)換為數(shù)字然后比較
其他類型與布爾類型
先把布爾類型轉(zhuǎn)換為數(shù)字,然后繼續(xù)進(jìn)行比較
對(duì)象與非對(duì)象
執(zhí)行對(duì)象的 ToPrimitive(對(duì)象)然后繼續(xù)進(jìn)行比較
假值列表
undefined null false +0, -0, NaN ""
(2)問(wèn):了解 this 嘛,bind,call,apply 具體指什么
它們都是函數(shù)的方法
call: Array.prototype.call(this, args1, args2])apply: Array.prototype.apply(this, [args1, args2]) :ES6 之前用來(lái)展開(kāi)數(shù)組調(diào)用, foo.appy(null, []),ES6 之后使用 ... 操作符
New 綁定 > 顯示綁定 > 隱式綁定 > 默認(rèn)綁定 如果需要使用 bind 的柯里化和 apply 的數(shù)組解構(gòu),綁定到 null,盡可能使用 Object.create(null) 創(chuàng)建一個(gè) DMZ 對(duì)象
四條規(guī)則:
默認(rèn)綁定,沒(méi)有其他修飾(bind、apply、call),在非嚴(yán)格模式下定義指向全局對(duì)象,在嚴(yán)格模式下定義指向 undefined
function?foo()?{
??console.log(this.a);?
}
var?a?=?2;
foo();
隱式綁定:調(diào)用位置是否有上下文對(duì)象,或者是否被某個(gè)對(duì)象擁有或者包含,那么隱式綁定規(guī)則會(huì)把函數(shù)調(diào)用中的 this 綁定到這個(gè)上下文對(duì)象。而且,對(duì)象屬性鏈只有上一層或者說(shuō)最后一層在調(diào)用位置中起作用
function?foo()?{
??console.log(this.a);
}
var?obj?=?{
??a:?2,
??foo:?foo,
}
obj.foo();?//?2
顯示綁定:通過(guò)在函數(shù)上運(yùn)行 call 和 apply ,來(lái)顯示的綁定 this
function?foo()?{
??console.log(this.a);
}
var?obj?=?{
??a:?2
};
foo.call(obj);
顯示綁定之硬綁定
function?foo(something)?{
??console.log(this.a,?something);
??
??return?this.a?+?something;
}
function?bind(fn,?obj)?{
??return?function()?{
????return?fn.apply(obj,?arguments);
??};
}
var?obj?=?{
??a:?2
}
var?bar?=?bind(foo,?obj);
New 綁定,new 調(diào)用函數(shù)會(huì)創(chuàng)建一個(gè)全新的對(duì)象,并將這個(gè)對(duì)象綁定到函數(shù)調(diào)用的 this。
New 綁定時(shí),如果是 new 一個(gè)硬綁定函數(shù),那么會(huì)用 new 新建的對(duì)象替換這個(gè)硬綁定 this,
function?foo(a)?{
??this.a?=?a;
}
var?bar?=?new?foo(2);
console.log(bar.a)
(4)問(wèn):手寫(xiě) bind、apply、call
//?call
Function.prototype.call?=?function?(context,?...args)?{
??context?=?context?||?window;
??
??const?fnSymbol?=?Symbol("fn");
??context[fnSymbol]?=?this;
??
??context[fnSymbol](...args);
??delete?context[fnSymbol];
}
//?apply
Function.prototype.apply?=?function?(context,?argsArr)?{
??context?=?context?||?window;
??
??const?fnSymbol?=?Symbol("fn");
??context[fnSymbol]?=?this;
??
??context[fnSymbol](...argsArr);
??delete?context[fnSymbol];
}
//?bind
Function.prototype.bind?=?function?(context,?...args)?{
??context?=?context?||?window;
??const?fnSymbol?=?Symbol("fn");
??context[fnSymbol]?=?this;
??
??return?function?(..._args)?{
????_args?=?_args.concat(args);
????
????context[fnSymbol](..._args);
????delete?context[fnSymbol];???
??}
}
????
(3)問(wèn):setTimeout(fn, 0)多久才執(zhí)行,Event Loop
setTimeout 按照順序放到隊(duì)列里面,然后等待函數(shù)調(diào)用棧清空之后才開(kāi)始執(zhí)行,而這些操作進(jìn)入隊(duì)列的順序,則由設(shè)定的延遲時(shí)間來(lái)決定
手寫(xiě)題:Promise 原理
class?MyPromise?{
??constructor(fn)?{
????this.resolvedCallbacks?=?[];
????this.rejectedCallbacks?=?[];
????
????this.state?=?'PENDING';
????this.value?=?'';
????
????fn(this.resolve.bind(this),?this.reject.bind(this));
????
??}
??
??resolve(value)?{
????if?(this.state?===?'PENDING')?{
??????this.state?=?'RESOLVED';
??????this.value?=?value;
??????
??????this.resolvedCallbacks.map(cb?=>?cb(value));???
????}
??}
??
??reject(value)?{
????if?(this.state?===?'PENDING')?{
??????this.state?=?'REJECTED';
??????this.value?=?value;
??????
??????this.rejectedCallbacks.map(cb?=>?cb(value));
????}
??}
??
??then(onFulfilled,?onRejected)?{
????if?(this.state?===?'PENDING')?{
??????this.resolvedCallbacks.push(onFulfilled);
??????this.rejectedCallbacks.push(onRejected);
??????
????}
????
????if?(this.state?===?'RESOLVED')?{
??????onFulfilled(this.value);
????}
????
????if?(this.state?===?'REJECTED')?{
??????onRejected(this.value);
????}
??}
}
??????
問(wèn):js腳本加載問(wèn)題,async、defer問(wèn)題
如果依賴其他腳本和 DOM 結(jié)果,使用 defer 如果與 DOM 和其他腳本依賴不強(qiáng)時(shí),使用 async
參考資料
問(wèn):如何判斷一個(gè)對(duì)象是不是空對(duì)象?
Object.keys(obj).length === 0
問(wèn):?
一区二区男女无码
|
国产无码一二三
|
av777777
|
婷婷五月天天爽
|
亚洲影院之台湾
|
