JS继承
1. 原型鏈繼承
它是下面這個形式的。
function F() {
this.f_age = 8;
this.f_name = 'father';
}
F.prototype.getFAge = function() {
return this.f_age;
}
function S() {
this.s_age = 3;
}
S.prototype = new F();
S.prototype.getSAge = function() {
return this.s_age;
}
let s = new S();
console.log(s.getFAge()); // 8
console.log(s.getSAge()); // 3
console.log(s.constructor); // [Function: F]
console.log(s instanceof S); // true
console.log(s instanceof F); // true
它的主要過程:
實例化父類 F -> f
將子類的原型對象 S.prototype 指向 f
實例化子類 S -> s
加深理解:
s.__proto__ -> S.prototype -> f.__proto__ -> F.prototype
需要注意的是:
本來 S 的 constructor 是 S,但是因為 S.prototype 重寫了,所以現在 constructor 是 F.
查找機制: s -> S.prototype -> F實例 -> F.prototype
function F() {
this.name = 'F';
}
F.prototype.name = 'F-prototype';
function S() {}
S.prototype = new F();
let s = new S();
console.log(s.name); // F
原型繼承中 子類 重寫 父類的方法
function F() {
this.x = true;
}
F.prototype.getF = function() {
return this.x;
}
function S() {
this.y = false;
}
S.prototype = new F();
S.prototype.getS = function() {
return this.y;
}
// 重寫父類型中的 getF
S.prototype.getF = function() {
return '33';
}
let s = new S();
console.log(s.getS()); // false
console.log(s.getF()); // 33, 原來的 父類中 返回 true
delete(S.prototype.getF);
console.log(s.getF()); // true
如果你想要改變(覆蓋)父類的方法,必須在子類的 原型對象 被 父類的實例 賦值之后 覆蓋。后來居上,你懂的。
后來居上的 字面量方法 也一樣,如下
function F() {}
function S() {}
S.prototype = new F();
// 下面的這句話重寫了上面這句話。
S.prototype = {
getX: function() {
return '33';
}
}
原型鏈繼承的特點和示例代碼:
a. 原來的父類實例屬性 變為了 子類的原型屬性。共享性。(也可以是缺點)
function F() {
this.colors = ['red', 'blue'];
}
function S() {}
S.prototype = new F();
let s1 = new S();
let s2 = new S();
s1.colors.push('yellow');
console.log(s1.colors); // ['red', 'blue', 'yellow']
console.log(s2.colors); // ['red', 'blue', 'yellow']
b. 創建子類型的實例的時候,不能像超類型的構造函數傳遞參數。(是不足之處)
2. 借用構造函數 繼承
真是騷操作啊。
原理:
A.誰調用了函數,函數中的 this 就指向誰。
B.利用 apply 和 call 在 子類 內部調用 函數。
function F() {
this.colors = ['red', 'blue'];
}
function S() {
F.call(this);
}
let s1 = new S();
let s2 = new S();
s1.colors.push('yellow');
console.log(s1.colors); // ['red', 'blue', 'yellow']
console.log(s2.colors); // ['red', 'blue']
特點:
屬性不會共享。(上面說了)
可以傳參
function F(name) {
this.name = name;
this.colors = ['red', 'blue'];
}
function S(name) {
F.call(this, name);
}
let s1 = new S('ccc');
缺點在于函數復用。
其實和 JS構造模式 是一個道理。在原型鏈 中的 共享的函數還是很有必要的。一些屬性也是應該共享的。
3. 組合繼承: 跟JS模式中的 組合構造模式 很像。
可以傳參。
可以 選擇 是否共享屬性和方法。(非戰爭的年代,人們有權選擇過自己的生活)
function F(name) {
this.name = name;
this.colors = ['red'];
}
F.prototype.getF = function() {
console.log(this.name);
}
function S(name, age) {
F.call(this, name);
this.age = age;
}
S.prototype = new F();
S.prototype.constructor = S;
S.prototype.getS = function() {
console.log(this.age);
}
// 強行讓 constructor 為子類,缺點是 constructor 變為可枚舉。
let s1 = new S('s1', 18);
let s2 = new S('s2', 20);
s1.colors.push('yellow');
console.log(s1); // S {name: 's1', colors: ['red', 'yellow'], age: 18}
console.log(s2); // S {name: 's2', colors: ['red'], age: 20}
delete(s1.colors); // 刪掉了 子類實例中,借用父類構造函數繼承的 colors
console.log(s1.colors); // 子類實例中:父類原型中的 colors 還是存在的。
// ---------------------------------------------------------------
console.log(s1.constructor); // [Function: S]
console.log(Object.keys(S.prototype)); // 其中包含 constructor 屬性
4. 原型式繼承: 可以說是 ES3中對 ES5中 Object.create 的實現了。
缺點很明顯:共享屬性。
function Create(o) {
function F(){};
F.prototype = o;
return new F();
}
let person = {
name: '123',
colors: ['red']
}
let f1 = Create(person);
let f2 = Create(person);
f1.colors.push('yellow');
console.log(f2.colors); // ['red', 'yellow']
ES5中的 Object.create(用作新對象原型的對象,可選的定義額外屬性的對象)
若是沒有第二個參數,和我們上方自己寫的 Create 方法相同。
let person = {
name: '123',
colors: ['red']
}
let f1 = Object.create(person);
let f2 = Object.create(person);
f1.colors.push('yellow');
console.log(f2.colors); // ['red', 'yellow']
若是寫了第二個參數,則會覆蓋掉原型中的 屬性。
let person = {
name: '123',
colors: ['red']
}
let f1 = Object.create(person, {
colors: {
value: ['red']
}
});
let f2 = Object.create(person, {
colors: {
value: ['red']
}
});
f1.colors.push('yellow');
console.log(f2.colors); // ['red']
在沒有必要興師動眾地 創建構造函數,只是想讓 一個對象 與 另外一個對象保持 相似的情況下,使用 原型式繼承 就可以了。
5. 寄生組合式繼承。
產生的原因是組合繼承 的缺點:
調用兩次父類。
1.創建子類原型的時候
2.子類型構造函數內部的調用
最后造成的結果。子類會包含 父類的全部 實例屬性。并且在調用子類構造函數的時候會重寫(覆蓋)一些屬性。
function Create(o) {
function F() {};
F.prototype = o;
return new F();
}
// 1. 這一步為了 子類繼承 父類的原型
function Inherit(subType, superType) {
let prototype = Create(superType.prototype);
// 只承包了 父類的原型。
prototype.constructor = subType;
// 增強對象,重寫 被重寫的 constructor
subType.prototype = prototype;
}
function F(name) {
this.name = name;
this.colors = ['blue'];
}
F.prototype.say = function() {
console.log(this.name);
}
// 2. 這一步為了 子類繼承 父類的一些構造函數內部的東西
function S(name, age) {
F.call(this, name);
this.age = age;
}
Inherit(S, F);
疑問:其實本質上沒有啥差別。主要的騷操作就要在于:子類的原型繼承的地方改了下。
complete.
總結
- 上一篇: 巨鹿之战是哪两支军队之间的战斗?
- 下一篇: 小学生桌布名字绣到哪里