本週面試題一覽:
更多優質文章可戳: https://github.com/YvetteLau/...javascript
閉包是指有權訪問另外一個函數做用域中的變量的函數,建立閉包最經常使用的方式就是在一個函數內部建立另外一個函數。html
function foo() { var a = 2; return function fn() { console.log(a); } } let func = foo(); func(); //輸出2
閉包使得函數能夠繼續訪問定義時的詞法做用域。拜 fn 所賜,在 foo() 執行後,foo 內部做用域不會被銷燬。html5
不管經過何種手段將內部函數傳遞到所在的詞法做用域以外,它都會持有對原始定義做用域的引用,不管在何處執行這個函數都會使用閉包。如:java
function foo() { var a = 2; function inner() { console.log(a); } outer(inner); } function outer(fn){ fn(); //閉包 } foo();
function base() { let x = 10; //私有變量 return { getX: function() { return x; } } } let obj = base(); console.log(obj.getX()); //10
var a = []; for (var i = 0; i < 10; i++) { a[i] = (function(j){ return function () { console.log(j); } })(i); } a[6](); // 6
function coolModule() { let name = 'Yvette'; let age = 20; function sayName() { console.log(name); } function sayAge() { console.log(age); } return { sayName, sayAge } } let info = coolModule(); info.sayName(); //'Yvette'
模塊模式具備兩個必備的條件(來自《你不知道的JavaScript》)git
閉包會致使函數的變量一直保存在內存中,過多的閉包可能會致使內存泄漏es6
在實現 Promise.all 方法以前,咱們首先要知道 Promise.all 的功能和特色,由於在清楚了 Promise.all 功能和特色的狀況下,咱們才能進一步去寫實現。github
Promise.all(iterable)
返回一個新的 Promise 實例。此實例在 iterable
參數內全部的 promise
都 fulfilled
或者參數中不包含 promise
時,狀態變成 fulfilled
;若是參數中 promise
有一個失敗rejected
,此實例回調失敗,失敗緣由的是第一個失敗 promise
的返回結果。面試
let p = Promise.all([p1, p2, p3]);
p的狀態由 p1,p2,p3決定,分紅如下;兩種狀況:數組
(1)只有p一、p二、p3的狀態都變成 fulfilled
,p的狀態纔會變成 fulfilled
,此時p一、p二、p3的返回值組成一個數組,傳遞給p的回調函數。promise
(2)只要p一、p二、p3之中有一個被 rejected
,p的狀態就變成 rejected
,此時第一個被reject的實例的返回值,會傳遞給p的回調函數。
Promise.all 的返回值是一個 promise 實例
Promise.all
會 同步 返回一個已完成狀態的 promise
Promise.all
會 異步 返回一個已完成狀態的 promise
Promise.all
返回一個 處理中(pending) 狀態的 promise
.Promise.all 返回的 promise 的狀態
Promise.all
返回的 promise
異步地變爲完成。promise
失敗,Promise.all
異步地將失敗的那個結果給失敗狀態的回調函數,而無論其它 promise
是否完成Promise.all
返回的 promise
的完成狀態的結果都是一個數組僅考慮傳入的參數是數組的狀況
/** 僅考慮 promises 傳入的是數組的狀況時 */ Promise.all = function (promises) { return new Promise((resolve, reject) => { if (promises.length === 0) { resolve([]); } else { let result = []; let index = 0; for (let i = 0; i < promises.length; i++ ) { //考慮到 i 多是 thenable 對象也多是普通值 Promise.resolve(promises[i]).then(data => { result[i] = data; if (++index === promises.length) { //全部的 promises 狀態都是 fulfilled,promise.all返回的實例才變成 fulfilled 態 resolve(result); } }, err => { reject(err); return; }); } } }); }
可以使用 MDN 上的代碼進行測試
考慮 iterable 對象
Promise.all = function (promises) { /** promises 是一個可迭代對象,省略對參數類型的判斷 */ return new Promise((resolve, reject) => { if (promises.length === 0) { //若是傳入的參數是空的可迭代對象 return resolve([]); } else { let result = []; let index = 0; let j = 0; for (let value of promises) { (function (i) { Promise.resolve(value).then(data => { result[i] = data; //保證順序 index++; if (index === j) { //此時的j是length. resolve(result); } }, err => { //某個promise失敗 reject(err); return; }); })(j) j++; //length } } }); }
測試代碼:
let p2 = Promise.all({ a: 1, [Symbol.iterator]() { let index = 0; return { next() { index++; if (index == 1) { return { value: new Promise((resolve, reject) => { setTimeout(resolve, 100, 'foo'); }), done: false } } else if (index == 2) { return { value: new Promise((resolve, reject) => { resolve(222); }), done: false } } else if(index === 3) { return { value: 3, done: false } }else { return { done: true } } } } } }); setTimeout(() => { console.log(p2) }, 200);
<script>
標籤中增長 async
(html5) 或者 defer
(html4) 屬性,腳本就會異步加載。<script src="../XXX.js" defer></script>
defer
和 async
的區別在於:
defer
要等到整個頁面在內存中正常渲染結束(DOM 結構徹底生成,以及其餘腳本執行完成),在window.onload 以前執行;async
一旦下載完,渲染引擎就會中斷渲染,執行這個腳本之後,再繼續渲染。defer
腳本,會按照它們在頁面出現的順序加載async
腳本不能保證加載順序script
標籤動態建立的 script
,設置 src
並不會開始下載,而是要添加到文檔中,JS文件纔會開始下載。
let script = document.createElement('script'); script.src = 'XXX.js'; // 添加到html文件中才會開始下載 document.body.append(script);
let xhr = new XMLHttpRequest(); xhr.open("get", "js/xxx.js",true); xhr.send(); xhr.onreadystatechange = function() { if (xhr.readyState == 4 && xhr.status == 200) { eval(xhr.responseText); } }
ES6 爲數組實例新增了 flat
方法,用於將嵌套的數組「拉平」,變成一維的數組。該方法返回一個新數組,對原數組沒有影響。
flat
默認只會 「拉平」 一層,若是想要 「拉平」 多層的嵌套數組,須要給 flat
傳遞一個整數,表示想要拉平的層數。
function flattenDeep(arr, deepLength) { return arr.flat(deepLength); } console.log(flattenDeep([1, [2, [3, [4]], 5]], 3));
當傳遞的整數大於數組嵌套的層數時,會將數組拉平爲一維數組,JS能表示的最大數字爲 Math.pow(2, 53) - 1
,所以咱們能夠這樣定義 flattenDeep
函數
function flattenDeep(arr) { //固然,大多時候咱們並不會有這麼多層級的嵌套 return arr.flat(Math.pow(2,53) - 1); } console.log(flattenDeep([1, [2, [3, [4]], 5]]));
function flattenDeep(arr){ return arr.reduce((acc, val) => Array.isArray(val) ? acc.concat(flattenDeep(val)) : acc.concat(val), []); } console.log(flattenDeep([1, [2, [3, [4]], 5]]));
function flattenDeep(input) { const stack = [...input]; const res = []; while (stack.length) { // 使用 pop 從 stack 中取出並移除值 const next = stack.pop(); if (Array.isArray(next)) { // 使用 push 送回內層數組中的元素,不會改動原始輸入 original input stack.push(...next); } else { res.push(next); } } // 使用 reverse 恢復原數組的順序 return res.reverse(); } console.log(flattenDeep([1, [2, [3, [4]], 5]]));
ES6 規定,默認的 Iterator
接口部署在數據結構的 Symbol.iterator
屬性,換個角度,也能夠認爲,一個數據結構只要具備 Symbol.iterator
屬性(Symbol.iterator
方法對應的是遍歷器生成函數,返回的是一個遍歷器對象),那麼就能夠其認爲是可迭代的。
Symbol.iterator
屬性,Symbol.iterator()
返回的是一個遍歷器對象for ... of
進行循環let arry = [1, 2, 3, 4]; let iter = arry[Symbol.iterator](); console.log(iter.next()); //{ value: 1, done: false } console.log(iter.next()); //{ value: 2, done: false } console.log(iter.next()); //{ value: 3, done: false }
Iterator
接口的數據結構:上面咱們說,一個對象只有具備正確的 Symbol.iterator
屬性,那麼其就是可迭代的,所以,咱們能夠經過給對象新增 Symbol.iterator
使其可迭代。
let obj = { name: "Yvette", age: 18, job: 'engineer', *[Symbol.iterator]() { const self = this; const keys = Object.keys(self); for (let index = 0; index < keys.length; index++) { yield self[keys[index]];//yield表達式僅能使用在 Generator 函數中 } } }; for (var key of obj) { console.log(key); //Yvette 18 engineer }
[1] MDN Promise.all
[2] Promise
[3] Iterator
謝謝各位小夥伴願意花費寶貴的時間閱讀本文,若是本文給了您一點幫助或者是啓發,請不要吝嗇你的贊和Star,您的確定是我前進的最大動力。 https://github.com/YvetteLau/...
推薦關注本人公衆號: