動態

詳情 返回 返回

繼承 - 動態 詳情

繼承

原型鏈繼承

  • 實現方式如下
function Parent() {
    this.name = '微言';
}
Parent.prototype.getName = function () {
    console.log(this.name);
};
function Child() {}
// new Parent產生的一個parent 的實例,同時包含了,實例屬性以及原型方法。
Child.prototype = new Parent();
console.log(Child.prototype.constructor); // [Function: Parent]
// <1>
Child.prototype.constructor = Child;
console.log(Child.prototype.constructor); // [Function: Child]

const child = new Child();
child.getName();
  • 為什麼需要<1>?

    存在的問題,直接把 Child 的原型對象給覆蓋了,
    此處指向了 parent 通過 修改 constructor 來實現 指向 Child

缺點

  1. 如果有屬性是引用類型的一旦某個實例修改了屬性,所有的實例都會改變
  2. 無法傳遞參數
function Parent() {
    this.name = '微言';
    this.obj = { name: 'obj' };
}

Parent.prototype.getName = function () {
    console.log(this.name);
};

function Child() {}
Child.prototype = new Parent();
Child.prototype.constructor = Child;

const child = new Child();
const child1 = new Child();

child1.obj.name = 'child1';

// 因為都是找的原型對象上的同一個東西,所以都會受到影響
console.log(child.obj); // { name: 'child1' }
console.log(child1.obj); // { name: 'child1' }
  1. 創建 Child 實例的時候不可以進行傳參

構造函數繼承

缺點

  1. 屬性或者方法如果想被繼承,只能在構造函數中定義.

如果方法在構造函數中定義了。每次創建實例都會創建一遍方法,多佔用一塊內存。

function Parent(name, obj) {
    this.name = name;
    this.obj = obj;
    this.eat = function () {};
}

function Child(id, name, obj) {
    Parent.call(this, name, obj);
    this.id = id;
}

const child = new Child(1, 'c1', { name: '微言', age: '18' });
const child1 = new Child(2, 'c2', { name: '微言' });

console.log(child.eat === child1.eat); // false 造成了內存泄露,內存浪費

組合繼承

組合了構造函數繼承和原型鏈繼承。

原型鏈繼承:方法存在 prototype,子類可以隨時調用,引用類型的屬性會被放在所有實例上共享,並且不可以傳參。

構造函數繼承: 使用 call 在子構造中重複一遍屬性和方法的操作,可以傳參了。

function Parent(name, obj, actions) {
    this.name = name;
    this.obj = obj;
    this.actions = actions;
}

Parent.prototype.eat = function () {
    console.log(`${this.name} - eat`);
};

// 採用解構的方式,不用每次都增刪值的傳入
function Child(id, ...args) {
    // 為了實例屬性的不共享
    Parent.apply(this, args); // 1
    this.id = id;
}

Child.prototype = new Parent(); // 2
Child.prototype.constructor = Child;

const child = new Child(1, 'c1', { name: '微言', age: '18' }, [1]);
const child1 = new Child(2, 'c2', { name: '微言1' }, [2, 43]);

console.log(child.eat === child1.eat); // true

child.obj.name = 'asdfasdf';

child.actions.pop();

console.log(child.obj); // { name: 'asdfasdf', age: '18' }
console.log(child1.obj); // { name: '微言1' }

console.log(child.actions); // []
console.log(child1.actions); // [2, 43]

缺點

  1. parent 構造函數被調用了兩次

寄生組合式繼承

function Parent(name, obj, actions) {
    this.name = name;
    this.obj = obj;
    this.actions = actions;
}

Parent.prototype.eat = function () {
    console.log(`${this.name} - eat`);
};

// 採用解構的方式,不用每次都增刪值的傳入
function Child(id, ...args) {
    // 為了實例屬性的不共享
    Parent.apply(this, args);
    this.id = id;
}

// Child.prototype = new Parent();

// 第一個方法. temp構造函數
// let TempFunction = function () {};
// // 通過曲線救國,讓臨時構造函數和parent構造函數都一樣
// TempFunction.prototype = Parent.prototype;  // 這裏只是繼承了原型對象, parent裏面整個構造函數的創建並不會執行, (其實並沒有parent裏面的執行)
// // 讓臨時構造函數的實例指向Child.prototype
// Child.prototype = new TempFunction();

// 第二個方法. Object.create
Child.prototype = Object.create(Parent.prototype);

// 為什麼不能直接這樣寫? 修改 子類 會影響到父類
// Child.prototype = Parent.prototype;

Child.prototype.constructor = Child;

const child = new Child(1, 'c1', { name: '微言', age: '18' }, [1]);
const child1 = new Child(2, 'c2', { name: '微言1' }, [2, 43]);

console.log(child.eat === child1.eat); // true

child.obj.name = '新的名字';

child.actions.pop();

console.log(child.obj); // { name: '新的名字', age: '18' }
console.log(child1.obj); // { name: '微言1' }

console.log(child.actions); // []
console.log(child1.actions); // [2, 43]

總結

本文涉及到的知識點,原型,原型鏈,Object
user avatar Leesz 頭像 alibabawenyujishu 頭像 haoqidewukong 頭像 freeman_tian 頭像 qingzhan 頭像 razyliang 頭像 leexiaohui1997 頭像 huajianketang 頭像 inslog 頭像 banana_god 頭像 huichangkudelingdai 頭像 Dream-new 頭像
點贊 109 用戶, 點贊了這篇動態!
點贊

Add a new 評論

Some HTML is okay.