<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                # JavaScript筆試部分 點擊關注本[公眾號](http://www.hmoore.net/book/dsh225/javascript_vue_css/edit#_118)獲取文檔最新更新,并可以領取配套于本指南的《**前端面試手冊**》以及**最標準的簡歷模板**. [TOC] ## 實現防抖函數(debounce) 防抖函數原理:在事件被觸發n秒后再執行回調,如果在這n秒內又被觸發,則重新計時。 那么與節流函數的區別直接看這個動畫實現即可。 https://codesandbox.io/s/static-ce05g?from-embed 手寫簡化版: ~~~ // 防抖函數 let?deBounce?=?(fn,?delay)?=>?{ let?timer?=?null; return?function?(...args)?{ if?(timer)?{ clearTimeout(timer); } timer?=?setTimeout(()=>?{ fn(...args); },?delay) } } //手動實現防抖函數 function?debounce(fn,?wait,?immedite){ let timer; let debounced?=?function(){ let context?=?this if(timer){ clearTimeout(timer) ?} if(immedite){?//?是否立即執行 let callNow?=?!timer if(callNow){ fn.apply(context,arguments) ?} timer?=?setTimeout(()?=>?{ timer?=?null//閉包引用了timer,手動置空使其能被垃圾回收機制回收 },?wait); }else{ timer?=?setTimeout(()=>{ fn.apply(context,arguments) },wait) } } debounced.cancel?=?function(){?//取消立即執行 clearTimeout(timer) timer?=?null } return debounced } ~~~ 適用場景: * 按鈕提交場景:防止多次提交按鈕,只執行最后提交的一次 * 服務端驗證場景:表單驗證需要服務端配合,只執行一段連續的輸入事件的最后一次,還有搜索聯想詞功能類似 生存環境請用lodash.debounce ## 實現節流函數(throttle) 節流函數原理:規定在一個單位時間內,只能觸發一次函數。如果這個單位時間內觸發多次函數,只有一次生效。 手寫簡化版: ~~~ // 節流函數 //定時器方案 const throttle = (fn, delay = 500) => { let flag = true; return (...args) => { if (!flag) return; flag = false; setTimeout(() => { fn.apply(this, args); flag = true; }, delay); }; }; //節流函數 // 時間戳方案 function?throttle(fn,?wait){ let pre = 0; return (...args) => { let now = new Date(); if (now - pre > wait) { fn(...args); pre = now; } }; } ~~~ 適用場景: * 拖拽場景:固定時間內只執行一次,防止超高頻次觸發位置變動 * 縮放場景:監控瀏覽器resize * 動畫場景:避免短時間內多次觸發動畫引起性能問題 ***** 總結: 防抖:控制事件觸發的次數;節流:控制事件觸發的頻率 ## 深克隆(deepclone) 簡單版: ~~~ const newObj = JSON.parse(JSON.stringify(oldObj)); ~~~ 局限性: 1. 他無法實現對函數 、RegExp等特殊對象的克隆 2. 會拋棄對象的constructor,所有的構造函數會指向Object 3. 對象有循環引用,會報錯 面試版: ~~~ /** * deep clone * @param {[type]} parent object 需要進行克隆的對象 * @return {[type]} 深克隆后的對象 */ const clone = parent => { // 判斷類型 const isType = (obj, type) => { if (typeof obj !== "object") return false; const typeString = Object.prototype.toString.call(obj); let flag; switch (type) { case "Array": flag = typeString === "[object Array]"; break; case "Date": flag = typeString === "[object Date]"; break; case "RegExp": flag = typeString === "[object RegExp]"; break; default: flag = false; } return flag; }; // 處理正則 const getRegExp = re => { var flags = ""; if (re.global) flags += "g"; if (re.ignoreCase) flags += "i"; if (re.multiline) flags += "m"; return flags; }; // 維護兩個儲存循環引用的數組 const parents = []; const children = []; const _clone = parent => { if (parent === null) return null; if (typeof parent !== "object") return parent; let child, proto; if (isType(parent, "Array")) { // 對數組做特殊處理 child = []; } else if (isType(parent, "RegExp")) { // 對正則對象做特殊處理 child = new RegExp(parent.source, getRegExp(parent)); if (parent.lastIndex) child.lastIndex = parent.lastIndex; } else if (isType(parent, "Date")) { // 對Date對象做特殊處理 child = new Date(parent.getTime()); } else { // 處理對象原型 proto = Object.getPrototypeOf(parent); // 利用Object.create切斷原型鏈 child = Object.create(proto); } // 處理循環引用 const index = parents.indexOf(parent); if (index != -1) { // 如果父數組存在本對象,說明之前已經被引用過,直接返回此對象 return children[index]; } parents.push(parent); children.push(child); for (let i in parent) { // 遞歸 child[i] = _clone(parent[i]); } return child; }; return _clone(parent); }; //深拷貝方法 function?deeClone(obj){ let newObj if(typeof obj?===?'object'?&&?obj?!==?null){?//如果是引用類型 newObj?=?obj.constructor?===?Array??[]?:?{} for(i in obj){?//遍歷對象屬性的key值 newObj[i]?=?type of obj[i]?===?'object'???deeClone(obj[i])?:?obj[i] ????} }else?{?//如果是基本數據類型 newObj?=?obj ?} return newObj } ~~~ 局限性: 1. 一些特殊情況沒有處理: 例如Buffer對象、Promise、Set、Map 2. 另外對于確保沒有循環引用的對象,我們可以省去對循環引用的特殊處理,因為這很消耗時間 > 原理詳解[實現深克隆](https://www.cxymsg.com/guide/jsWritten.html#deepclone) ## 實現Event(event bus) event bus既是node中各個模塊的基石,又是前端組件通信的依賴手段之一,同時涉及了訂閱-發布設計模式,是非常重要的基礎。 簡單版: ~~~ class EventEmeitter { constructor() { this._events = this._events || new Map(); // 儲存事件/回調鍵值對 this._maxListeners = this._maxListeners || 10; // 設立監聽上限 } } // 觸發名為type的事件 EventEmeitter.prototype.emit = function(type, ...args) { let handler; // 從儲存事件鍵值對的this._events中獲取對應事件回調函數 handler = this._events.get(type); if (args.length > 0) { handler.apply(this, args); } else { handler.call(this); } return true; }; // 監聽名為type的事件 EventEmeitter.prototype.addListener = function(type, fn) { // 將type事件以及對應的fn函數放入this._events中儲存 if (!this._events.get(type)) { this._events.set(type, fn); } }; ~~~ 面試版: ~~~ class EventEmeitter { constructor() { this._events = this._events || new Map(); // 儲存事件/回調鍵值對 this._maxListeners = this._maxListeners || 10; // 設立監聽上限 } } // 觸發名為type的事件 EventEmeitter.prototype.emit = function(type, ...args) { let handler; handler = this._events.get(type); if (Array.isArray(handler)) { // 如果是一個數組說明有多個監聽者,需要依次此觸發里面的函數 for (let i = 0; i < handler.length; i++) { if (args.length > 0) { handler[i].apply(this, args); } else { handler[i].call(this); } } } else { // 單個函數的情況我們直接觸發即可 if (args.length > 0) { handler.apply(this, args); } else { handler.call(this); } } return true; }; // 監聽名為type的事件 EventEmeitter.prototype.addListener = function(type, fn) { const handler = this._events.get(type); // 獲取對應事件名稱的函數清單 if (!handler) { this._events.set(type, fn); } else if (handler && typeof handler === "function") { // 如果handler是函數說明只有一個監聽者 this._events.set(type, [handler, fn]); // 多個監聽者我們需要用數組儲存 } else { handler.push(fn); // 已經有多個監聽者,那么直接往數組里push函數即可 } }; EventEmeitter.prototype.removeListener = function(type, fn) { const handler = this._events.get(type); // 獲取對應事件名稱的函數清單 // 如果是函數,說明只被監聽了一次 if (handler && typeof handler === "function") { this._events.delete(type, fn); } else { let postion; // 如果handler是數組,說明被監聽多次要找到對應的函數 for (let i = 0; i < handler.length; i++) { if (handler[i] === fn) { postion = i; } else { postion = -1; } } // 如果找到匹配的函數,從數組中清除 if (postion !== -1) { // 找到數組對應的位置,直接清除此回調 handler.splice(postion, 1); // 如果清除后只有一個函數,那么取消數組,以函數形式保存 if (handler.length === 1) { this._events.set(type, handler[0]); } } else { return this; } } }; ~~~ > 實現具體過程和思路見[實現event](https://www.cxymsg.com/guide/jsWritten.html#event) ## 實現instanceOf ~~~ // 模擬 instanceof function instance_of(L, R) { //L 表示左表達式,R 表示右表達式 var O = R.prototype; // 取 R 的顯示原型 L = L.__proto__; // 取 L 的隱式原型 while (true) { if (L === null) return false; if (O === L) // 這里重點:當 O 嚴格等于 L 時,返回 true return true; L = L.__proto__; } } ~~~ ## 模擬new new操作符做了這些事: * 創建一個空對象,且讓該對象繼承了Function.prototype; * 執行我們的構造函數,改變this指向(指向剛剛創建的新對象); * 拋出我們的新對象; ~~~ // objectFactory(name, 'cxk', '18') function objectFactory() { const obj = new Object(); const Constructor = [].shift.call(arguments); obj.__proto__ = Constructor.prototype; const ret = Constructor.apply(obj, arguments); return typeof ret === "object" ? ret : obj; } ~~~ ## 實現一個call call做了什么: * 將函數設為對象的屬性 * 執行&刪除這個函數 * 指定this到函數并傳入給定參數執行函數 * 如果不傳入參數,默認指向為 window ~~~ // 模擬 call bar.mycall(null); //實現一個call方法: Function.prototype.myCall = function(context) { //此處沒有考慮context非object情況 context.fn = this; let args = []; for (let i = 1, len = arguments.length; i < len; i++) { args.push(arguments[i]); } context.fn(...args); let result = context.fn(...args); delete context.fn; return result; }; ~~~ > 具體實現參考[JavaScript深入之call和apply的模擬實現](https://github.com/mqyqingfeng/Blog/issues/11) ## 實現apply方法 apply原理與call很相似,不多贅述 ~~~ // 模擬 apply Function.prototype.myapply = function(context, arr) { var context = Object(context) || window; context.fn = this; var result; if (!arr) { result = context.fn(); } else { var args = []; for (var i = 0, len = arr.length; i < len; i++) { args.push("arr[" + i + "]"); } result = eval("context.fn(" + args + ")"); } delete context.fn; return result; }; ~~~ ## 實現bind 實現bind要做什么 * 返回一個函數,綁定this,傳遞預置參數 * bind返回的函數可以作為構造函數使用。故作為構造函數時應使得this失效,但是傳入的參數依然有效 ~~~ // mdn的實現 if (!Function.prototype.bind) { Function.prototype.bind = function(oThis) { if (typeof this !== 'function') { // closest thing possible to the ECMAScript 5 // internal IsCallable function throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable'); } var aArgs = Array.prototype.slice.call(arguments, 1), fToBind = this, fNOP = function() {}, fBound = function() { // this instanceof fBound === true時,說明返回的fBound被當做new的構造函數調用 return fToBind.apply(this instanceof fBound ? this : oThis, // 獲取調用時(fBound)的傳參.bind 返回的函數入參往往是這么傳遞的 aArgs.concat(Array.prototype.slice.call(arguments))); }; // 維護原型關系 if (this.prototype) { // Function.prototype doesn't have a prototype property fNOP.prototype = this.prototype; } // 下行的代碼使fBound.prototype是fNOP的實例,因此 // 返回的fBound若作為new的構造函數,new生成的新對象作為this傳入fBound,新對象的__proto__就是fNOP的實例 fBound.prototype = new fNOP(); return fBound; }; } ~~~ > 詳解請移步[JavaScript深入之bind的模擬實現 #12](https://github.com/mqyqingfeng/Blog/issues/12) ## 模擬Object.create Object.create()方法創建一個新對象,使用現有的對象來提供新創建的對象的\_\_proto\_\_。 ~~~ // 模擬 Object.create function create(proto) { function F() {} F.prototype = proto; return new F(); } ~~~ ## JS繼承的實現方式 https://www.cnblogs.com/humin/p/4556820.html https://www.jianshu.com/p/cf5543b77b36 ## 實現類的繼承 類的繼承在幾年前是重點內容,有n種繼承方式各有優劣,es6普及后越來越不重要,那么多種寫法有點『回字有四樣寫法』的意思,如果還想深入理解的去看紅寶書即可,我們目前只實現一種最理想的繼承方式。 ~~~ function Parent(name) { this.parent = name } Parent.prototype.say = function() { console.log(`${this.parent}: 你打籃球的樣子像kunkun`) } function Child(name, parent) { // 將父類的構造函數綁定在子類上 Parent.call(this, parent) this.child = name } /** 1. 這一步不用Child.prototype =Parent.prototype的原因是怕共享內存,修改父類原型對象就會影響子類 2. 不用Child.prototype = new Parent()的原因是會調用2次父類的構造方法(另一次是call),會存在一份多余的父類實例屬性 3. Object.create是創建了父類原型的副本,與父類原型完全隔離 */ Child.prototype = Object.create(Parent.prototype); Child.prototype.say = function() { console.log(`${this.parent}好,我是練習時長兩年半的${this.child}`); } // 注意記得把子類的構造指向子類本身 Child.prototype.constructor = Child; var parent = new Parent('father'); parent.say() // father: 你打籃球的樣子像kunkun var child = new Child('cxk', 'father'); child.say() // father好,我是練習時長兩年半的cxk ~~~ ## 實現JSON.parse ~~~ var json = '{"name":"cxk", "age":25}'; var obj = eval("(" + json + ")"); ~~~ 此方法屬于黑魔法,極易容易被xss攻擊,還有一種`new Function`大同小異。 簡單的教程看這個[半小時實現一個 JSON 解析器](https://zhuanlan.zhihu.com/p/28049617) ## 實現Promise > 我很早之前實現過一版,而且注釋很多,但是居然找不到了,這是在網絡上找了一版帶注釋的,目測沒有大問題,具體過程可以看這篇[史上最易讀懂的 Promise/A+ 完全實現](https://zhuanlan.zhihu.com/p/21834559) > https://blog.csdn.net/weixin_43299180/article/details/122827147 簡化版: ``` let PromiseTest = function (resolve,reject) { this.status = 'pending' this.msg = ''; let self = this,process = arguments[0]; process(function () { self.status = 'resolve' self.msg = arguments[0] },function() { self.status = 'reject' self.msg = arguments[0] }) return this; } PromiseTest.prototype.then = function (resolve,reject) { if(this.status === 'resolve') { arguments[0](this.msg) } if(this.status === 'reject' && arguments[1]) { arguments[1](this.msg) } } let test = new PromiseTest(function(resolve,reject) { resolve('124') }) test.then(function(result) { console.log(result) console.log('ok') },function(result) { console.log(result) console.log('no ok !') }) ``` 復雜版: ~~~ var PromisePolyfill = (function () { // 和reject不同的是resolve需要嘗試展開thenable對象 function tryToResolve (value) { if (this === value) { // 主要是防止下面這種情況 // let y = new Promise(res => setTimeout(res(y))) throw TypeError('Chaining cycle detected for promise!') } // 根據規范2.32以及2.33 對對象或者函數嘗試展開 // 保證S6之前的 polyfill 也能和ES6的原生promise混用 if (value !== null && (typeof value === 'object' || typeof value === 'function')) { try { // 這里記錄這次then的值同時要被try包裹 // 主要原因是 then 可能是一個getter, 也也就是說 // 1. value.then可能報錯 // 2. value.then可能產生副作用(例如多次執行可能結果不同) var then = value.then // 另一方面, 由于無法保證 then 確實會像預期的那樣只調用一個onFullfilled / onRejected // 所以增加了一個flag來防止resolveOrReject被多次調用 var thenAlreadyCalledOrThrow = false if (typeof then === 'function') { // 是thenable 那么嘗試展開 // 并且在該thenable狀態改變之前this對象的狀態不變 then.bind(value)( // onFullfilled function (value2) { if (thenAlreadyCalledOrThrow) return thenAlreadyCalledOrThrow = true tryToResolve.bind(this, value2)() }.bind(this), // onRejected function (reason2) { if (thenAlreadyCalledOrThrow) return thenAlreadyCalledOrThrow = true resolveOrReject.bind(this, 'rejected', reason2)() }.bind(this) ) } else { // 擁有then 但是then不是一個函數 所以也不是thenable resolveOrReject.bind(this, 'resolved', value)() } } catch (e) { if (thenAlreadyCalledOrThrow) return thenAlreadyCalledOrThrow = true resolveOrReject.bind(this, 'rejected', e)() } } else { // 基本類型 直接返回 resolveOrReject.bind(this, 'resolved', value)() } } function resolveOrReject (status, data) { if (this.status !== 'pending') return this.status = status this.data = data if (status === 'resolved') { for (var i = 0; i < this.resolveList.length; ++i) { this.resolveList[i]() } } else { for (i = 0; i < this.rejectList.length; ++i) { this.rejectList[i]() } } } function Promise (executor) { if (!(this instanceof Promise)) { throw Error('Promise can not be called without new !') } if (typeof executor !== 'function') { // 非標準 但與Chrome谷歌保持一致 throw TypeError('Promise resolver ' + executor + ' is not a function') } this.status = 'pending' this.resolveList = [] this.rejectList = [] try { executor(tryToResolve.bind(this), resolveOrReject.bind(this, 'rejected')) } catch (e) { resolveOrReject.bind(this, 'rejected', e)() } } Promise.prototype.then = function (onFullfilled, onRejected) { // 返回值穿透以及錯誤穿透, 注意錯誤穿透用的是throw而不是return,否則的話 // 這個then返回的promise狀態將變成resolved即接下來的then中的onFullfilled // 會被調用, 然而我們想要調用的是onRejected if (typeof onFullfilled !== 'function') { onFullfilled = function (data) { return data } } if (typeof onRejected !== 'function') { onRejected = function (reason) { throw reason } } var executor = function (resolve, reject) { setTimeout(function () { try { // 拿到對應的handle函數處理this.data // 并以此為依據解析這個新的Promise var value = this.status === 'resolved' ? onFullfilled(this.data) : onRejected(this.data) resolve(value) } catch (e) { reject(e) } }.bind(this)) } // then 接受兩個函數返回一個新的Promise // then 自身的執行永遠異步與onFullfilled/onRejected的執行 if (this.status !== 'pending') { return new Promise(executor.bind(this)) } else { // pending return new Promise(function (resolve, reject) { this.resolveList.push(executor.bind(this, resolve, reject)) this.rejectList.push(executor.bind(this, resolve, reject)) }.bind(this)) } } // for prmise A+ test Promise.deferred = Promise.defer = function () { var dfd = {} dfd.promise = new Promise(function (resolve, reject) { dfd.resolve = resolve dfd.reject = reject }) return dfd } // for prmise A+ test if (typeof module !== 'undefined') { module.exports = Promise } return Promise })() PromisePolyfill.all = function (promises) { return new Promise((resolve, reject) => { const result = [] let cnt = 0 for (let i = 0; i < promises.length; ++i) { promises[i].then(value => { cnt++ result[i] = value if (cnt === promises.length) resolve(result) }, reject) } }) } PromisePolyfill.race = function (promises) { return new Promise((resolve, reject) => { for (let i = 0; i < promises.length; ++i) { promises[i].then(resolve, reject) } }) } PromisePolyfill.any = (promises) => { let arr = []; let count = 0; return new Promise((resolve, reject) => { promises.forEach(item => { Promise.resolve(item).then(res => resolve(res), err => { count += 1; if (count === promises.length) { reject('AggregateError: All promises were rejected') } }) }) }) } ~~~ * * * ## 實現some、every、flat ``` // 實現some Array.prototype.some = function(fn, value){ if (typeof fn !== "function") { return false; } let arr = this; for (var i = 0; i < arr.length; i++) { var result = fn.call(value, arr[i], i, arr); if (result) return true; } return false; } // 實現every Array.prototype.every = function(fn, value){ if (typeof fn !== "function") { return false; } let arr = this; for (var i = 0; i < arr.length; i++) { var result = fn.call(value, arr[i], i, arr); if (!result) return false; } return true; } // 實現flat Array.prototype.myFlat = function (dep = 1) { return this.reduce((acc, val) => { return acc.concat(Array.isArray(val) && dep > 0 ? // 這里的三目就是防止這個現象:[3].concat([4]) // 結果為[3, 4] val.myFlat(--dep) : Array.isArray(val) ? [val] : val); }, []) } ``` ## 代碼中有很多console.log,如何做到不改動代碼的提前下,做到log內容的上報 ### 重寫console.log函數 ``` var lastLog; console.oldLog?=?console.log; console.log?=?function(str,fn)?{ console.oldLog(str); lastLog?=?str; if(fn)?{ fn(); ????} } console.log("Hello,?Neo",function()?{ alert("1111") }); document.write(lastLog); ``` ## 實現buildTree ``` const flatTreeData = [ { id: '1', parentId: '0' }, { id: '1-1', parentId: '1' }, { id: '1-1-1', parentId: '1-1' }, { id: '1-2', parentId: '1' }, { id: '1-3', parentId: '1' }, { id: '2', parentId: '0' }, { id: '2-1', parentId: '2' }, { id: '2-2', parentId: '2' }, { id: '2-3', parentId: '2' }, ]; // 實現函數buildTree,輸出: const buildTree = { id: '0', children: [ { id: '1', children: [{ id: '1-1', children: [{ id: '1-1-1' }] }, { id: '1-2' }, { id: '1-3' }], }, { id: '2', children: [{ id: '2-1' }, { id: '2-2' }, { id: '2-3' }], }, ], }; // 遞歸 function buildTreeFn(data, parentId) { const tree = { id: parentId, children: [] }; data.forEach(item => { if (item.parentId === parentId) { const childNode = { id: item.id }; const grandchildren = buildTreeFn(data, item.id); if (grandchildren.children.length > 0) { childNode.children = grandchildren.children; } tree.children.push(childNode); } }); return tree; } const treeData = buildTreeFn(flatTreeData, '0'); console.log(treeData); // 不使用遞歸 function buildTreeFn2(data = []) { let _data = JSON.parse(JSON.stringify(data)) return _data.filter((p) => { const _arr = _data.filter((c) => c.parentId == p.id) _arr.length && (p.children = _arr) return p.parentId == 0 }) } const treeData = buildTreeFn2(flatTreeData); console.log(treeData); ``` # 用fetch封裝最大并發請求函數 ``` 方案一: 遞歸 function handleFetchQueue(urls, max, callback) { let urlsCopy = [... urls];//防止影響外部urls變量 function request() { function Handle () { count--; console.log('end 當前并發數為: '+count); if(urlsCopy.length) {//還有未請求的則遞歸 request(); } else if (count === 0) {//并發數為0則表示全部請求完成 callback() } } count++; console.log('start 當前并發數為: '+count); //請求 fetch(urlsCopy.shift()).then(Handle).catch(Handle); //并發數不足時遞歸 count < max && request(); } let count = 0;//幾率并發數 request(); } 方案二: Promise function sendResquest(urls, max, callback) { let pending_count = 0, //并發數 idx = 0;//當前請求的位置 while (pending_count < max) { _fetch(urls[idx++]) } async function _fetch(url) { if (!url) return; pending_count++; console.log(url + ':start','并發數: '+pending_count); await fetch(url) pending_count--; console.log(url + ':done','并發數: '+pending_count); _fetch(urls[idx++]); pending_count || callback && callback() } } ``` ## 二維和多維數組變成一維數組 方法一、apply結合concat拉平數組 ``` let arr=[[1,2,3],[4,5],[6]]; console.log([].concat.apply([],arr)); //輸出 [1, 2, 3, 4, 5, 6] ``` 方法二、ES6新增數組擴展 flat() ~~~ [1, 2, [3, 4]].flat() ~~~ flat()默認只會“拉平”一層,如果想要“拉平”多層的嵌套數組,可以將flat()方法的參數寫成一個整數,表示想要拉平的層數,默認為1。 如果我們不知道數組究竟層級有多深我們可以用Infinity關鍵字作為參數 ~~~ [1, [2, [3]]].flat(Infinity) // [1, 2, 3] ~~~ 方法三、遞歸實現 ``` function doWhile(array) { let newArray = []; for (var i = 0; i < array.length - 1; i++) { if (array[i] instanceof Array) {//判斷是不是數組 newArray = newArray.concat(doWhile(array[i])) } else { newArray.push(array[i]) } } return newArray; } ``` ## 面試題總結 ``` 1. var obj = { name: 'baidu', arr: ['a', 'b', 'c'] } var obj2 = obj var arr = obj.arr obj2.arr = ['a', 'b', 'c', 'd'] obj2.name = 'inke' console.log(obj.arr) //['a', 'b', 'c', 'd'] console.log(arr)// ['a', 'b', 'c'] console.log(obj.name) // inke console.log(obj === obj2) // true console.log(obj.arr === obj2.arr) // true console.log(obj.arr === arr)//false 2. var MAP = { onclick: function () { }, curry: function (val) { return function (z) { return val++ + z } } } var getInfo = function (val) { return MAP[val] } var fn = getInfo('curry') var a = fn(100) console.log(a(200)) // 300 console.log(a(300)) // 401 console.log(fn(100)(200)) // 300 console.log(getInfo('curry')(100)(300)) //400 3. var name = 'oop' var Person = function (options) { this.name = options.name } Person.prototype.name = 'iron man' Person.prototype.getName = function () { return this.name } Person.getName = ()=> { return this.name; } var p = new Person({ name: 'inke' }) console.log(p.constructor === Person) // true console.log(p instanceof Person) // true console.log(p.__proto__ === Person.prototype) // true console.log(p.hasOwnProperty('name')) // true console.log(p.hasOwnProperty('getName')) // false var getName = p.getName console.log(getName === Person.prototype.getName) // true? console.log(getName()) // oop? console.log(Person.prototype.getName()) // iron man? console.log(p.getName()) // inke? console.log(Person.getName()) // oop? ``` ## 公眾號 想要實時關注筆者最新的文章和最新的文檔更新請關注公眾號**程序員面試官**,后續的文章會優先在公眾號更新. **簡歷模板**:關注公眾號回復「模板」獲取 《**前端面試手冊**》:配套于本指南的突擊手冊,關注公眾號回復「fed」獲取 ![2019-08-12-03-18-41](https://xiaomuzhu-image.oss-cn-beijing.aliyuncs.com/d846f65d5025c4b6c4619662a0669503.png)
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看