国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

【Step-By-Step】高頻面試題深入解析 / 周刊04

youkede / 3340人閱讀

摘要:關于點擊進入項目是我于開始的一個項目,每個工作日發布一道面試題。的狀態由決定,分成以下兩種情況只有的狀態都變成,的狀態才會變成,此時的返回值組成一個數組,傳遞給的回調函數。

關于【Step-By-Step】
Step-By-Step (點擊進入項目) 是我于 2019-05-20 開始的一個項目,每個工作日發布一道面試題。

每個周末我會仔細閱讀大家的答案,整理最一份較優答案出來,因本人水平有限,有誤的地方,大家及時指正。

如果想 加群 學習,可以通過文末的公眾號,添加我為好友。

更多優質文章可戳: https://github.com/YvetteLau/...

__

本周面試題一覽:

什么是閉包?閉包的作用是什么?

實現 Promise.all 方法

異步加載 js 腳本的方法有哪些?

請實現一個 flattenDeep 函數,把嵌套的數組扁平化

可迭代對象有什么特點?

15. 什么是閉包?閉包的作用是什么? 什么是閉包?

閉包是指有權訪問另一個函數作用域中的變量的函數,創建閉包最常用的方式就是在一個函數內部創建另一個函數。

創建一個閉包
function foo() {
    var a = 2;
    return function fn() {
        console.log(a);
    }
}
let func = foo();
func(); //輸出2

閉包使得函數可以繼續訪問定義時的詞法作用域。拜 fn 所賜,在 foo() 執行后,foo 內部作用域不會被銷毀。

無論通過何種手段將內部函數傳遞到所在的詞法作用域之外,它都會持有對原始定義作用域的引用,無論在何處執行這個函數都會使用閉包。如:

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

必須有外部的封閉函數,該函數必須至少被調用一次(每次調用都會創建一個新的模塊實例)

封閉函數必須返回至少一個內部函數,這樣內部函數才能在私有作用域中形成閉包,并且可以訪問或者修改私有的狀態。

閉包的缺點

閉包會導致函數的變量一直保存在內存中,過多的閉包可能會導致內存泄漏

16. 實現 Promise.all 方法

在實現 Promise.all 方法之前,我們首先要知道 Promise.all 的功能和特點,因為在清楚了 Promise.all 功能和特點的情況下,我們才能進一步去寫實現。

Promise.all 功能

Promise.all(iterable) 返回一個新的 Promise 實例。此實例在 iterable 參數內所有的 promisefulfilled 或者參數中不包含 promise 時,狀態變成 fulfilled;如果參數中 promise 有一個失敗rejected,此實例回調失敗,失敗原因的是第一個失敗 promise 的返回結果。

let p = Promise.all([p1, p2, p3]);

p的狀態由 p1,p2,p3決定,分成以下;兩種情況:

(1)只有p1、p2、p3的狀態都變成 fulfilled,p的狀態才會變成 fulfilled,此時p1、p2、p3的返回值組成一個數組,傳遞給p的回調函數。

(2)只要p1、p2、p3之中有一個被 rejected,p的狀態就變成 rejected,此時第一個被reject的實例的返回值,會傳遞給p的回調函數。

Promise.all 的特點
Promise.all 的返回值是一個 promise 實例

如果傳入的參數為空的可迭代對象,Promise.all同步 返回一個已完成狀態的 promise

如果傳入的參數中不包含任何 promise,Promise.all異步 返回一個已完成狀態的 promise

其它情況下,Promise.all 返回一個 處理中(pending) 狀態的 promise.

Promise.all 返回的 promise 的狀態

如果傳入的參數中的 promise 都變成完成狀態,Promise.all 返回的 promise 異步地變為完成。

如果傳入的參數中,有一個 promise 失敗,Promise.all 異步地將失敗的那個結果給失敗狀態的回調函數,而不管其它 promise 是否完成

在任何情況下,Promise.all 返回的 promise 的完成狀態的結果都是一個數組

Promise.all 實現
僅考慮傳入的參數是數組的情況
/** 僅考慮 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);
17. 異步加載 js 腳本的方法有哪些?

deferasync 的區別在于:

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);
XHR 異步加載JS
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);
    }
}
18. 請實現一個 flattenDeep 函數,把嵌套的數組扁平化 利用 Array.prototype.flat

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]]));
利用 reduce 和 concat
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]]));
使用 stack 無限反嵌套多層嵌套數組
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]]));
19. 可迭代對象有什么特點

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 接口的數據結構:

Array

Map

Set

String

TypedArray

函數的 arguments 對象

NodeList 對象

自定義一個可迭代對象

上面我們說,一個對象只有具有正確的 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/...

關注公眾號,加入技術交流群。

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/104782.html

相關文章

  • Step-By-Step高頻面試深入解析 / 周刊05

    摘要:關于點擊進入項目是我于開始的一個項目,每個工作日發布一道面試題。那個率先改變的實例的返回值,就傳遞給的回調函數。通過插入標簽的方式來實現跨域,參數只能通過傳入,僅能支持請求。因此清除浮動,只需要觸發一個即可。 關于【Step-By-Step】 Step-By-Step (點擊進入項目) 是我于 2019-05-20 開始的一個項目,每個工作日發布一道面試題。每個周末我會仔細閱讀大家的...

    xiangchaobin 評論0 收藏0
  • Step-By-Step高頻面試深入解析 / 周刊06

    摘要:實例擁有構造函數屬性,該屬性返回創建實例對象的構造函數。在考慮對象而不是自定義類型和構造函數的情況下,寄生式繼承也是一種有用的模式。在子類的構造函數中,只有調用之后,才能使用關鍵字,否則報錯。 不積跬步無以至千里。 關于【Step-By-Step】 Step-By-Step (點擊進入項目) 是我于 2019-05-20 開始的一個項目,每個工作日發布一道面試題。每個周末我會仔細閱讀...

    LiuRhoRamen 評論0 收藏0

發表評論

0條評論

youkede

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<