成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專(zhuān)欄INFORMATION COLUMN

【面試篇】寒冬求職季之你必須要懂的原生JS(上)

AlphaWatch / 1098人閱讀

摘要:循環(huán)可以使用的范圍包括數(shù)組和結(jié)構(gòu)某些類(lèi)似數(shù)組的對(duì)象對(duì)象,以及字符串。只能遍歷數(shù)組,不能中斷,返回值是修改后的數(shù)組。除了之外,等,也有同樣的問(wèn)題。聲明一個(gè)只讀的常量。這在語(yǔ)法上,稱(chēng)為暫時(shí)性死區(qū)。暫時(shí)性死區(qū)也意味著不再是一個(gè)百分百安全的操作。

互聯(lián)網(wǎng)寒冬之際,各大公司都縮減了HC,甚至是采取了“裁員”措施,在這樣的大環(huán)境之下,想要獲得一份更好的工作,必然需要付出更多的努力。

一年前,也許你搞清楚閉包,this,原型鏈,就能獲得認(rèn)可。但是現(xiàn)在,很顯然是不行了。本文梳理出了一些面試中有一定難度的高頻原生JS問(wèn)題,部分知識(shí)點(diǎn)可能你之前從未關(guān)注過(guò),或者看到了,卻沒(méi)有仔細(xì)研究,但是它們卻非常重要。

本文將以真實(shí)的面試題的形式來(lái)呈現(xiàn)知識(shí)點(diǎn),大家在閱讀時(shí),建議不要先看我的答案,而是自己先思考一番。盡管,本文所有的答案,都是我在翻閱各種資料,思考并驗(yàn)證之后,才給出的(絕非復(fù)制粘貼而來(lái))。但因水平有限,本人的答案未必是最優(yōu)的,如果您有更好的答案,歡迎給我留言。

本文篇幅較長(zhǎng),但是滿(mǎn)滿(mǎn)的都是干貨!并且還埋伏了可愛(ài)的表情包,希望小伙伴們能夠堅(jiān)持讀完。

衷心的祝愿大家都能找到心儀的工作。

1. 基本類(lèi)型有哪幾種?null 是對(duì)象嗎?基本數(shù)據(jù)類(lèi)型和復(fù)雜數(shù)據(jù)類(lèi)型存儲(chǔ)有什么區(qū)別?

基本類(lèi)型有6種,分別是undefined,null,bool,string,number,symbol(ES6新增)。

雖然 typeof null 返回的值是 object,但是null不是對(duì)象,而是基本數(shù)據(jù)類(lèi)型的一種。

基本數(shù)據(jù)類(lèi)型存儲(chǔ)在棧內(nèi)存,存儲(chǔ)的是值。

復(fù)雜數(shù)據(jù)類(lèi)型的值存儲(chǔ)在堆內(nèi)存,地址(指向堆中的值)存儲(chǔ)在棧內(nèi)存。當(dāng)我們把對(duì)象賦值給另外一個(gè)變量的時(shí)候,復(fù)制的是地址,指向同一塊內(nèi)存空間,當(dāng)其中一個(gè)對(duì)象改變時(shí),另一個(gè)對(duì)象也會(huì)變化。


2. typeof 是否正確判斷類(lèi)型");

首先 typeof 能夠正確的判斷基本數(shù)據(jù)類(lèi)型,但是除了 null, typeof null輸出的是對(duì)象。

但是對(duì)象來(lái)說(shuō),typeof 不能正確的判斷其類(lèi)型, typeof 一個(gè)函數(shù)可以輸出 "function",而除此之外,輸出的全是 object,這種情況下,我們無(wú)法準(zhǔn)確的知道對(duì)象的類(lèi)型。

instanceof可以準(zhǔn)確的判斷復(fù)雜數(shù)據(jù)類(lèi)型,但是不能正確判斷基本數(shù)據(jù)類(lèi)型。(正確判斷數(shù)據(jù)類(lèi)型請(qǐng)戳:github.com/YvetteLau/B…)

instanceof 是通過(guò)原型鏈判斷的,A instanceof B, 在A(yíng)的原型鏈中層層查找,是否有原型等于B.prototype,如果一直找到A的原型鏈的頂端(null;即Object.prototype.__proto__),仍然不等于B.prototype,那么返回false,否則返回true.

instanceof的實(shí)現(xiàn)代碼:

// L instanceof R
function instance_of(L, R) {//L 表示左表達(dá)式,R 表示右表達(dá)式
    var O = R.prototype;// 取 R 的顯式原型
    L = L.__proto__;    // 取 L 的隱式原型
    while (true) { 
        if (L === null) //已經(jīng)找到頂層
            return false;  
        if (O === L)   //當(dāng) O 嚴(yán)格等于 L 時(shí),返回 true
            return true; 
        L = L.__proto__;  //繼續(xù)向上一層原型鏈查找
    } 
}

3. for of , for in 和 forEach,map 的區(qū)別。

for...of循環(huán):具有 iterator 接口,就可以用for...of循環(huán)遍歷它的成員(屬性值)。for...of循環(huán)可以使用的范圍包括數(shù)組、Set 和 Map 結(jié)構(gòu)、某些類(lèi)似數(shù)組的對(duì)象、Generator 對(duì)象,以及字符串。for...of循環(huán)調(diào)用遍歷器接口,數(shù)組的遍歷器接口只返回具有數(shù)字索引的屬性。對(duì)于普通的對(duì)象,for...of結(jié)構(gòu)不能直接使用,會(huì)報(bào)錯(cuò),必須部署了 Iterator 接口后才能使用??梢灾袛嘌h(huán)。

for...in循環(huán):遍歷對(duì)象自身的和繼承的可枚舉的屬性, 不能直接獲取屬性值。可以中斷循環(huán)。

forEach: 只能遍歷數(shù)組,不能中斷,沒(méi)有返回值(或認(rèn)為返回值是undefined)。

map: 只能遍歷數(shù)組,不能中斷,返回值是修改后的數(shù)組。

PS: Object.keys():返回給定對(duì)象所有可枚舉屬性的字符串?dāng)?shù)組。

關(guān)于forEach是否會(huì)改變?cè)瓟?shù)組的問(wèn)題,有些小伙伴提出了異議,為此我寫(xiě)了代碼測(cè)試了下(注意數(shù)組項(xiàng)是復(fù)雜數(shù)據(jù)類(lèi)型的情況)。 除了forEach之外,map等API,也有同樣的問(wèn)題。

let arry = [1, 2, 3, 4];

arry.forEach((item) => {
    item *= 10;
});
console.log(arry); //[1, 2, 3, 4]

arry.forEach((item) => {
    arry[1] = 10; //直接操作數(shù)組
});
console.log(arry); //[ 1, 10, 3, 4 ]

let arry2 = [
    { name: "Yve" },
    { age: 20 }
];
arry2.forEach((item) => {
    item.name = 10;
});
console.log(arry2);//[ { name: 10 }, { age: 20, name: 10 } ]

如還不了解 iterator 接口或 for...of, 請(qǐng)先閱讀ES6文檔: Iterator 和 for...of 循環(huán)

更多細(xì)節(jié)請(qǐng)戳: github.com/YvetteLau/B…


4. 如何判斷一個(gè)變量是不是數(shù)組?

使用 Array.isArray 判斷,如果返回 true, 說(shuō)明是數(shù)組

使用 instanceof Array 判斷,如果返回true, 說(shuō)明是數(shù)組

使用 Object.prototype.toString.call 判斷,如果值是 [object Array], 說(shuō)明是數(shù)組

通過(guò) constructor 來(lái)判斷,如果是數(shù)組,那么 arr.constructor === Array. (不準(zhǔn)確,因?yàn)槲覀兛梢灾付?obj.constructor = Array)

function fn() {
    console.log(Array.isArray(arguments));   //false; 因?yàn)閍rguments是類(lèi)數(shù)組,但不是數(shù)組
    console.log(Array.isArray([1,2,3,4]));   //true
    console.log(arguments instanceof Array); //fasle
    console.log([1,2,3,4] instanceof Array); //true
    console.log(Object.prototype.toString.call(arguments)); //[object Arguments]
    console.log(Object.prototype.toString.call([1,2,3,4])); //[object Array]
    console.log(arguments.constructor === Array); //false
    arguments.constructor = Array;
    console.log(arguments.constructor === Array); //true
    console.log(Array.isArray(arguments));        //false
}
fn(1,2,3,4);

5. 類(lèi)數(shù)組和數(shù)組的區(qū)別是什么?

類(lèi)數(shù)組:

1)擁有l(wèi)ength屬性,其它屬性(索引)為非負(fù)整數(shù)(對(duì)象中的索引會(huì)被當(dāng)做字符串來(lái)處理);

2)不具有數(shù)組所具有的方法;

類(lèi)數(shù)組是一個(gè)普通對(duì)象,而真實(shí)的數(shù)組是Array類(lèi)型。

常見(jiàn)的類(lèi)數(shù)組有: 函數(shù)的參數(shù) arguments, DOM 對(duì)象列表(比如通過(guò) document.querySelectorAll 得到的列表), jQuery 對(duì)象 (比如 $("div")).

類(lèi)數(shù)組可以轉(zhuǎn)換為數(shù)組:

//第一種方法
Array.prototype.slice.call(arrayLike, start);
//第二種方法
[...arrayLike];
//第三種方法:
Array.from(arrayLike);

PS: 任何定義了遍歷器(Iterator)接口的對(duì)象,都可以用擴(kuò)展運(yùn)算符轉(zhuǎn)為真正的數(shù)組。

Array.from方法用于將兩類(lèi)對(duì)象轉(zhuǎn)為真正的數(shù)組:類(lèi)似數(shù)組的對(duì)象(array-like object)和可遍歷(iterable)的對(duì)象。


6. == 和 === 有什么區(qū)別?

=== 不需要進(jìn)行類(lèi)型轉(zhuǎn)換,只有類(lèi)型相同并且值相等時(shí),才返回 true.

== 如果兩者類(lèi)型不同,首先需要進(jìn)行類(lèi)型轉(zhuǎn)換。具體流程如下:

    首先判斷兩者類(lèi)型是否相同,如果相等,判斷值是否相等.

    如果類(lèi)型不同,進(jìn)行類(lèi)型轉(zhuǎn)換

    判斷比較的是否是 null 或者是 undefined, 如果是, 返回 true .

    判斷兩者類(lèi)型是否為 string 和 number, 如果是, 將字符串轉(zhuǎn)換成 number

    判斷其中一方是否為 boolean, 如果是, 將 boolean 轉(zhuǎn)為 number 再進(jìn)行判斷

    判斷其中一方是否為 object 且另一方為 string、number 或者 symbol , 如果是, 將 object 轉(zhuǎn)為原始類(lèi)型再進(jìn)行判斷

let person1 = {
    age: 25
}
let person2 = person1;
person2.gae = 20;
console.log(person1 === person2); //true,注意復(fù)雜數(shù)據(jù)類(lèi)型,比較的是引用地址
思考: [] == ![]

我們來(lái)分析一下: [] == ![] 是true還是false?

    首先,我們需要知道 ! 優(yōu)先級(jí)是高于 == (更多運(yùn)算符優(yōu)先級(jí)可查看: 運(yùn)算符優(yōu)先級(jí))

    ![] 引用類(lèi)型轉(zhuǎn)換成布爾值都是true,因此![]的是false

    根據(jù)上面的比較步驟中的第五條,其中一方是 boolean,將 boolean 轉(zhuǎn)為 number 再進(jìn)行判斷,false轉(zhuǎn)換成 number,對(duì)應(yīng)的值是 0.

    根據(jù)上面比較步驟中的第六條,有一方是 number,那么將object也轉(zhuǎn)換成Number,空數(shù)組轉(zhuǎn)換成數(shù)字,對(duì)應(yīng)的值是0.(空數(shù)組轉(zhuǎn)換成數(shù)字,對(duì)應(yīng)的值是0,如果數(shù)組中只有一個(gè)數(shù)字,那么轉(zhuǎn)成number就是這個(gè)數(shù)字,其它情況,均為NaN)

    0 == 0; 為true


7. ES6中的class和ES5的類(lèi)有什么區(qū)別?

    ES6 class 內(nèi)部所有定義的方法都是不可枚舉的;

    ES6 class 必須使用 new 調(diào)用;

    ES6 class 不存在變量提升;

    ES6 class 默認(rèn)即是嚴(yán)格模式;

    ES6 class 子類(lèi)必須在父類(lèi)的構(gòu)造函數(shù)中調(diào)用super(),這樣才有this對(duì)象;ES5中類(lèi)繼承的關(guān)系是相反的,先有子類(lèi)的this,然后用父類(lèi)的方法應(yīng)用在this上。


8. 數(shù)組的哪些API會(huì)改變?cè)瓟?shù)組?

修改原數(shù)組的API有:

splice/reverse/fill/copyWithin/sort/push/pop/unshift/shift

不修改原數(shù)組的API有:

slice/map/forEach/every/filter/reduce/entries/find

注: 數(shù)組的每一項(xiàng)是簡(jiǎn)單數(shù)據(jù)類(lèi)型,且未直接操作數(shù)組的情況下(稍后會(huì)對(duì)此題重新作答)。


9. let、const 以及 var 的區(qū)別是什么?

let 和 const 定義的變量不會(huì)出現(xiàn)變量提升,而 var 定義的變量會(huì)提升。

let 和 const 是JS中的塊級(jí)作用域

let 和 const 不允許重復(fù)聲明(會(huì)拋出錯(cuò)誤)

let 和 const 定義的變量在定義語(yǔ)句之前,如果使用會(huì)拋出錯(cuò)誤(形成了暫時(shí)性死區(qū)),而 var 不會(huì)。

const 聲明一個(gè)只讀的常量。一旦聲明,常量的值就不能改變(如果聲明是一個(gè)對(duì)象,那么不能改變的是對(duì)象的引用地址)


10. 在JS中什么是變量提升?什么是暫時(shí)性死區(qū)?

變量提升就是變量在聲明之前就可以使用,值為undefined。

在代碼塊內(nèi),使用 let/const 命令聲明變量之前,該變量都是不可用的(會(huì)拋出錯(cuò)誤)。這在語(yǔ)法上,稱(chēng)為“暫時(shí)性死區(qū)”。暫時(shí)性死區(qū)也意味著 typeof 不再是一個(gè)百分百安全的操作。

typeof x; // ReferenceError(暫時(shí)性死區(qū),拋錯(cuò))
let x;
typeof y; // 值是undefined,不會(huì)報(bào)錯(cuò)

暫時(shí)性死區(qū)的本質(zhì)就是,只要一進(jìn)入當(dāng)前作用域,所要使用的變量就已經(jīng)存在了,但是不可獲取,只有等到聲明變量的那一行代碼出現(xiàn),才可以獲取和使用該變量。


11. 如何正確的判斷this");

this的綁定規(guī)則有四種:默認(rèn)綁定,隱式綁定,顯式綁定,new綁定.

    函數(shù)是否在 new 中調(diào)用(new綁定),如果是,那么 this 綁定的是新創(chuàng)建的對(duì)象【前提是構(gòu)造函數(shù)中沒(méi)有返回對(duì)象或者是function,否則this指向返回的對(duì)象/function】。

    函數(shù)是否通過(guò) call,apply 調(diào)用,或者使用了 bind (即硬綁定),如果是,那么this綁定的就是指定的對(duì)象。

    函數(shù)是否在某個(gè)上下文對(duì)象中調(diào)用(隱式綁定),如果是的話(huà),this 綁定的是那個(gè)上下文對(duì)象。一般是 obj.foo()

    如果以上都不是,那么使用默認(rèn)綁定。如果在嚴(yán)格模式下,則綁定到 undefined,否則綁定到全局對(duì)象。

    如果把 null 或者 undefined 作為 this 的綁定對(duì)象傳入 call、apply 或者 bind, 這些值在調(diào)用時(shí)會(huì)被忽略,實(shí)際應(yīng)用的是默認(rèn)綁定規(guī)則。

    箭頭函數(shù)沒(méi)有自己的 this, 它的this繼承于上一層代碼塊的this。

測(cè)試下是否已經(jīng)成功Get了此知識(shí)點(diǎn)(瀏覽器執(zhí)行環(huán)境):

var number = 5;
var obj = {
    number: 3,
    fn1: (function () {
        var number;
        this.number *= 2;
        number = number * 2;
        number = 3;
        return function () {
            var num = this.number;
            this.number *= 2;
            console.log(num);
            number *= 3;
            console.log(number);
        }
    })()
}
var fn1 = obj.fn1;
fn1.call(null);
obj.fn1();
console.log(window.number);

如果this的知識(shí)點(diǎn),您還不太懂,請(qǐng)戳: 嗨,你真的懂this嗎?


12. 詞法作用域和this的區(qū)別。

詞法作用域是由你在寫(xiě)代碼時(shí)將變量和塊作用域?qū)懺谀睦飦?lái)決定的

this 是在調(diào)用時(shí)被綁定的,this 指向什么,完全取決于函數(shù)的調(diào)用位置(關(guān)于this的指向問(wèn)題,本文已經(jīng)有說(shuō)明)


13. 談?wù)勀銓?duì)JS執(zhí)行上下文棧和作用域鏈的理解。

執(zhí)行上下文就是當(dāng)前 JavaScript 代碼被解析和執(zhí)行時(shí)所在環(huán)境, JS執(zhí)行上下文??梢哉J(rèn)為是一個(gè)存儲(chǔ)函數(shù)調(diào)用的棧結(jié)構(gòu),遵循先進(jìn)后出的原則。

JavaScript執(zhí)行在單線(xiàn)程上,所有的代碼都是排隊(duì)執(zhí)行。

一開(kāi)始瀏覽器執(zhí)行全局的代碼時(shí),首先創(chuàng)建全局的執(zhí)行上下文,壓入執(zhí)行棧的頂部。

每當(dāng)進(jìn)入一個(gè)函數(shù)的執(zhí)行就會(huì)創(chuàng)建函數(shù)的執(zhí)行上下文,并且把它壓入執(zhí)行棧的頂部。當(dāng)前函數(shù)執(zhí)行-完成后,當(dāng)前函數(shù)的執(zhí)行上下文出棧,并等待垃圾回收。

瀏覽器的JS執(zhí)行引擎總是訪(fǎng)問(wèn)棧頂?shù)膱?zhí)行上下文。

全局上下文只有唯一的一個(gè),它在瀏覽器關(guān)閉時(shí)出棧。

作用域鏈: 無(wú)論是 LHS 還是 RHS 查詢(xún),都會(huì)在當(dāng)前的作用域開(kāi)始查找,如果沒(méi)有找到,就會(huì)向上級(jí)作用域繼續(xù)查找目標(biāo)標(biāo)識(shí)符,每次上升一個(gè)作用域,一直到全局作用域?yàn)橹埂?/p>


題難不難?不難!繼續(xù)挑戰(zhàn)一下!難!知道難,就更要繼續(xù)了!

14. 什么是閉包?閉包的作用是什么?閉包有哪些使用場(chǎng)景?

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

閉包的作用有:

    封裝私有變量

    模仿塊級(jí)作用域(ES5中沒(méi)有塊級(jí)作用域)

    實(shí)現(xiàn)JS的模塊


15. call、apply有什么區(qū)別?call,aplly和bind的內(nèi)部是如何實(shí)現(xiàn)的?

call 和 apply 的功能相同,區(qū)別在于傳參的方式不一樣:

fn.call(obj, arg1, arg2, ...),調(diào)用一個(gè)函數(shù), 具有一個(gè)指定的this值和分別地提供的參數(shù)(參數(shù)的列表)。

fn.apply(obj, [argsArray]),調(diào)用一個(gè)函數(shù),具有一個(gè)指定的this值,以及作為一個(gè)數(shù)組(或類(lèi)數(shù)組對(duì)象)提供的參數(shù)。

call核心:

將函數(shù)設(shè)為傳入?yún)?shù)的屬性

指定this到函數(shù)并傳入給定參數(shù)執(zhí)行函數(shù)

如果不傳入?yún)?shù)或者參數(shù)為null,默認(rèn)指向?yàn)?window / global

刪除參數(shù)上的函數(shù)

Function.prototype.call = function (context) {
    /** 如果第一個(gè)參數(shù)傳入的是 null 或者是 undefined, 那么指向this指向 window/global */
    /** 如果第一個(gè)參數(shù)傳入的不是null或者是undefined, 那么必須是一個(gè)對(duì)象 */
    if (!context) {
        //context為null或者是undefined
        context = typeof window === "undefined" ");window;
    }
    context.fn = this; //this指向的是當(dāng)前的函數(shù)(Function的實(shí)例)
    let rest = [...arguments].slice(1);//獲取除了this指向?qū)ο笠酝獾膮?shù), 空數(shù)組slice后返回的仍然是空數(shù)組
    let result = context.fn(...rest); //隱式綁定,當(dāng)前函數(shù)的this指向了context.
    delete context.fn;
    return result;
}

//測(cè)試代碼
var foo = {
    name: "Selina"
}
var name = "Chirs";
function bar(job, age) {
    console.log(this.name);
    console.log(job, age);
}
bar.call(foo, "programmer", 20);
// Selina programmer 20
bar.call(null, "teacher", 25);
// 瀏覽器環(huán)境: Chirs teacher 25; node 環(huán)境: undefined teacher 25

apply:

apply的實(shí)現(xiàn)和call很類(lèi)似,但是需要注意他們的參數(shù)是不一樣的,apply的第二個(gè)參數(shù)是數(shù)組或類(lèi)數(shù)組.

Function.prototype.apply = function (context, rest) {
    if (!context) {
        //context為null或者是undefined時(shí),設(shè)置默認(rèn)值
        context = typeof window === "undefined" ");window;
    }
    context.fn = this;
    let result;
    if(rest === undefined || rest === null) {
        //undefined 或者 是 null 不是 Iterator 對(duì)象,不能被 ...
        result = context.fn(rest);
    }else if(typeof rest === "object") {
        result = context.fn(...rest);
    }
    delete context.fn;
    return result;
}
var foo = {
    name: "Selina"
}
var name = "Chirs";
function bar(job, age) {
    console.log(this.name);
    console.log(job, age);
}
bar.apply(foo, ["programmer", 20]);
// Selina programmer 20
bar.apply(null, ["teacher", 25]);
// 瀏覽器環(huán)境: Chirs programmer 20; node 環(huán)境: undefined teacher 25

bind

bind 和 call/apply 有一個(gè)很重要的區(qū)別,一個(gè)函數(shù)被 call/apply 的時(shí)候,會(huì)直接調(diào)用,但是 bind 會(huì)創(chuàng)建一個(gè)新函數(shù)。當(dāng)這個(gè)新函數(shù)被調(diào)用時(shí),bind() 的第一個(gè)參數(shù)將作為它運(yùn)行時(shí)的 this,之后的一序列參數(shù)將會(huì)在傳遞的實(shí)參前傳入作為它的參數(shù)。

Function.prototype.bind = function(context) {
    if(typeof this !== "function"){
       throw new TypeError("not a function");
    }
    let self = this;
    let args = [...arguments].slice(1);
    function Fn() {};
    Fn.prototype = this.prototype;
    let bound = function() {
        let res = [...args, ...arguments]; //bind傳遞的參數(shù)和函數(shù)調(diào)用時(shí)傳遞的參數(shù)拼接
        context = this instanceof Fn ");this : context || this;
        return self.apply(context, res);
    }
    //原型鏈
    bound.prototype = new Fn();
    return bound;
}

var name = "Jack";
function person(age, job, gender){
    console.log(this.name , age, job, gender);
}
var Yve = {name : "Yvette"};
let result = person.bind(Yve, 22, "enginner")("female");	

16. new的原理是什么?通過(guò)new的方式創(chuàng)建對(duì)象和通過(guò)字面量創(chuàng)建有什么區(qū)別?

new:

    創(chuàng)建一個(gè)新對(duì)象。

    這個(gè)新對(duì)象會(huì)被執(zhí)行[[原型]]連接。

    屬性和方法被加入到 this 引用的對(duì)象中。并執(zhí)行了構(gòu)造函數(shù)中的方法.

    如果函數(shù)沒(méi)有返回其他對(duì)象,那么this指向這個(gè)新對(duì)象,否則this指向構(gòu)造函數(shù)中返回的對(duì)象。

function new(func) {
    let target = {};
    target.__proto__ = func.prototype;
    let res = func.call(target);
    if (res && typeof(res) == "object" || typeof(res) == "function") {
    	return res;
    }
    return target;
}

字面量創(chuàng)建對(duì)象,不會(huì)調(diào)用 Object構(gòu)造函數(shù), 簡(jiǎn)潔且性能更好;

new Object() 方式創(chuàng)建對(duì)象本質(zhì)上是方法調(diào)用,涉及到在proto鏈中遍歷該方法,當(dāng)找到該方法后,又會(huì)生產(chǎn)方法調(diào)用必須的 堆棧信息,方法調(diào)用結(jié)束后,還要釋放該堆棧,性能不如字面量的方式。

通過(guò)對(duì)象字面量定義對(duì)象時(shí),不會(huì)調(diào)用Object構(gòu)造函數(shù)。


17. 談?wù)勀銓?duì)原型的理解?

在 JavaScript 中,每當(dāng)定義一個(gè)對(duì)象(函數(shù)也是對(duì)象)時(shí)候,對(duì)象中都會(huì)包含一些預(yù)定義的屬性。其中每個(gè)函數(shù)對(duì)象都有一個(gè)prototype 屬性,這個(gè)屬性指向函數(shù)的原型對(duì)象。使用原型對(duì)象的好處是所有對(duì)象實(shí)例共享它所包含的屬性和方法。


18. 什么是原型鏈?【原型鏈解決的是什么問(wèn)題?】

原型鏈解決的主要是繼承問(wèn)題。

每個(gè)對(duì)象擁有一個(gè)原型對(duì)象,通過(guò) proto (讀音: dunder proto) 指針指向其原型對(duì)象,并從中繼承方法和屬性,同時(shí)原型對(duì)象也可能擁有原型,這樣一層一層,最終指向 null(Object.proptotype.__proto__ 指向的是null)。這種關(guān)系被稱(chēng)為原型鏈 (prototype chain),通過(guò)原型鏈一個(gè)對(duì)象可以擁有定義在其他對(duì)象中的屬性和方法。

構(gòu)造函數(shù) Parent、Parent.prototype 和 實(shí)例 p 的關(guān)系如下:(p.__proto__ === Parent.prototype)


19. prototype 和 __proto__ 區(qū)別是什么?

prototype是構(gòu)造函數(shù)的屬性。

__proto__ 是每個(gè)實(shí)例都有的屬性,可以訪(fǎng)問(wèn) [[prototype]] 屬性。

實(shí)例的__proto__ 與其構(gòu)造函數(shù)的prototype指向的是同一個(gè)對(duì)象。

function Student(name) {
    this.name = name;
}
Student.prototype.setAge = function(){
    this.age=20;
}
let Jack = new Student("jack");
console.log(Jack.__proto__);
//console.log(Object.getPrototypeOf(Jack));;
console.log(Student.prototype);
console.log(Jack.__proto__ === Student.prototype);//true

20. 使用ES5實(shí)現(xiàn)一個(gè)繼承?

組合繼承(最常用的繼承方式)

function SuperType(name) {
    this.name = name;
    this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function() {
    console.log(this.name);
}

function SubType(name, age) {
    SuperType.call(this, name);
    this.age = age;
}
SubType.prototype = new SuperType();
SubType.prototype.constructor = SubType;

SubType.prototype.sayAge = function() {
    console.log(this.age);
}

其它繼承方式實(shí)現(xiàn),可以參考《JavaScript高級(jí)程序設(shè)計(jì)》


21. 什么是深拷貝?深拷貝和淺拷貝有什么區(qū)別?

淺拷貝是指只復(fù)制第一層對(duì)象,但是當(dāng)對(duì)象的屬性是引用類(lèi)型時(shí),實(shí)質(zhì)復(fù)制的是其引用,當(dāng)引用指向的值改變時(shí)也會(huì)跟著變化。

深拷貝復(fù)制變量值,對(duì)于非基本類(lèi)型的變量,則遞歸至基本類(lèi)型變量后,再?gòu)?fù)制。深拷貝后的對(duì)象與原來(lái)的對(duì)象是完全隔離的,互不影響,對(duì)一個(gè)對(duì)象的修改并不會(huì)影響另一個(gè)對(duì)象。

實(shí)現(xiàn)一個(gè)深拷貝:

function deepClone(obj) { //遞歸拷貝
    if(obj === null) return null; //null 的情況
    if(obj instanceof RegExp) return new RegExp(obj);
    if(obj instanceof Date) return new Date(obj);
    if(typeof obj !== "object") {
        //如果不是復(fù)雜數(shù)據(jù)類(lèi)型,直接返回
        return obj;
    }
    /**
     * 如果obj是數(shù)組,那么 obj.constructor 是 [Function: Array]
     * 如果obj是對(duì)象,那么 obj.constructor 是 [Function: Object]
     */
    let t = new obj.constructor();
    for(let key in obj) {
        //如果 obj[key] 是復(fù)雜數(shù)據(jù)類(lèi)型,遞歸
        t[key] = deepClone(obj[key]);
    }
    return t;
}

看不下去了?別人的送分題會(huì)成為你的送命題

22. 防抖和節(jié)流的區(qū)別是什么?防抖和節(jié)流的實(shí)現(xiàn)。

防抖和節(jié)流的作用都是防止函數(shù)多次調(diào)用。區(qū)別在于,假設(shè)一個(gè)用戶(hù)一直觸發(fā)這個(gè)函數(shù),且每次觸發(fā)函數(shù)的間隔小于設(shè)置的時(shí)間,防抖的情況下只會(huì)調(diào)用一次,而節(jié)流的情況會(huì)每隔一定時(shí)間調(diào)用一次函數(shù)。

防抖(debounce): n秒內(nèi)函數(shù)只會(huì)執(zhí)行一次,如果n秒內(nèi)高頻事件再次被觸發(fā),則重新計(jì)算時(shí)間

function debounce(func, wait, immediate=true) {
    let timeout, context, args;
        // 延遲執(zhí)行函數(shù)
        const later = () => setTimeout(() => {
            // 延遲函數(shù)執(zhí)行完畢,清空定時(shí)器
            timeout = null
            // 延遲執(zhí)行的情況下,函數(shù)會(huì)在延遲函數(shù)中執(zhí)行
            // 使用到之前緩存的參數(shù)和上下文
            if (!immediate) {
                func.apply(context, args);
                context = args = null;
            }
        }, wait);
        let debounced = function (...params) {
            if (!timeout) {
                timeout = later();
                if (immediate) {
                    //立即執(zhí)行
                    func.apply(this, params);
                } else {
                    //閉包
                    context = this;
                    args = params;
                }
            } else {
                clearTimeout(timeout);
                timeout = later();
            }
        }
    debounced.cancel = function () {
        clearTimeout(timeout);
        timeout = null;
    };
    return debounced;
};

防抖的應(yīng)用場(chǎng)景:

每次 resize/scroll 觸發(fā)統(tǒng)計(jì)事件

文本輸入的驗(yàn)證(連續(xù)輸入文字后發(fā)送 AJAX 請(qǐng)求進(jìn)行驗(yàn)證,驗(yàn)證一次就好)

節(jié)流(throttle): 高頻事件在規(guī)定時(shí)間內(nèi)只會(huì)執(zhí)行一次,執(zhí)行一次后,只有大于設(shè)定的執(zhí)行周期后才會(huì)執(zhí)行第二次。

//underscore.js
function throttle(func, wait, options) {
    var timeout, context, args, result;
    var previous = 0;
    if (!options) options = {};

    var later = function () {
        previous = options.leading === false ");0 : Date.now() || new Date().getTime();
        timeout = null;
        result = func.apply(context, args);
        if (!timeout) context = args = null;
    };

    var throttled = function () {
        var now = Date.now() || new Date().getTime();
        if (!previous && options.leading === false) previous = now;
        var remaining = wait - (now - previous);
        context = this;
        args = arguments;
        if (remaining <= 0 || remaining > wait) {
            if (timeout) {
                clearTimeout(timeout);
                timeout = null;
            }
            previous = now;
            result = func.apply(context, args);
            if (!timeout) context = args = null;
        } else if (!timeout && options.trailing !== false) {
            // 判斷是否設(shè)置了定時(shí)器和 trailing
            timeout = setTimeout(later, remaining);
        }
        return result;
    };

    throttled.cancel = function () {
        clearTimeout(timeout);
        previous = 0;
        timeout = context = args = null;
    };

    return throttled;
};

函數(shù)節(jié)流的應(yīng)用場(chǎng)景有:

DOM 元素的拖拽功能實(shí)現(xiàn)(mousemove)

射擊游戲的 mousedown/keydown 事件(單位時(shí)間只能發(fā)射一顆子彈)

計(jì)算鼠標(biāo)移動(dòng)的距離(mousemove)

Canvas 模擬畫(huà)板功能(mousemove)

搜索聯(lián)想(keyup)

監(jiān)聽(tīng)滾動(dòng)事件判斷是否到頁(yè)面底部自動(dòng)加載更多:給 scroll 加了 debounce 后,只有用戶(hù)停止?jié)L動(dòng)后,才會(huì)判斷是否到了頁(yè)面底部;如果是 throttle 的話(huà),只要頁(yè)面滾動(dòng)就會(huì)間隔一段時(shí)間判斷一次


23. 取數(shù)組的最大值(ES5、ES6)
// ES5 的寫(xiě)法
Math.max.apply(null, [14, 3, 77, 30]);

// ES6 的寫(xiě)法
Math.max(...[14, 3, 77, 30]);

// reduce
[14,3,77,30].reduce((accumulator, currentValue)=>{
    return accumulator = accumulator > currentValue ");

24. ES6新的特性有哪些?

    新增了塊級(jí)作用域(let,const)

    提供了定義類(lèi)的語(yǔ)法糖(class)

    新增了一種基本數(shù)據(jù)類(lèi)型(Symbol)

    新增了變量的解構(gòu)賦值

    函數(shù)參數(shù)允許設(shè)置默認(rèn)值,引入了rest參數(shù),新增了箭頭函數(shù)

    數(shù)組新增了一些API,如 isArray / from / of 方法;數(shù)組實(shí)例新增了 entries(),keys() 和 values() 等方法

    對(duì)象和數(shù)組新增了擴(kuò)展運(yùn)算符

    ES6 新增了模塊化(import/export)

    ES6 新增了 Set 和 Map 數(shù)據(jù)結(jié)構(gòu)

    ES6 原生提供 Proxy 構(gòu)造函數(shù),用來(lái)生成 Proxy 實(shí)例

    ES6 新增了生成器(Generator)和遍歷器(Iterator)


25. setTimeout倒計(jì)時(shí)為什么會(huì)出現(xiàn)誤差?

setTimeout() 只是將事件插入了“任務(wù)隊(duì)列”,必須等當(dāng)前代碼(執(zhí)行棧)執(zhí)行完,主線(xiàn)程才會(huì)去執(zhí)行它指定的回調(diào)函數(shù)。要是當(dāng)前代碼消耗時(shí)間很長(zhǎng),也有可能要等很久,所以并沒(méi)辦法保證回調(diào)函數(shù)一定會(huì)在 setTimeout() 指定的時(shí)間執(zhí)行。所以, setTimeout() 的第二個(gè)參數(shù)表示的是最少時(shí)間,并非是確切時(shí)間。

HTML5標(biāo)準(zhǔn)規(guī)定了 setTimeout() 的第二個(gè)參數(shù)的最小值不得小于4毫秒,如果低于這個(gè)值,則默認(rèn)是4毫秒。在此之前。老版本的瀏覽器都將最短時(shí)間設(shè)為10毫秒。另外,對(duì)于那些DOM的變動(dòng)(尤其是涉及頁(yè)面重新渲染的部分),通常是間隔16毫秒執(zhí)行。這時(shí)使用 requestAnimationFrame() 的效果要好于 setTimeout();


26. 為什么 0.1 + 0.2 != 0.3 ");

0.1 + 0.2 != 0.3 是因?yàn)樵谶M(jìn)制轉(zhuǎn)換和進(jìn)階運(yùn)算的過(guò)程中出現(xiàn)精度損失。

下面是詳細(xì)解釋:

JavaScript使用 Number 類(lèi)型表示數(shù)字(整數(shù)和浮點(diǎn)數(shù)),使用64位表示一個(gè)數(shù)字。


圖片說(shuō)明:

第0位:符號(hào)位,0表示正數(shù),1表示負(fù)數(shù)(s)

第1位到第11位:儲(chǔ)存指數(shù)部分(e)

第12位到第63位:儲(chǔ)存小數(shù)部分(即有效數(shù)字)f

計(jì)算機(jī)無(wú)法直接對(duì)十進(jìn)制的數(shù)字進(jìn)行運(yùn)算, 需要先對(duì)照 IEEE 754 規(guī)范轉(zhuǎn)換成二進(jìn)制,然后對(duì)階運(yùn)算。

1.進(jìn)制轉(zhuǎn)換

0.1和0.2轉(zhuǎn)換成二進(jìn)制后會(huì)無(wú)限循環(huán)

0.1 -> 0.0001100110011001...(無(wú)限循環(huán))
0.2 -> 0.0011001100110011...(無(wú)限循環(huán))

但是由于IEEE 754尾數(shù)位數(shù)限制,需要將后面多余的位截掉,這樣在進(jìn)制之間的轉(zhuǎn)換中精度已經(jīng)損失。

2.對(duì)階運(yùn)算

由于指數(shù)位數(shù)不相同,運(yùn)算時(shí)需要對(duì)階運(yùn)算 這部分也可能產(chǎn)生精度損失。

按照上面兩步運(yùn)算(包括兩步的精度損失),最后的結(jié)果是

0.0100110011001100110011001100110011001100110011001100

結(jié)果轉(zhuǎn)換成十進(jìn)制之后就是 0.30000000000000004。

27. promise 有幾種狀態(tài), Promise 有什么優(yōu)缺點(diǎn) ");

promise有三種狀態(tài): fulfilled, rejected, pending.

Promise 的優(yōu)點(diǎn):

    一旦狀態(tài)改變,就不會(huì)再變,任何時(shí)候都可以得到這個(gè)結(jié)果

    可以將異步操作以同步操作的流程表達(dá)出來(lái),避免了層層嵌套的回調(diào)函數(shù)

Promise 的缺點(diǎn):

    無(wú)法取消 Promise

    當(dāng)處于pending狀態(tài)時(shí),無(wú)法得知目前進(jìn)展到哪一個(gè)階段


28. Promise構(gòu)造函數(shù)是同步還是異步執(zhí)行,then中的方法呢 ");

Promise的構(gòu)造函數(shù)是同步執(zhí)行的。then 中的方法是異步執(zhí)行的。

promise的then實(shí)現(xiàn),詳見(jiàn): Promise源碼實(shí)現(xiàn)


29. Promise和setTimeout的區(qū)別 ");

Promise 是微任務(wù),setTimeout 是宏任務(wù),同一個(gè)事件循環(huán)中,promise.then總是先于 setTimeout 執(zhí)行。


30. 如何實(shí)現(xiàn) Promise.all ");

要實(shí)現(xiàn) Promise.all,首先我們需要知道 Promise.all 的功能:

    如果傳入的參數(shù)是一個(gè)空的可迭代對(duì)象,那么此promise對(duì)象回調(diào)完成(resolve),只有此情況,是同步執(zhí)行的,其它都是異步返回的。

    如果傳入的參數(shù)不包含任何 promise,則返回一個(gè)異步完成. promises 中所有的promise都“完成”時(shí)或參數(shù)中不包含 promise 時(shí)回調(diào)完成。

    如果參數(shù)中有一個(gè)promise失敗,那么Promise.all返回的promise對(duì)象失敗

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

Promise.all = function (promises) {
    return new Promise((resolve, reject) => {
        let index = 0;
        let result = [];
        if (promises.length === 0) {
            resolve(result);
        } else {
            function processValue(i, data) {
                result[i] = data;
                if (++index === promises.length) {
                    resolve(result);
                }
            }
            for (let i = 0; i < promises.length; i++) {
                //promises[i] 可能是普通值
                Promise.resolve(promises[i]).then((data) => {
                    processValue(i, data);
                }, (err) => {
                    reject(err);
                    return;
                });
            }
        }
    });
}

如果想了解更多Promise的源碼實(shí)現(xiàn),可以參考我的另一篇文章:Promise的源碼實(shí)現(xiàn)(完美符合Promise/A+規(guī)范)


31.如何實(shí)現(xiàn) Promise.finally ");

不管成功還是失敗,都會(huì)走到finally中,并且finally之后,還可以繼續(xù)then。并且會(huì)將值原封不動(dòng)的傳遞給后面的then.

Promise.prototype.finally = function (callback) {
    return this.then((value) => {
        return Promise.resolve(callback()).then(() => {
            return value;
        });
    }, (err) => {
        return Promise.resolve(callback()).then(() => {
            throw err;
        });
    });
}

32. 什么是函數(shù)柯里化?實(shí)現(xiàn) sum(1)(2)(3) 返回結(jié)果是1,2,3之和

函數(shù)柯里化是把接受多個(gè)參數(shù)的函數(shù)變換成接受一個(gè)單一參數(shù)(最初函數(shù)的第一個(gè)參數(shù))的函數(shù),并且返回接受余下的參數(shù)而且返回結(jié)果的新函數(shù)的技術(shù)。

function sum(a) {
    return function(b) {
        return function(c) {
            return a+b+c;
        }
    }
}
console.log(sum(1)(2)(3)); // 6

引申:實(shí)現(xiàn)一個(gè)curry函數(shù),將普通函數(shù)進(jìn)行柯里化:

function curry(fn, args = []) {
    return function(){
        let rest = [...args, ...arguments];
        if (rest.length < fn.length) {
            return curry.call(this,fn,rest);
        }else{
            return fn.apply(this,rest);
        }
    }
}
//test
function sum(a,b,c) {
    return a+b+c;
}
let sumFn = curry(sum);
console.log(sumFn(1)(2)(3)); //6
console.log(sumFn(1)(2, 3)); //6

如果您在面試中遇到了更多的原生JS問(wèn)題,或者有一些本文未涉及到且有一定難度的JS知識(shí),請(qǐng)給我留言。您的問(wèn)題將會(huì)出現(xiàn)在后續(xù)文章中~

本文的寫(xiě)成耗費(fèi)了非常多的時(shí)間,在這個(gè)過(guò)程中,我也學(xué)習(xí)到了很多知識(shí),謝謝各位小伙伴愿意花費(fèi)寶貴的時(shí)間閱讀本文,如果本文給了您一點(diǎn)幫助或者是啟發(fā),請(qǐng)不要吝嗇你的贊和Star,您的肯定是我前進(jìn)的最大動(dòng)力。github.com/YvetteLau/B…

 后續(xù)寫(xiě)作計(jì)劃

1.《寒冬求職季之你必須要懂的原生JS》(中)(下)

2.《寒冬求職季之你必須要知道的CSS》

3.《寒冬求職季之你必須要懂的前端安全》

4.《寒冬求職季之你必須要懂的一些瀏覽器知識(shí)》

5.《寒冬求職季之你必須要知道的性能優(yōu)化》

針對(duì)React技術(shù)棧:

1.《寒冬求職季之你必須要懂的React》系列

2.《寒冬求職季之你必須要懂的ReactNative》系列

參考文章:

    www.ibm.com/developerwo…

    juejin.im/post/5c7736…

    選用了面試之道上的部分面試題

    選用了木易楊說(shuō)文中提及的部分面試題: juejin.im/post/5bc92e…

    特別說(shuō)明: 0.1 + 0.2 !== 0.3 此題答案大量使用了此篇文章的圖文: juejin.im/post/5b90e0…

    選用了朋友面試大廠(chǎng)時(shí)遇到的一些面試題

    《你不知道的JavaSctipt》

    《JavaScript高級(jí)程序設(shè)計(jì)》

    github.com/hanzichi/un…

關(guān)注小姐姐的公眾號(hào),和小姐姐一起學(xué)前端。

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

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/7218.html

相關(guān)文章

  • 寫(xiě)給初入門(mén)/半路出家的前端er

    摘要:半路出家的前端程序員應(yīng)該不在少數(shù),我也是其中之一。年,馮馮同事兼師兄看我寫(xiě)太費(fèi)勁,跟我說(shuō)對(duì)面樓在找,問(wèn)我要不要學(xué),說(shuō)出來(lái)可能有點(diǎn)丟人,但是在那之前,我真得不知道什么是,什么是。 半路出家的前端程序員應(yīng)該不在少數(shù),我也是其中之一。 為何會(huì)走向前端 非計(jì)算機(jī)專(zhuān)業(yè)的我,畢業(yè)之后,就職于一家電力行業(yè)公司,做過(guò)設(shè)備調(diào)試、部門(mén)助理、測(cè)試,也寫(xiě)過(guò)一段時(shí)間的QT,那三年的時(shí)間,最難過(guò)的不是工作忙不忙,...

    Cc_2011 評(píng)論0 收藏0
  • 面試寒冬求職季之你必須要懂的原生JS(中)

    摘要:如果你還沒(méi)讀過(guò)上篇上篇和中篇并無(wú)依賴(lài)關(guān)系,您可以讀過(guò)本文之后再閱讀上篇,可戳面試篇寒冬求職季之你必須要懂的原生上小姐姐花了近百個(gè)小時(shí)才完成這篇文章,篇幅較長(zhǎng),希望大家閱讀時(shí)多花點(diǎn)耐心,力求真正的掌握相關(guān)知識(shí)點(diǎn)。 互聯(lián)網(wǎng)寒冬之際,各大公司都縮減了HC,甚至是采取了裁員措施,在這樣的大環(huán)境之下,想要獲得一份更好的工作,必然需要付出更多的努力。 一年前,也許你搞清楚閉包,this,原型鏈,就...

    Mike617 評(píng)論0 收藏0
  • 面試寒冬求職季之你必須要懂的原生JS()

    摘要:只能遍歷數(shù)組,不能中斷,返回值是修改后的數(shù)組。這在語(yǔ)法上,稱(chēng)為暫時(shí)性死區(qū)。作用域鏈無(wú)論是還是查詢(xún),都會(huì)在當(dāng)前的作用域開(kāi)始查找,如果沒(méi)有找到,就會(huì)向上級(jí)作用域繼續(xù)查找目標(biāo)標(biāo)識(shí)符,每次上升一個(gè)作用域,一直到全局作用域?yàn)橹埂? 互聯(lián)網(wǎng)寒冬之際,各大公司都縮減了HC,甚至是采取了裁員措施,在這樣的大環(huán)境之下,想要獲得一份更好的工作,必然需要付出更多的努力。 一年前,也許你搞清楚閉包,this,原...

    寵來(lái)也 評(píng)論0 收藏0
  • 前端開(kāi)發(fā)面試題鏈接

    摘要:手冊(cè)網(wǎng)超級(jí)有用的前端基礎(chǔ)技術(shù)面試問(wèn)題收集前端面試題目及答案匯總史上最全前端面試題含答案常見(jiàn)前端面試題及答案經(jīng)典面試題及答案精選總結(jié)前端面試過(guò)程中最容易出現(xiàn)的問(wèn)題前端面試題整理騰訊前端面試經(jīng)驗(yàn)前端基礎(chǔ)面試題部分最新前端面試題攻略前端面試前端入 手冊(cè)網(wǎng):http://www.shouce.ren/post/index 超級(jí)有用的前端基礎(chǔ)技術(shù)面試問(wèn)題收集:http://www.codec...

    h9911 評(píng)論0 收藏0
  • 前端開(kāi)發(fā)面試題鏈接

    摘要:手冊(cè)網(wǎng)超級(jí)有用的前端基礎(chǔ)技術(shù)面試問(wèn)題收集前端面試題目及答案匯總史上最全前端面試題含答案常見(jiàn)前端面試題及答案經(jīng)典面試題及答案精選總結(jié)前端面試過(guò)程中最容易出現(xiàn)的問(wèn)題前端面試題整理騰訊前端面試經(jīng)驗(yàn)前端基礎(chǔ)面試題部分最新前端面試題攻略前端面試前端入 手冊(cè)網(wǎng):http://www.shouce.ren/post/index 超級(jí)有用的前端基礎(chǔ)技術(shù)面試問(wèn)題收集:http://www.codec...

    snifes 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<