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

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