你不可不知的JS面試題(第二期)

1、什么是繼承?

子類(lèi)可以使用父類(lèi)的所有功能,并且對(duì)功能進(jìn)行擴(kuò)展。

    新增方法
    改用方法

(1)、ES6使用extends子類(lèi)繼承父類(lèi)的方法。

    // 父類(lèi)
    class A{
        constructor(name){
            this.name= name;
        }
        getName () {
            return this.name;
        }
    };
    // 子類(lèi)繼承
    class B extends A {
        constructor(name){
           super(name) //  記得用super調(diào)用父類(lèi)的構(gòu)造方法!
        }
        getName(){
            const name = super.getName();
            return name;
        }
    }
    
    var b = new B('2');
    console.log(b.getName()); //2

 

(2)、ES5的繼承方法:

// 父類(lèi)
function P(name) {
    this.name = name;
}
// 父類(lèi)方法
P.prototype.get=function(){
    return this.name;
}
// 子類(lèi)
function C(name){
    P.call(this,name);
}
// 封裝繼承。也就是C.prototype.__proto__ = P.prototype
function I(Pfn,Cfn){
    var prototype = Object.create(Pfn.prototype);
    prototype.constructor = Cfn;
    Cfn.prototype = prototype;
}
// 調(diào)用繼承方法,并傳入?yún)?shù)
I(P,C);


var c = new C('maomin');
console.log(c.get()); // maomin

 

(3)、ES3實(shí)現(xiàn)繼承

使用ES3實(shí)現(xiàn)繼承無(wú)非是替代了Object.create(Pfn.prototype),我們先來(lái)看下


大家知道我們封裝的I方法是原理是C.prototype.__proto__ = P.prototype。但是我們不推薦這樣,因?yàn)開(kāi)_proto__是瀏覽器內(nèi)置的屬性,并不是JS內(nèi)置的,所以不推薦這樣做。我們來(lái)封裝一個(gè)方法來(lái)替代Object.create(Pfn.prototype)。

function objectCreate (o) {
    function P1() {}
    P1.prototype = o;
    return new P1();
}

 














完整代碼:

    // 父類(lèi)
    function P(name) {
        this.name = name;
    }
      // 父類(lèi)方法
    P.prototype.get = function () {
        return this.name;
    }
    // 子類(lèi)
    function C(name) {
        P.call(this, name);
    }
    // 封裝object.create()
    function objectCreate(o) {
        function P1() {}
        P1.prototype = o;
        return new P1();
    }
    // 封裝繼承
    //C.prototype.__proto__ = P.prototype;
    function I(Pfn, Cfn) {
        var prototype = objectCreate(Pfn.prototype);
        prototype.constructor = Cfn;
        Cfn.prototype = prototype;
    }
    
    // 調(diào)用繼承方法,并傳入?yún)?shù)
    I(P, C);
    
    var c = new C('maomin');
    console.log(c.get()); // maomin

 

(4)、新增API

新增ES6方法 Reflect.setPrototypeOf()可以實(shí)現(xiàn)C.prototype.__proto__ = P.prototype

   function A(name){this.name=name}
   A.prototype.get=function () {return this.name}
   function B (name) {A.call(this,name)}
   Reflect.setPrototypeOf(B.prototype,A.prototype);
   var b = new B('maomin');
   console.log(b.get()); //maomin

 

2、關(guān)于Promise,你知道什么?
(1)、Promise是什么?

Promise是異步編程的一種解決方案,同時(shí)他有很多規(guī)范,如Promise/A,Promise/B,Promise/D以及Promise/A的升級(jí)版Promise/A+,而ES6中采用了Promise/A+規(guī)范。
(2)、Promise的作用是什么?

    解決“回調(diào)地獄”問(wèn)題
    解決并發(fā)請(qǐng)求問(wèn)題
    解決異步編程代碼執(zhí)行順序理解困難的問(wèn)題

① 解決“回調(diào)地獄”問(wèn)題

我們先看下面代碼,看到會(huì)不會(huì)覺(jué)得太冗余了啊。如果代碼多的話(huà),很難維護(hù)。

    let count = 0;
    setTimeout(() => {
        count++;
        console.log(`地獄${count}層`);
        setTimeout(() => {
            count++;
            console.log(`地獄${count}層`);
            setTimeout(() => {
                 count++;
                 console.log(`地獄${count}層`);
            }, 500);
        }, 500);
    }, 500);

 

我們可以看到使用Promise讓它永遠(yuǎn)在第一層,打印出 我還在人間 ,而不會(huì)越來(lái)越深。

    let count = 0;
    new Promise(resolve =>{
        setTimeout(() => {
             count++;
             resolve();
        }, 500);
    }).then(()=>{
        return new Promise(resolve=>{
            setTimeout(() => {
                 count++;
                 resolve();
            }, 500);
        })
    }).then(()=>{
        console.log('我還在人間')
    })

 

② 解決并發(fā)請(qǐng)求問(wèn)題
可以在執(zhí)行result1 、result2 結(jié)束后再執(zhí)行下面的代碼

const result1 = fetch('/getName');
const result2 = fetch('/getAge');
Promise.all([result1,result2]).then(()=>{
// 執(zhí)行
})

 

③解決異步編程代碼執(zhí)行順序理解困難的問(wèn)題
我們先看下這個(gè)場(chǎng)景,get方法是異步的方法,在執(zhí)行g(shù)etInfo方法時(shí),并不會(huì)先執(zhí)行g(shù)et方法,而是先打印出我是getInfo方法。

function get() {
    setTimeout(() => {
        console.log('執(zhí)行g(shù)et方法');
    }, 1000);
}
function getInfo() {
    get();
    console.log('我是getInfo方法');
}
getInfo();
// 我是getInfo方法
// 執(zhí)行g(shù)et方法

 

那么,我們使用Promise來(lái)解決異步,同時(shí)我們使用了ES6async與await來(lái)等待get方法執(zhí)行完再執(zhí)行下面的代碼。

function get() {
    return new Promise((resolve)=>{
        setTimeout(() => {
            console.log('執(zhí)行g(shù)et方法');
            resolve();
        }, 1000);
    })
}
async function getInfo() {
    await get();
    console.log('我是getInfo方法');
}
getInfo();
// 執(zhí)行g(shù)et方法
// 我是getInfo方法

 

3、如何實(shí)現(xiàn)Promise?

我們先來(lái)了解Promise

    Promise包含then方法
    then方法的兩個(gè)參數(shù)resolve和reject
    Promise包含3個(gè)狀態(tài):pending(等待態(tài))、resolved(成功態(tài))、rejected(失敗態(tài))。

返回成功resolve:

new Promise((resolve,reject)=>{
    setTimeout(() => {
        resolve('success!') //返回成功狀態(tài)
    }, 1000);
}).then((v)=>{
    console.log(v);
},(e)=>{
    console.log(e);
})

 

返回錯(cuò)誤reject:

new Promise((resolve,reject)=>{
    setTimeout(() => {
        reject('error!') //返回失敗狀態(tài)
    }, 1000);
}).then((v)=>{
    console.log(v);
},(e)=>{
    console.log(e);
})

 

好了,我們來(lái)實(shí)現(xiàn)一下,封裝一個(gè)Promise。

 function myPromise(fn) {
        this.status = 'pending'; // 初始化等待狀態(tài)
        this.data = undefined; // 初始化一個(gè)存儲(chǔ)變量
        this.resolvedCallback = []; //成功方法保存
        this.rejectedCallback = []; // 失敗方法保存
        
        const resolve = (val) => {
            if (this.status === 'pending') {
                this.status = 'resolved';
                this.data = val;
                this.resolvedCallback.forEach(fu => fu.call(this));
            }
        }
        
        const reject = (val) => {
            if (this.status === 'pending') {
                this.status = 'rejected';
                this.data = val;
                this.rejectedCallback.forEach(fu => fu.call(this));
            }
        }
        
        fn(resolve, reject);
    }
    // 封裝then方法
    myPromise.prototype.then = function (onResolved, onRejected) {
        return new myPromise((resolve, reject) => {
        
            const resolvedCallback = () => {
                const result = onResolved(this.data);
                if (result instanceof myPromise) {
                    result.then(resolve, reject);
                } else {
                    resolve(result);
                }
            }
            const rejectedCallback = () => {
                const result = onRejected(this.data);
                if (result instanceof myPromise) {
                    result.then(resolve, reject);
                } else {
                    resolve(result);
                }
            }
            
            if (this.status === 'resolved') {
                resolvedCallback();
            } else if (this.status === 'rejected') {
                rejectedCallback();
            } else { // this.status === 'pending'
                this.resolvedCallback.push(resolvedCallback);
                this.rejectedCallback.push(rejectedCallback);
            }
            
        })

    }
    // 使用
    new myPromise((resolve, reject) => {
        setTimeout(() => {
            resolve('success!');
        }, 1000);
    }).then((v) => {
        console.log(v)
    }, (e) => {
        console.log(e)
    }).then(() => {
        console.log('1')
    })

 




作者:Vam的金豆之路

主要領(lǐng)域:前端開(kāi)發(fā)

我的微信:maomin9761

微信公眾號(hào):前端歷劫之路