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

資訊專欄INFORMATION COLUMN

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

youkede / 3350人閱讀

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

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

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

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

更多優(yōu)質文章可戳: https://github.com/YvetteLau/...

__

本周面試題一覽:

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

實現(xiàn) Promise.all 方法

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

請實現(xiàn)一個 flattenDeep 函數(shù),把嵌套的數(shù)組扁平化

可迭代對象有什么特點?

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

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

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

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

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

function foo() {
    var a = 2;
    function inner() {
        console.log(a);
    }
    outer(inner);
}
function outer(fn){
    fn(); //閉包
}
foo();
閉包的作用

能夠訪問函數(shù)定義時所在的詞法作用域(阻止其被回收)。

私有化變量

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

創(chuàng)建模塊

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

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

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

閉包的缺點

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

16. 實現(xiàn) Promise.all 方法

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

Promise.all 功能

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

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

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

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

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

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

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

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

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

Promise.all 返回的 promise 的狀態(tài)

如果傳入的參數(shù)中的 promise 都變成完成狀態(tài),Promise.all 返回的 promise 異步地變?yōu)橥瓿伞?/p>

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

在任何情況下,Promise.all 返回的 promise 的完成狀態(tài)的結果都是一個數(shù)組

Promise.all 實現(xiàn)
僅考慮傳入的參數(shù)是數(shù)組的情況
/** 僅考慮 promises 傳入的是數(shù)組的情況時 */
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 狀態(tài)都是 fulfilled,promise.all返回的實例才變成 fulfilled 態(tài)
                        resolve(result);
                    }
                }, err => {
                    reject(err);
                    return;
                });
            }
        }
    });
}

可使用 MDN 上的代碼進行測試

考慮 iterable 對象
Promise.all = function (promises) {
    /** promises 是一個可迭代對象,省略對參數(shù)類型的判斷 */
    return new Promise((resolve, reject) => {
        if (promises.length === 0) {
            //如果傳入的參數(shù)是空的可迭代對象
            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 的區(qū)別在于:

defer 要等到整個頁面在內存中正常渲染結束(DOM 結構完全生成,以及其他腳本執(zhí)行完成),在window.onload 之前執(zhí)行;

async 一旦下載完,渲染引擎就會中斷渲染,執(zhí)行這個腳本以后,再繼續(xù)渲染。

如果有多個 defer 腳本,會按照它們在頁面出現(xiàn)的順序加載

多個 async 腳本不能保證加載順序

動態(tài)創(chuàng)建 script 標簽

動態(tài)創(chuàng)建的 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. 請實現(xiàn)一個 flattenDeep 函數(shù),把嵌套的數(shù)組扁平化 利用 Array.prototype.flat

ES6 為數(shù)組實例新增了 flat 方法,用于將嵌套的數(shù)組“拉平”,變成一維的數(shù)組。該方法返回一個新數(shù)組,對原數(shù)組沒有影響。

flat 默認只會 “拉平” 一層,如果想要 “拉平” 多層的嵌套數(shù)組,需要給 flat 傳遞一個整數(shù),表示想要拉平的層數(shù)。

function flattenDeep(arr, deepLength) {
    return arr.flat(deepLength);
}
console.log(flattenDeep([1, [2, [3, [4]], 5]], 3));

當傳遞的整數(shù)大于數(shù)組嵌套的層數(shù)時,會將數(shù)組拉平為一維數(shù)組,JS能表示的最大數(shù)字為 Math.pow(2, 53) - 1,因此我們可以這樣定義 flattenDeep 函數(shù)

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 無限反嵌套多層嵌套數(shù)組
function flattenDeep(input) {
    const stack = [...input];
    const res = [];
    while (stack.length) {
        // 使用 pop 從 stack 中取出并移除值
        const next = stack.pop();
        if (Array.isArray(next)) {
            // 使用 push 送回內層數(shù)組中的元素,不會改動原始輸入 original input
            stack.push(...next);
        } else {
            res.push(next);
        }
    }
    // 使用 reverse 恢復原數(shù)組的順序
    return res.reverse();
}
console.log(flattenDeep([1, [2, [3, [4]], 5]]));
19. 可迭代對象有什么特點

ES6 規(guī)定,默認的 Iterator 接口部署在數(shù)據(jù)結構的 Symbol.iterator 屬性,換個角度,也可以認為,一個數(shù)據(jù)結構只要具有 Symbol.iterator 屬性(Symbol.iterator 方法對應的是遍歷器生成函數(shù),返回的是一個遍歷器對象),那么就可以其認為是可迭代的。

可迭代對象的特點

具有 Symbol.iterator 屬性,Symbol.iterator() 返回的是一個遍歷器對象

可以使用 for ... of 進行循環(huán)

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 接口的數(shù)據(jù)結構:

Array

Map

Set

String

TypedArray

函數(shù)的 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 函數(shù)中
        }
    }
};

for (var key of obj) {
    console.log(key); //Yvette 18 engineer
}
參考文章:

[1] MDN Promise.all

[2] Promise

[3] Iterator

謝謝各位小伙伴愿意花費寶貴的時間閱讀本文,如果本文給了您一點幫助或者是啟發(fā),請不要吝嗇你的贊和Star,您的肯定是我前進的最大動力。 https://github.com/YvetteLau/...

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

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

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

相關文章

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

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

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

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

    LiuRhoRamen 評論0 收藏0

發(fā)表評論

0條評論

youkede

|高級講師

TA的文章

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