前端進階面試-JS面試題(阿里、字節、騰訊、美團面試高頻)


1. ES5和ES6繼承方式區別

ES5定義類以函數形式, 以prototype來實現繼承
ES6以class形式定義類, 以extend形式繼承

2. Generator了解

ES6 提供的一種異步編程解決方案, Generator 函數是一個狀態機,封裝了多個內部狀態。

function* helloWorldGenerator() { yield 'hello'; yield 'world'; return 'ending'; } var hw = helloWorldGenerator(); 

調用后返回指向內部狀態的指針, 調用next()才會移向下一個狀態, 參數:

hw.next() // { value: 'hello', done: false } hw.next() // { value: 'world', done: false } hw.next() // { value: 'ending', done: true } hw.next() // { value: undefined, done: true } 

3. 手寫Promise實現

var myPromise = new Promise((resolve, reject) => { // 需要執行的代碼 ... if (/* 異步執行成功 */) { resolve(value) } else if (/* 異步執行失敗 */) { reject(error) } }) myPromise.then((value) => { // 成功后調用, 使用value值 }, (error) => { // 失敗后調用, 獲取錯誤信息error }) 

4. Promise優缺點

優點: 解決回調地獄, 對異步任務寫法更標准化與簡潔化
缺點: 首先,無法取消Promise,一旦新建它就會立即執行,無法中途取消; 其次,如果不設置回調函數,Promise內部拋出的錯誤,不會反應到外部; 第三,當處於pending狀態時,無法得知目前進展到哪一個階段(剛剛開始還是即將完成).
極簡版promise封裝:

function promise () { this.msg = '' // 存放value和error this.status = 'pending' var that = this var process = arguments[0] process (function () { that.status = 'fulfilled' that.msg = arguments[0] }, function () { that.status = 'rejected' that.msg = arguments[0] }) return this } promise.prototype.then = function () { if (this.status === 'fulfilled') { arguments[0](this.msg) } else if (this.status === 'rejected' && arguments[1]) { arguments[1](this.msg) } } 

5. 觀察者模式

又稱發布-訂閱模式, 舉例子說明.
實現: 發布者管理訂閱者隊列, 並有新消息推送功能. 訂閱者僅關注更新就行

6. 手寫實現bind

Function.prototype.bind = function () { // 保存原函數 var self = this // 取出第一個參數作為上下文, 相當於[].shift.call(arguments) var context = Array.prototype.shift.call(arguments) // 取剩余的參數作為arg; 因為arguments是偽數組, 所以要轉化為數組才能使用數組方法 var arg = Array.prototype.slice.call(arguments) // 返回一個新函數 return function () { // 綁定上下文並傳參 self.apply(context, Array.prototype.concat.call(arg, Array.prototype.slice.call(arguments))) } } 

7. 手寫實現4種繼承

function Father () {} function Child () {} // 1\. 原型繼承 Child.prototype = new Father() // 2\. 構造繼承 function Child (name) { Father.call(this, name) } // 3\. 組合繼承 function Child (name) { Father.call(this, name) } Child.prototype = new Father() // 4\. 寄生繼承 function cloneObj (o) { var clone = object.create(o) clone.sayName = ... return clone } // 5\. 寄生組合繼承 // 6\. ES6 class extend繼承 

8. css菊花圖

四個小圓點一直旋轉

// 父標簽 animation: antRotate 1.2s infinite linear; // 子標簽 animation: antSpin 1s infinite linear; @keyframe antSpin { to { opacity: 1 } } @keyframe antRotate { to { transform: rotate(405) } } // animation-delay: 逐個延遲0.4s 

9. http狀態碼

1**: 服務器收到請求, 需請求者進一步操作
2**: 請求成功
3**: 重定向, 資源被轉移到其他URL了
4**: 客戶端錯誤, 請求語法錯誤或沒有找到相應資源
5**: 服務端錯誤, server error
304: Not Modified. 指定日期后未修改, 不返回資源

10. 變量對象

變量對象,是執行上下文中的一部分,可以抽象為一種 數據作用域,其實也可以理解為就是一個簡單的對象,它存儲着該執行上下文中的所有 變量和函數聲明(不包含函數表達式)。
活動對象 (AO): 當變量對象所處的上下文為 active EC 時,稱為活動對象。

11. async和await:

Generator函數的語法糖,將*改成async,將yield換成await。
是對Generator函數的改進, 返回promise。
異步寫法同步化,遇到await先返回,執行完異步再執行接下來的.
內置執行器, 無需next()

12. 算法和數據結構:

算法:
解決具體問題所需要的解決方法。執行效率最快的最優算法。時間復雜度。輸入,輸出,有窮性,確定性,可行性。冒泡排序,二叉樹遍歷,最長回文,二分查找,指針,鏈表等,堆棧,隊列等。力扣,codewar,算法導論。
數據結構:
邏輯結構:集合、線性、樹形、圖形結構
物理結構:順序、鏈式存儲結構

13. 封裝JSONP

function jsonp ({url, param, callback}) { return new Promise((resolve, reject) => { var script = document.createElement('script') window.callback = function (data) { resolve(data) document.body.removeChild('script') } var param = {...param, callback} var arr = [] for (let key in param) { arr.push(`${key}=${param[key]}`) } script.src = `${url}?${arr.join('&')}` document.body.appendChild(script) }) } 

14. 手動實現map(forEach以及filter也類似)

// for循環實現 Array.prototype.myMap = function () { var arr = this var [fn, thisValue] = Array.prototype.slice.call(arguments) var result = [] for (var i = 0; i < arr.length; i++) { result.push(fn.call(thisValue, arr[i], i, arr)) } return result } var arr0 = [1, 2, 3] console.log(arr0.myMap(v => v + 1)) // forEach實現(reduce類似) Array.prototype.myMap = function (fn, thisValue) { var result = [] this.forEach((v, i, arr) => { result.push(fn.call(thisValue, v, i, arr)) }) return result } var arr0 = [1, 2, 3] console.log(arr0.myMap(v => v + 1)) 

15. js實現checkbox全選以及反選

<body> <button id="other">反選</button> <input type="checkbox" id="all" />全選 <input type="checkbox" class="check" />1 <input type="checkbox" class="check" />2 <input type="checkbox" class="check" />3 <script> var checkbox = document.getElementsByClassName('check') var checkAll = document.getElementById('all') var checkOther = document.getElementById('other') checkAll.onclick = function() { var flag = true for (var i = 0; i < checkbox.length; i++) { if (!checkbox[i].checked) flag = false } if (flag) { for (var i = 0; i < checkbox.length; i++) { checkbox[i].checked = false } } else { for (var i = 0; i < checkbox.length; i++) { checkbox[i].checked = true } } } checkOther.onclick = function() { for (var i = 0; i < checkbox.length; i++) { checkbox[i].checked = !checkbox[i].checked } } </script> </body> 

16. 對原型鏈的理解?prototype上都有哪些屬性

在js里,繼承機制是原型繼承。繼承的起點是 對象的原型(Object prototype)。
一切皆為對象,只要是對象,就會有 proto 屬性,該屬性存儲了指向其構造的指針。
Object prototype也是對象,其 proto 指向null。
對象分為兩種:函數對象和普通對象,只有函數對象擁有『原型』對象(prototype)。
prototype的本質是普通對象。
Function prototype比較特殊,是沒有prototype的函數對象。
new操作得到的對象是普通對象。
當調取一個對象的屬性時,會先在本身查找,若無,就根據 proto 找到構造原型,若無,繼續往上找。最后會到達頂層Object prototype,它的 proto 指向null,均無結果則返回undefined,結束。
由 proto 串起的路徑就是『原型鏈』。
通過prototype可以給所有子類共享屬性

17. 為什么使用繼承

通常在一般的項目里不需要,因為應用簡單,但你要用純js做一些復雜的工具或框架系統就要用到了,比如webgis、或者js框架如jquery、ext什么的,不然一個幾千行代碼的框架不用繼承得寫幾萬行,甚至還無法維護

18. setTimeout時間延遲為何不准

單線程, 先執行同步主線程, 再執行異步任務隊列

19. 事件循環述,宏任務和微任務有什么區別?

先主線程后異步任務隊列
先微任務再宏任務

20. let const var作用域

塊級作用域, 暫時性死區

21. 節流和防抖

函數節流是指一定時間內js方法只跑一次。比如人的眨眼睛,就是一定時間內眨一次。這是函數節流最形象的解釋。

// 函數節流 滾動條滾動 var canRun = true; document.getElementById("throttle").onscroll = function(){ if(!canRun){ // 判斷是否已空閑,如果在執行中,則直接return return; } canRun = false; setTimeout(function(){ console.log("函數節流"); canRun = true; }, 300); }; 

函數防抖是指頻繁觸發的情況下,只有足夠的空閑時間,才執行代碼一次。比如生活中的坐公交,就是一定時間內,如果有人陸續刷卡上車,司機就不會開車。只有別人沒刷卡了,司機才開車。

// 函數防抖 var timer = false; document.getElementById("debounce").onscroll = function(){ clearTimeout(timer); // 清除未執行的代碼,重置回初始化狀態 timer = setTimeout(function(){ console.log("函數防抖"); }, 300); }; 

22. 實現一個sleep函數

// 這種實現方式是利用一個偽死循環阻塞主線程。因為JS是單線程的。所以通過這種方式可以實現真正意義上的sleep()。

function sleep(delay) { var start = (new Date()).getTime(); while ((new Date()).getTime() - start < delay) { continue; } } function test() { console.log('111'); sleep(2000); console.log('222'); } test() 

23. 閉包

閉包屬於一種特殊的作用域,稱為 靜態作用域。它的定義可以理解為: 父函數被銷毀 的情況下,返回出的子函數的[[scope]]中仍然保留着父級的單變量對象和作用域鏈,因此可以繼續訪問到父級的變量對象,這樣的函數稱為閉包。
閉包會產生一個很經典的問題:
o多個子函數的[[scope]]都是同時指向父級,是完全共享的。因此當父級的變量對象被修改時,所有子函數都受到影響。
解決:
o變量可以通過 函數參數的形式 傳入,避免使用默認的[[scope]]向上查找
o使用setTimeout包裹,通過第三個參數傳入
o使用 塊級作用域,讓變量成為自己上下文的屬性,避免共享

24. Immutable.js

Facebook出品, 倡導數據的不可變性, 用的最多就是List和Map.

25. js實現instanceof

// 檢測l的原型鏈(__proto__)上是否有r.prototype,若有返回true,否則false function myInstanceof (l, r) { var R = r.prototype while (l.__proto__) { if (l.__proto__ === R) return true } return false } 

26. 嚴格模式

// 嚴格模式下, 隱式綁定丟失后this不會指向window, 而是指向undefined

  'use strict' var a = 2 var obj = { a: 1, b: function() { // console.log(this.a) console.log(this) } } var c = obj.b c() // undefined 

27. typescript缺點

並不是嚴格意義的js的超集, 與js不完全兼容, 會報錯
更多的限制, 是一種桎梏
有些js第三方庫沒有dts, 有問題

28. 構造函數實現原理

構造函數中沒有顯示的創建Object對象, 實際上后台自動創建了
直接給this對象賦值屬性和方法, this即指向創建的對象
沒有return返回值, 后台自動返回了該對象

// 模擬構造函數實現 var Book = function(name) { this.name = name; }; //正常用法 var java = new Book(‘Master Java’); //使用代碼模擬,在非IE瀏覽器中測試,IE瀏覽器不支持 var python = {}; python.__proto__ = Book.prototype; Book.call(python, 'Master Python'); 

29. for in 和 for of區別

for in遍歷數組會遍歷到數組原型上的屬性和方法, 更適合遍歷對象
forEach不支持break, continue, return等
使用for of可以成功遍歷數組的值, 而不是索引, 不會遍歷原型
for in 可以遍歷到myObject的原型方法method,如果不想遍歷原型方法和屬性的話,可以在循環內部判斷一下,hasOwnPropery方法可以判斷某屬性是否是該對象的實例屬性

30… JS實現並發控制:

使用消息隊列以及setInterval或promise進行入隊和出隊

31.promise.finally實現

Promise.prototype.finally = function (callback) { let P = this.constructor; return this.then( value => P.resolve(callback()).then(() => value), reason => P.resolve(callback()).then(() => { throw reason }) ); }; 

32.作用域

執行上下文中還包含作用域鏈。理解作用域之前,先介紹下作用域。作用域其實可理解為該上下文中聲明的 變量和聲明的作用范圍。可分為 塊級作用域 和 函數作用域
特性:
聲明提前: 一個聲明在函數體內都是可見的, 函數優先於變量
非匿名自執行函數,函數變量為 只讀 狀態,無法修改

let foo = function() { console.log(1) }; (function foo() { foo = 10 // 由於foo在函數中只為可讀,因此賦值無效 console.log(foo) }()) // 結果打印: ƒ foo() { foo = 10 ; console.log(foo) } 

33.作用域鏈

我們知道,我們可以在執行上下文中訪問到父級甚至全局的變量,這便是作用域鏈的功勞。作用域鏈可以理解為一組對象列表,包含 父級和自身的變量對象,因此我們便能通過作用域鏈訪問到父級里聲明的變量或者函數。
由兩部分組成:
o[[scope]]屬性: 指向父級變量對象和作用域鏈,也就是包含了父級的[[scope]]和AO
oAO: 自身活動對象
如此 [[scopr]]包含[[scope]],便自上而下形成一條 鏈式作用域。

34.對象的拷貝

淺拷貝: 以賦值的形式拷貝引用對象,仍指向同一個地址,修改時原對象也會受到影響
oObject.assign
o展開運算符(…)
深拷貝: 完全拷貝一個新對象,修改時原對象不再受到任何影響
oJSON.parse(JSON.stringify(obj)): 性能最快
具有循環引用的對象時,報錯
當值為函數、undefined、或symbol時,無法拷貝
o遞歸進行逐一賦值

35.new運算符的執行過程

新生成一個對象
鏈接到原型: obj.proto = Con.prototype
綁定this: apply
返回新對象(如果構造函數有自己 retrun 時,則返回該值)

36.instanceof原理

能在實例的 原型對象鏈 中找到該構造函數的prototype屬性所指向的 原型對象,就返回true。即:

// __proto__: 代表原型對象鏈 instance.[__proto__...] === instance.constructor.prototype // return true


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM