<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>

                企業??AI智能體構建引擎,智能編排和調試,一鍵部署,支持知識庫和私有化部署方案 廣告
                [TOC] ### 手寫 instanceof 方法 instanceof 運算符用于判斷構造函數的 prototype 屬性是否出現在對象的原型鏈中的任何位置。 實現步驟: 1. 首先獲取類型的原型 2. 然后獲得對象的原型 3. 然后一直循環判斷對象的原型是否等于類型的原型,直到對象原型為 `null`,因為原型鏈最終為 `null` 具體實現: ``` function myInstanceof(left, right) { let proto = Object.getPrototypeOf(left), // 獲取對象的原型 prototype = right.prototype; // 獲取構造函數的 prototype 對象 // 判斷構造函數的 prototype 對象是否在對象的原型鏈上 while (true) { if (!proto) return false; if (proto === prototype) return true; proto = Object.getPrototypeOf(proto); } } ``` ### 手寫 new 操作符 在調用 `new` 的過程中會發生以上四件事情: 1. 首先創建了一個新的空對象 2. 設置原型,將對象的原型設置為函數的 prototype 對象。 3. 讓函數的 this 指向這個對象,執行構造函數的代碼(為這個新對象添加屬性) 4. 判斷函數的返回值類型,如果是值類型,返回創建的對象。如果是引用類型,就返回這個引用類型的對象。 ``` function objectFactory() { let newObject = null; let constructor = Array.prototype.shift.call(arguments); let result = null; // 判斷參數是否是一個函數 if (typeof constructor !== "function") { console.error("type error"); return; } // 新建一個空對象,對象的原型為構造函數的 prototype 對象 newObject = Object.create(constructor.prototype); // 將 this 指向新建對象,并執行函數 result = constructor.apply(newObject, arguments); // 判斷返回對象 let flag = result && (typeof result === "object" || typeof result === "function"); // 判斷返回結果 return flag ? result : newObject; } // 使用方法 objectFactory(構造函數, 初始化參數); ``` ### 手寫Promise ``` const PENDING = "pending"; const RESOLVED = "resolved"; const REJECTED = "rejected"; function MyPromise(fn) { // 保存初始化狀態 var self = this; // 初始化狀態 this.state = PENDING; // 用于保存 resolve 或者 rejected 傳入的值 this.value = null; // 用于保存 resolve 的回調函數 this.resolvedCallbacks = []; // 用于保存 reject 的回調函數 this.rejectedCallbacks = []; // 狀態轉變為 resolved 方法 function resolve(value) { // 判斷傳入元素是否為 Promise 值,如果是,則狀態改變必須等待前一個狀態改變后再進行改變 if (value instanceof MyPromise) { return value.then(resolve, reject); } // 保證代碼的執行順序為本輪事件循環的末尾 setTimeout(() => { // 只有狀態為 pending 時才能轉變, if (self.state === PENDING) { // 修改狀態 self.state = RESOLVED; // 設置傳入的值 self.value = value; // 執行回調函數 self.resolvedCallbacks.forEach(callback => { callback(value); }); } }, 0); } // 狀態轉變為 rejected 方法 function reject(value) { // 保證代碼的執行順序為本輪事件循環的末尾 setTimeout(() => { // 只有狀態為 pending 時才能轉變 if (self.state === PENDING) { // 修改狀態 self.state = REJECTED; // 設置傳入的值 self.value = value; // 執行回調函數 self.rejectedCallbacks.forEach(callback => { callback(value); }); } }, 0); } // 將兩個方法傳入函數執行 try { fn(resolve, reject); } catch (e) { // 遇到錯誤時,捕獲錯誤,執行 reject 函數 reject(e); } } MyPromise.prototype.then = function(onResolved, onRejected) { // 首先判斷兩個參數是否為函數類型,因為這兩個參數是可選參數 onResolved = typeof onResolved === "function" ? onResolved : function(value) { return value; }; onRejected = typeof onRejected === "function" ? onRejected : function(error) { throw error; }; // 如果是等待狀態,則將函數加入對應列表中 if (this.state === PENDING) { this.resolvedCallbacks.push(onResolved); this.rejectedCallbacks.push(onRejected); } // 如果狀態已經凝固,則直接執行對應狀態的函數 if (this.state === RESOLVED) { onResolved(this.value); } if (this.state === REJECTED) { onRejected(this.value); } }; ``` ### 手寫 Promise.then `then` 方法返回一個新的 `promise` 實例,為了在 `promise` 狀態發生變化時(`resolve` / `reject` 被調用時)再執行 `then` 里的函數,我們使用一個 `callbacks` 數組先把傳給then的函數暫存起來,等狀態改變時再調用。 **那么,怎么保證后一個** `**then**` **里的方法在前一個** `**then**`**(可能是異步)結束之后再執行呢?** 我們可以將傳給 `then` 的函數和新 `promise` 的 `resolve` 一起 `push` 到前一個 `promise` 的 `callbacks` 數組中,達到承前啟后的效果: * 承前:當前一個 `promise` 完成后,調用其 `resolve` 變更狀態,在這個 `resolve` 里會依次調用 `callbacks` 里的回調,這樣就執行了 `then` 里的方法了 * 啟后:上一步中,當 `then` 里的方法執行完成后,返回一個結果,如果這個結果是個簡單的值,就直接調用新 `promise` 的 `resolve`,讓其狀態變更,這又會依次調用新 `promise` 的 `callbacks` 數組里的方法,循環往復。。如果返回的結果是個 `promise`,則需要等它完成之后再觸發新 `promise` 的 `resolve`,所以可以在其結果的 `then` 里調用新 `promise` 的 `resolve`。 ``` then(onFulfilled, onReject){ // 保存前一個promise的this const self = this; return new MyPromise((resolve, reject) => { // 封裝前一個promise成功時執行的函數 let fulfilled = () => { try{ const result = onFulfilled(self.value); // 承前 return result instanceof MyPromise? result.then(resolve, reject) : resolve(result); //啟后 }catch(err){ reject(err) } } // 封裝前一個promise失敗時執行的函數 let rejected = () => { try{ const result = onReject(self.reason); return result instanceof MyPromise? result.then(resolve, reject) : reject(result); }catch(err){ reject(err) } } switch(self.status){ case PENDING: self.onFulfilledCallbacks.push(fulfilled); self.onRejectedCallbacks.push(rejected); break; case FULFILLED: fulfilled(); break; case REJECT: rejected(); break; } }) } ``` **注意:** * 連續多個 `then` 里的回調方法是同步注冊的,但注冊到了不同的 `callbacks` 數組中,因為每次 `then` 都返回新的 `promise` 實例(參考上面的例子和圖) * 注冊完成后開始執行構造函數中的異步事件,異步完成之后依次調用 `callbacks` 數組中提前注冊的回調 ### 手寫 Promise.all **核心思路** 1. 接收一個 Promise 實例的數組或具有 Iterator 接口的對象作為參數 2. 這個方法返回一個新的 promise 對象, 3. 遍歷傳入的參數,用Promise.resolve()將參數"包一層",使其變成一個promise對象 4. 參數所有回調成功才是成功,返回值數組與參數順序一致 5. 參數數組其中一個失敗,則觸發失敗狀態,第一個觸發失敗的 Promise 錯誤信息作為 Promise.all 的錯誤信息。 **實現代碼** 一般來說,Promise.all 用來處理多個并發請求,也是為了頁面數據構造的方便,將一個頁面所用到的在不同接口的數據一起請求過來,不過,如果其中一個接口失敗了,多個請求也就失敗了,頁面可能啥也出不來,這就看當前頁面的耦合程度了。 ``` function promiseAll(promises) { return new Promise(function(resolve, reject) { if(!Array.isArray(promises)){ throw new TypeError(`argument must be a array`) } var resolvedCounter = 0; var promiseNum = promises.length; var resolvedResult = []; for (let i = 0; i < promiseNum; i++) { Promise.resolve(promises[i]).then(value=>{ resolvedCounter++; resolvedResult[i] = value; if (resolvedCounter == promiseNum) { return resolve(resolvedResult) } },error=>{ return reject(error) }) } }) } // test let p1 = new Promise(function (resolve, reject) { setTimeout(function () { resolve(1) }, 1000) }) let p2 = new Promise(function (resolve, reject) { setTimeout(function () { resolve(2) }, 2000) }) let p3 = new Promise(function (resolve, reject) { setTimeout(function () { resolve(3) }, 3000) }) promiseAll([p3, p1, p2]).then(res => { console.log(res) // [3, 1, 2] }) ``` ### 手寫 Promise.race 該方法的參數是 Promise 實例數組, 然后其 then 注冊的回調方法是數組中的某一個 Promise 的狀態變為 fulfilled 的時候就執行. 因為 Promise 的狀態**只能改變一次**, 那么我們只需要把 Promise.race 中產生的 Promise 對象的 resolve 方法, 注入到數組中的每一個 Promise 實例中的回調函數中即可。 ``` Promise.race = function (args) { return new Promise((resolve, reject) => { for (let i = 0, len = args.length; i < len; i++) { args[i].then(resolve, reject) } }) } ``` ### 手寫防抖函數 函數防抖是指在事件被觸發 n 秒后再執行回調,如果在這 n 秒內事件又被觸發,則重新計時。這可以使用在一些點擊請求的事件上,避免因為用戶的多次點擊向后端發送多次請求。也可以[點擊](http://www.hmoore.net/vvmily_king/vvmily/2331774)查看以往。 ``` // 函數防抖的實現 function debounce(fn, wait) { let timer = null; return function() { let context = this, args = arguments; // 如果此時存在定時器的話,則取消之前的定時器重新記時 if (timer) { clearTimeout(timer); timer = null; } // 設置定時器,使事件間隔指定事件后執行 timer = setTimeout(() => { fn.apply(context, args); }, wait); }; } ``` ### 手寫節流函數 函數節流是指規定一個單位時間,在這個單位時間內,只能有一次觸發事件的回調函數執行,如果在同一個單位時間內某事件被觸發多次,只有一次能生效。節流可以使用在 scroll 函數的事件監聽上,通過事件節流來降低事件調用的頻率。也可以[點擊](http://www.hmoore.net/vvmily_king/vvmily/2331774)查看以往。 ``` // 函數節流的實現; function throttle(fn, delay) { let curTime = Date.now(); return function() { let context = this, args = arguments, nowTime = Date.now(); // 如果兩次時間間隔超過了指定時間,則執行函數。 if (nowTime - curTime >= delay) { curTime = Date.now(); return fn.apply(context, args); } }; } ``` ### 手寫 call 函數 call 函數的實現步驟: 1. 判斷調用對象是否為函數,即使我們是定義在函數的原型上的,但是可能出現使用 call 等方式調用的情況。 2. 判斷傳入上下文對象是否存在,如果不存在,則設置為 window 。 3. 處理傳入的參數,截取第一個參數后的所有參數。 4. 將函數作為上下文對象的一個屬性。 5. 使用上下文對象來調用這個方法,并保存返回結果。 6. 刪除剛才新增的屬性。 7. 返回結果。 ``` // call函數實現 Function.prototype.myCall = function(context) { // 判斷調用對象 if (typeof this !== "function") { console.error("type error"); } // 獲取參數 let args = [...arguments].slice(1), result = null; // 判斷 context 是否傳入,如果未傳入則設置為 window context = context || window; // 將調用函數設為對象的方法 context.fn = this; // 調用函數 result = context.fn(...args); // 將屬性刪除 delete context.fn; return result; }; ``` ### 手寫 apply 函數 apply 函數的實現步驟: 1. 判斷調用對象是否為函數,即使我們是定義在函數的原型上的,但是可能出現使用 call 等方式調用的情況。 2. 判斷傳入上下文對象是否存在,如果不存在,則設置為 window 。 3. 將函數作為上下文對象的一個屬性。 4. 判斷參數值是否傳入 5. 使用上下文對象來調用這個方法,并保存返回結果。 6. 刪除剛才新增的屬性 7. 返回結果 ``` // apply 函數實現 Function.prototype.myApply = function(context) { // 判斷調用對象是否為函數 if (typeof this !== "function") { throw new TypeError("Error"); } let result = null; // 判斷 context 是否存在,如果未傳入則為 window context = context || window; // 將函數設為對象的方法 context.fn = this; // 調用方法 if (arguments[1]) { result = context.fn(...arguments[1]); } else { result = context.fn(); } // 將屬性刪除 delete context.fn; return result; }; ``` ### 手寫 bind 函數 bind 函數的實現步驟: 1. 判斷調用對象是否為函數,即使我們是定義在函數的原型上的,但是可能出現使用 call 等方式調用的情況。 2. 保存當前函數的引用,獲取其余傳入參數值。 3. 創建一個函數返回 4. 函數內部使用 apply 來綁定函數調用,需要判斷函數作為構造函數的情況,這個時候需要傳入當前函數的 this 給 apply 調用,其余情況都傳入指定的上下文對象。 ``` // bind 函數實現 Function.prototype.myBind = function(context) { // 判斷調用對象是否為函數 if (typeof this !== "function") { throw new TypeError("Error"); } // 獲取參數 var args = [...arguments].slice(1), fn = this; return function Fn() { // 根據調用方式,傳入不同綁定值 return fn.apply( this instanceof Fn ? this : context, args.concat(...arguments) ); }; }; ``` ### 實現AJAX請求 AJAX是 Asynchronous JavaScript and XML 的縮寫,指的是通過 JavaScript 的 異步通信,從服務器獲取 XML 文檔從中提取數據,再更新當前網頁的對應部分,而不用刷新整個網頁。 創建AJAX請求的步驟: * **創建一個 XMLHttpRequest 對象。** * 在這個對象上**使用 open 方法創建一個 HTTP 請求**,open 方法所需要的參數是請求的方法、請求的地址、是否異步和用戶的認證信息。 * 在發起請求前,可以為這個對象**添加一些信息和監聽函數**。比如說可以通過 setRequestHeader 方法來為請求添加頭信息。還可以為這個對象添加一個狀態監聽函數。一個 XMLHttpRequest 對象一共有 5 個狀態,當它的狀態變化時會觸發onreadystatechange 事件,可以通過設置監聽函數,來處理請求成功后的結果。當對象的 readyState 變為 4 的時候,代表服務器返回的數據接收完成,這個時候可以通過判斷請求的狀態,如果狀態是 2xx 或者 304 的話則代表返回正常。這個時候就可以通過 response 中的數據來對頁面進行更新了。 * 當對象的屬性和監聽函數設置完成后,最后調**用 sent 方法來向服務器發起請求**,可以傳入參數作為發送的數據體。 ``` const SERVER_URL = "/server"; let xhr = new XMLHttpRequest(); // 創建 Http 請求 xhr.open("GET", SERVER_URL, true); // 設置狀態監聽函數 xhr.onreadystatechange = function() { if (this.readyState !== 4) return; // 當請求成功時 if (this.status === 200) { handle(this.response); } else { console.error(this.statusText); } }; // 設置請求失敗時的監聽函數 xhr.onerror = function() { console.error(this.statusText); }; // 設置請求頭信息 xhr.responseType = "json"; xhr.setRequestHeader("Accept", "application/json"); // 發送 Http 請求 xhr.send(null); ``` ### 實現淺拷貝 淺拷貝是指,一個新的對象對原始對象的屬性值進行精確地拷貝,如果拷貝的是基本數據類型,拷貝的就是基本數據類型的值,如果是引用數據類型,拷貝的就是內存地址。如果其中一個對象的引用內存地址發生改變,另一個對象也會發生變化。 1. `Object.assign()`:是ES6中對象的拷貝方法,接受的第一個參數是目標對象,其余參數是源對象,用法:`Object.assign(target, source_1, ···)`,該方法可以實現淺拷貝,也可以實現一維對象的深拷貝。 2. 擴展運算符:使用擴展運算符可以在構造字面量對象的時候,進行屬性的拷貝。語法:`let cloneObj = { ...obj };`。 3. **Array.prototype.slice**:`arr.slice()`。 4. **Array.prototype.concat**:`arr.concat()`。 5. 手寫淺拷貝: ``` // 淺拷貝的實現; function shallowCopy(object) { // 只拷貝對象 if (!object || typeof object !== "object") return; // 根據 object 的類型判斷是新建一個數組還是對象 let newObject = Array.isArray(object) ? [] : {}; // 遍歷 object,并且判斷是 object 的屬性才拷貝 for (let key in object) { if (object.hasOwnProperty(key)) { newObject[key] = object[key]; } } return newObject; } ``` ### 實現深拷貝 * **淺拷貝:** 淺拷貝指的是將一個對象的屬性值復制到另一個對象,如果有的屬性的值為引用類型的話,那么會將這個引用的地址復制給對象,因此兩個對象會有同一個引用類型的引用。淺拷貝可以使用 ?Object.assign 和展開運算符來實現。 * **深拷貝:** 深拷貝相對淺拷貝而言,如果遇到屬性值為引用類型的時候,它新建一個引用類型并將對應的值復制給它,因此對象獲得的一個新的引用類型而不是一個原有類型的引用。深拷貝對于一些對象可以使用 JSON 的兩個函數來實現,但是由于 JSON 的對象格式比 js 的對象格式更加嚴格,所以如果屬性值里邊出現函數或者 Symbol 類型的值時,會轉換失敗。 1. JSON.stringify() * `JSON.parse(JSON.stringify(obj))`是目前比較常用的深拷貝方法之一,它的原理就是利用`JSON.stringify` 將`js`對象序列化(JSON字符串),再使用`JSON.parse`來反序列化(還原)`js`對象。 * 這個方法可以簡單粗暴的實現深拷貝,但是還存在問題,拷貝的對象中如果有函數,undefined,symbol,當使用過`JSON.stringify()`進行處理之后,都會消失。 ``` let obj1 = { a: 0, b: { c: 0 } }; let obj2 = JSON.parse(JSON.stringify(obj1)); obj1.a = 1; obj1.b.c = 1; console.log(obj1); // {a: 1, b: {c: 1}} console.log(obj2); // {a: 0, b: {c: 0}} ``` 2. 手寫實現深拷貝函數 ``` // 深拷貝的實現 function deepCopy(object) { if (!object || typeof object !== "object") return; let newObject = Array.isArray(object) ? [] : {}; for (let key in object) { if (object.hasOwnProperty(key)) { newObject[key] = typeof object[key] === "object" ? deepCopy(object[key]) : object[key]; } } return newObject; } ``` ### 實現數組的扁平化 **遞歸實現** * 普通的遞歸思路很容易理解,就是通過循環遞歸的方式,一項一項地去遍歷,如果每一項還是一個數組,那么就繼續往下遍歷,利用遞歸程序的方法,來實現數組的每一項的連接: ``` let arr = [1, [2, [3, 4, 5]]]; function flatten(arr) { let result = []; for(let i = 0; i < arr.length; i++) { if(Array.isArray(arr[i])) { result = result.concat(flatten(arr[i])); } else { result.push(arr[i]); } } return result; } flatten(arr); // [1, 2, 3, 4,5] ``` **reduce 函數迭代** * 從上面普通的遞歸函數中可以看出,其實就是對數組的每一項進行處理,那么其實也可以用reduce 來實現數組的拼接,從而簡化第一種方法的代碼,改造后的代碼如下所示: ``` let arr = [1, [2, [3, 4]]]; function flatten(arr) { return arr.reduce(function(prev, next){ return prev.concat(Array.isArray(next) ? flatten(next) : next) }, []) } console.log(flatten(arr));// [1, 2, 3, 4,5] ``` **擴展運算符實現** * 這個方法的實現,采用了擴展運算符和 some 的方法,兩者共同使用,達到數組扁平化的目的: ``` let arr = [1, [2, [3, 4]]]; function flatten(arr) { while (arr.some(item => Array.isArray(item))) { arr = [].concat(...arr); } return arr; } console.log(flatten(arr)); // [1, 2, 3, 4,5] ``` ### 實現數組去重 給定某無序數組,要求去除數組中的重復數字并且返回新的無重復數組。(方法很多,就不一一列舉了) * ES6方法(使用數據結構集合): ``` const array = [1, 2, 3, 5, 1, 5, 9, 1, 2, 8]; Array.from(new Set(array)); // [1, 2, 3, 5, 9, 8] ``` * ES5方法:使用map存儲不重復的數字 ``` const array = [1, 2, 3, 5, 1, 5, 9, 1, 2, 8]; uniqueArray(array); // [1, 2, 3, 5, 9, 8] function uniqueArray(array) { let map = {}; let res = []; for(var i = 0; i < array.length; i++) { if(!map.hasOwnProperty([array[i]])) { map[array[i]] = 1; res.push(array[i]); } } return res; } ``` ### 大數相加 如果想要對一個超大的整數(`> Number.MAX_SAFE_INTEGER`)進行加法運算,但是又想輸出一般形式,那么使用 + 是無法達到的,一旦數字超過 `Number.MAX_SAFE_INTEGER` 數字會被立即轉換為科學計數法,并且數字精度相比以前將會有誤差。 實現一個算法進行大數的相加: ``` function sumBigNumber(a, b) { let res = ''; let temp = 0; a = a.split(''); b = b.split(''); while (a.length || b.length || temp) { temp += ~~a.pop() + ~~b.pop(); res = (temp % 10) + res; temp = temp > 9 } return res.replace(/^0+/, ''); } ``` 其主要的思路如下: * 首先用字符串的方式來保存大數,這樣數字在數學表示上就不會發生變化 * 初始化res,temp來保存中間的計算結果,并將兩個字符串轉化為數組,以便進行每一位的加法運算 * 將兩個數組的對應的位進行相加,兩個數相加的結果可能大于10,所以可能要僅為,對10進行取余操作,將結果保存在當前位 * 判斷當前位是否大于9,也就是是否會進位,若是則將temp賦值為true,因為在加法運算中,true會自動隱式轉化為1,以便于下一次相加 * 重復上述操作,直至計算結束 ### 大數相乘 ``` function multiplyBigNum(num1, num2) { //判斷輸入是不是數字 if (isNaN(num1) || isNaN(num2)) return ""; num1 = num1 + "" num2 = num2 + "" let len1 = num1.length, len2 = num2.length; let pos = []; //j放外面,先固定被乘數的一位,分別去乘乘數的每一位,更符合豎式演算法 for (let j = len2 - 1; j >= 0; j--) { for (let i = len1 - 1; i >= 0; i--) { //兩個個位數相乘,最多產生兩位數,index1代表十位,index2代表個位 let index1 = i + j, index2 = i + j + 1; //兩個個位數乘積加上當前位置個位已累積的數字,會產生進位,比如08 + 7 = 15,產生了進位1 let mul = num1[i] * num2[j] + (pos[index2] || 0); //mul包含新計算的十位,加上原有的十位就是最新的十位 pos[index1] = Math.floor(mul / 10) + (pos[index1] || 0); //mul的個位就是最新的個位 pos[index2] = mul % 10; } } //去掉前置0 let result = pos.join("").replace(/^0+/, ""); return result - 0 || '0'; } ``` ### 實現 add(1)(2)(3) 函數柯里化概念: 柯里化(Currying)是把接受多個參數的函數轉變為接受一個單一參數的函數,并且返回接受余下的參數且返回結果的新函數的技術。 ``` function add (a) { return function (b) { return function (c) { return a + b + c; } } } console.log(add(1)(2)(3)); // 6 ``` ### 實現類數組轉化為數組 類數組轉換為數組的方法有這樣幾種: * 通過 call 調用數組的 slice 方法來實現轉換 ``` Array.prototype.slice.call(arrayLike); ``` * 通過 call 調用數組的 splice 方法來實現轉換 ``` Array.prototype.splice.call(arrayLike, 0); ``` * 通過 apply 調用數組的 concat 方法來實現轉換 ``` Array.prototype.concat.apply([], arrayLike); ``` * 通過 Array.from 方法來實現轉換 ``` Array.from(arrayLike); ``` ### 將js對象轉化為樹形結構 ``` // 轉換前: source = [{ id: 1, pid: 0, name: 'body' }, { id: 2, pid: 1, name: 'title' }, { id: 3, pid: 2, name: 'div' }] // 轉換為: tree = [{ id: 1, pid: 0, name: 'body', children: [{ id: 2, pid: 1, name: 'title', children: [{ id: 3, pid: 1, name: 'div' }] } }] ``` 代碼實現: ``` function jsonToTree(data) { // 初始化結果數組,并判斷輸入數據的格式 let result = [] if(!Array.isArray(data)) { return result } // 使用map,將當前對象的id與當前對象對應存儲起來 let map = {}; data.forEach(item => { map[item.id] = item; }); // data.forEach(item => { let parent = map[item.pid]; if(parent) { (parent.children || (parent.children = [])).push(item); } else { result.push(item); } }); return result; } ``` ### 實現prototype繼承 所謂的原型鏈繼承就是讓新實例的原型等于父類的實例: ``` //父方法 function SupperFunction(flag1){ this.flag1 = flag1; } //子方法 function SubFunction(flag2){ this.flag2 = flag2; } //父實例 var superInstance = new SupperFunction(true); //子繼承父 SubFunction.prototype = superInstance; //子實例 var subInstance = new SubFunction(false); //子調用自己和父的屬性 subInstance.flag1; // true subInstance.flag2; // false ``` ### 實現雙向數據綁定 ``` let obj = {} let input = document.getElementById('input') let span = document.getElementById('span') // 數據劫持 Object.defineProperty(obj, 'text', { configurable: true, enumerable: true, get() { console.log('獲取數據了') }, set(newVal) { console.log('數據更新了') input.value = newVal span.innerHTML = newVal } }) // 輸入監聽 input.addEventListener('keyup', function(e) { obj.text = e.target.value }) ``` ### 實現斐波那契數列 ``` // 遞歸 function fn (n){ if(n==0) return 0 if(n==1) return 1 return fn(n-2)+fn(n-1) } // 優化 function fibonacci2(n) { const arr = [1, 1, 2]; const arrLen = arr.length; if (n <= arrLen) { return arr[n]; } for (let i = arrLen; i < n; i++) { arr.push(arr[i - 1] + arr[ i - 2]); } return arr[arr.length - 1]; } // 非遞歸 function fn(n) { let pre1 = 1; let pre2 = 1; let current = 2; if (n <= 2) { return current; } for (let i = 2; i < n; i++) { pre1 = pre2; pre2 = current; current = pre1 + pre2; } return current; } ``` ### 使用 setTimeout 實現 setInterval setInterval 的作用是每隔一段指定時間執行一個函數,但是這個執行不是真的到了時間立即執行,它真正的作用是每隔一段時間將事件加入事件隊列中去,只有當當前的執行棧為空的時候,才能去從事件隊列中取出事件執行。所以可能會出現這樣的情況,就是當前執行棧執行的時間很長,導致事件隊列里邊積累多個定時器加入的事件,當執行棧結束的時候,這些事件會依次執行,因此就不能到間隔一段時間執行的效果。 針對 setInterval 的這個缺點,我們可以使用 setTimeout 遞歸調用來模擬 setInterval,這樣我們就確保了只有一個事件結束了,我們才會觸發下一個定時器事件,這樣解決了 setInterval 的問題。 實現思路是使用遞歸函數,不斷地去執行 setTimeout 從而達到 setInterval 的效果 ``` function mySetInterval(fn, timeout) { // 控制器,控制定時器是否繼續執行 var timer = { flag: true }; // 設置遞歸函數,模擬定時器執行。 function interval() { if (timer.flag) { fn(); setTimeout(interval, timeout); } } // 啟動定時器 setTimeout(interval, timeout); // 返回控制器 return timer; } ```
                  <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>

                              哎呀哎呀视频在线观看