手寫instanceof (詳解原型鏈) 和 實現綁定解綁和派發的事件類

A  instanceof  B    是判斷  A  是否繼承自B,是返回true,  否返回false數組

再精確點就是判斷B   是否  再  A  的 原型鏈上,瀏覽器

什麼是原型鏈,舉個例子:函數

咱們定義三個對象:測試

const grandFather = {
    name: 'liu',
    age: 80
},father = {
    age: 50
},son = {
    age: 18
};

怎麼讓這三個對象造成一種繼承關係呢 ?  讓father 和 son 繼承 grandFather  的   name  屬性this

咱們知道對象上有一個內部屬性__proto__   ,   該屬性的屬性值指向改對象的原型,spa

咱們是否能夠這樣寫:prototype

const grandFather = {
    name: 'liu',
    age: 80
},father = {
    age: 50,
    __proto__: grandFather
},son = {
    age: 18,
    __proto__: father
};
console.log(father.name);
console.log(son.name);

ok  ,打印出:3d

原理就是若是對象自己沒有查找的屬性, 就會沿着原型鏈也就是__proto__屬性往上找直到爲null爲止。code

這就是原型鏈的概念。對象

可是__proto__先後加雙下劃線說明它本質是一個內部屬性, 而不是一個正式的對外的API,只是因爲瀏覽器普遍支持,才被加入了ES6, 

因此只有瀏覽器必須部署這個屬性,其餘運行環境不必定要部署,所以,不管從語義的角度,仍是從兼容性的角度,都最好不要使用這個屬性,

而是使用Object.setPrototyleOf(寫操做), Object.getPrototyleOf(讀操做), Object.create(生成操做), 

因此咱們改爲這樣:

const grandFather = {
    name: 'liu',
    age: 80
},father = {
    age: 50,
},son = {
    age: 18,
};
Object.setPrototypeOf(father, grandFather);
Object.setPrototypeOf(son, father);
console.log(father.name);
console.log(son.name);

打印結果是同樣的。

 又或者這樣:

const grandFather = {
        name: 'liu',
        age: 80
    },
    father = Object.create(grandFather),
    son = Object.create(father);
father.age = 50;
son.age = 18;
console.log(father.name);
console.log(son.name);

打印結果也是同樣的。

原型鏈你們弄清楚了 咱們就能夠寫一個instanceof 的 方法了:

function instanceofMy (A, B) {
    const proto = Object.getPrototypeOf(A), prototype = B.prototype;
    if (proto === null || proto === undefined) {
        return false;
    } else if (proto === prototype) {
        return true;
    } else {
        return instanceofMy(Object.getPrototypeOf(proto), B);
    }
}
// 測試
console.log(instanceofMy({}, Object));
console.log(instanceofMy([], Array));
function Test() {}
let test = new Test();
console.log(instanceofMy(test, Test));
console.log(instanceofMy('', Array));

測試結果:

利用遞歸來沿着原型鏈往上查找, 有同窗不想用遞歸,太耗費內存了,咱們能夠改爲while循環:

function instanceofMy (A, B) {
    let proto = Object.getPrototypeOf(A), prototype = B.prototype;
    while (proto !== null && proto !== undefined) {
        if (proto === prototype) {
            return true;
        } else {
            proto = Object.getPrototypeOf(proto);
        }
    }
    return false;
}

proto = null 做爲while循環的出口, 出來了就return false。

 

---------------------------------------------------------------------------分割線

 

實現綁定/派發自定義事件的事件類

有時候咱們須要自定義一個事件並在特定的條件下手動觸發該事件綁定的函數,

這個時候咱們就須要這樣一個事件類:

class Event {
    constructor() {
        // 存事件和回調函數的對象
        this.cache  = {};
    }
    // 綁定事件
    on (type, callback) {
        if (this.cache[type] && !this.cache[type].includes(callback)) {
            this.cache[type].push(callback);
        } else if (this.cache[type] && this.cache[type].includes(callback)) {
            return this;
        } else {
            this.cache[type] = [callback];
        }
        return this;
    }

    // 觸發事件
    trigger(type, ...params) {
        if (this.cache[type]) {
            this.cache[type].forEach(fn => {
                fn(...params);
            })
        }
        return this;
    }

    // 解綁事件
    off(type, callback) {
        // 傳了callback清除指定callback, 沒傳清空數組
        if (this.cache[type] && this.cache[type].includes(callback)) {
            this.cache[type].split(this.cache[type].findIndex(callback), 1);
        } else if (this.cache[type] && !callback) {
            this.cache[type] = [];
        }
        return this;
    }
}
const event = new Event();

// 測試
function start(str) {
    console.log(str + ' start');
}
function end(str) {
    console.log(str + ' end');
}
event.on('start', start)
    .on('start', end)
    .trigger('start', 'Hello world')
    .off('start')
    ;

咱們先給一個自定start事件綁定兩個函數,而後傳參觸發, 最後解綁

打印結果:

這樣這個事件類就寫完了

相關文章
相關標籤/搜索