继承对象
主要实现原理:通过原型链继承~~~~
所以先来了解下原型链
Child.prototype.__proto__ === Parent.prototype;
Child.__proto__ === Parent;
- Parent是Function的实例。
Parent.__proto__ === Function.prototype;
Function.prototype.__proto__ === Object.prototype;
- 所有函数的默认原型都是Object的实例。
Parent.prototype.__proto__ === Object.prototype;
Object.prototype.__proto__ === null;
- 继承的原型链图
继承模式总结比较
继承模式 | 优点 | 缺点 | 可以用instance或者isPrototypeOf判断类型吗 |
---|---|---|---|
原型链基础 | Child.prototype = new Parent();重写了原型链继承了Parent的方法 | 1、Parent类型的实例变成了Child类型的原型对象,如果Parent有引用类型比如Array的属性会被篡改 2、创建Child类型的实例时,不能向Parent的构造函数传参 | 可以 |
借用构造函数(伪造对象或经典继承) | 解决了引用属性共享和不可传参 | 无法复用函数 | 可以 |
组合继承(伪经典继承) | 结合了原型链和构造模式,最常用 | 总是会调用两次超类型构造函数 | 可以 |
原型式继承 | 简单轻量,不复杂,适合简单需求 | 会共享引用类型的值,篡改 | 不可以 |
寄生式继承 | 替代不是自定义和构造函数的情况 | 不能函数复用 | 不可以 |
寄生组合式继承 | 最理想的继承范式 | 可以 |
0、类式继承的方案
class Parent {
constructor(name){
this.name = name;
}
static sayHello() {
console.log('hello');
}
sayName() {
console.log(`my name is ${this.name}`);
return this.name;
}
}
class Child extends Parent {
constructor (name, age) {
super(name);
this.age = age;
}
sayAge() {
console.log(`my age is ${this.age}`);
return this.age;
}
}
let parent = new Parent('Parent');
let child = new Child('Child', 18);
console.log(parent);
console.log(child);
console.log(child.__proto__ === Child.prototype);
console.log(Child.prototype.constructor === Child);
console.log(Child.prototype.__proto__ === Parent.prototype); // 继承的原理1
console.log(Parent.prototype.constructor === Parent);
console.log(Parent.prototype.__proto__ === Object.prototype);
console.log(Object.prototype.__proto__ === null);
console.log(Child.__proto__ === Parent); // 继承的原理2
console.log(Parent.__proto__ === Function.prototype);
console.log(Function.prototype.__proto__ === Object.prototype);
console.log(Object.prototype.__proto__ === null);
1、通过原型链实现继承---实现继承的主要方法。基本思想:让一个引用类型继承另一个引用类型的属性和方法。
-
实现的本质是重写原型对象,代之以一个新类型的实例。因为重写了原型对象,所以给原型添加方法一定要放在替换原型的后面,如果先添加再替换,新添加的都被替换走了,所以永远也不会生效了。
function Parent () { this.parentProperty = true; } Parent.prototype.getParentProperty = function () { console.log(`parent's ${this.parentProperty}`); } function Child() { this.childProperty = false; } Child.prototype = new Parent(); // 本质---重写原型对象 Child.prototype.getChildProperty = function () { console.log(`child's${this.childProperty}`); } const parentInstance = new Parent(); console.log(parentInstance.constructor); // Parent{} const childInstance = new Child(); console.log(childInstance.constructor); // Parent{} 导致childInstance的constructor指向Parent childInstance.getParentProperty(); // parent's true
- 确定原型和实例的关系
// 法一:instanceof
console.log(childInstance instanceof Child);
console.log(childInstance instanceof Parent);
console.log(childInstance instanceof Object);
// 法二:isPrototypeOf
console.log(Child.prototype.isPrototypeOf(childInstance));
console.log(Parent.prototype.isPrototypeOf(childInstance));
console.log(Object.prototype.isPrototypeOf(childInstance));
-
不能使用对象字面量创建原型方法。
// yes Child.prototype = new Parent(); // no。这样做新建了一个Object实例,而非Parent的实例,切断了与Parent的联系。 Child.prototype = { };
2、借用构造函数:在Child类型构造函数的内部调用Parent类型构造函数。
-
函数是特定环境中执行代码的对象,可以通过call和apply在新创建的对象上执行构造函数。
function Parent (name) { this.colors = ['red', 'green']; this.name = name; this.age = 99; } function Child(name){ // 继承了Parent Parent.call(this, name); // 添加Child的属性,一定要在调用Parent的后面,不然会被Parent构造函数重写属性(如果正好存在) this.age = 29; } const childInstance1 = new Child('tom'); const childInstance2 = new Child('jack'); childInstance1.colors.push('white'); console.log(childInstance1.colors); // ['red', 'green', 'white'] console.log(childInstance2.colors); // ['red', 'green'] console.log(childInstance1.name); // tom console.log(childInstance2.name); // jack console.log(childInstance1.age); // 29 console.log(childInstance2.age); // 29 console.log(childInstance2 instanceof Child); // true console.log(childInstance2 instanceof Parent); // false console.log(childInstance2 instanceof Object); // true
3、组合继承:组合使用原型链和构造函数模式
-
使用原型链实现对原型属性和方法的继承(复用);使用构造函数实现对实例属性的继承(副本)
function Parent (name) { this.colors = ['red', 'green']; this.name = name; } Parent.prototype.getParentName = function () { console.log(this.name); } function Child(name, age){ // 构造函数继承属性 Parent.call(this, name); this.age = age; } // 使用原型链继承方法 Child.prototype = new Parent(); Child.prototype.constructor = Child; Child.prototype.getChildAge = function () { console.log(this.age); } const childInstance1 = new Child('tom', 29); const childInstance2 = new Child('jack', 18); childInstance1.colors.push('white'); console.log(childInstance1.colors); // ['red', 'green', 'white'] console.log(childInstance2.colors); // ['red', 'green'] childInstance1.getParentName(); // 'tom' childInstance2.getParentName(); // 'jack' childInstance1.getChildAge(); // 29 childInstance2.getChildAge(); // 18 console.log(childInstance2 instanceof Child); // true console.log(childInstance2 instanceof Parent); // true console.log(childInstance2 instanceof Object); // true
4、原型式继承
-
借助原型基于已有的对象创建新对象,同时还不必因此创建自定义类型
// object对传入其中的对象进行了一次浅复制 function object (o) { function F() {} F.prototype = o; return new F(); } const person = { name: 'tom', friends: ['a', 'b', 'c'] }; const anotherPerson = object(person); anotherPerson.name = 'huahua'; anotherPerson.friends.push('d'); const onePerson = object(person); console.log(person.friends); // ['a', 'b', 'c', 'd'] console.log(anotherPerson.friends); // ['a', 'b', 'c', 'd'] console.log(onePerson.friends); // ['a', 'b', 'c', 'd']
- ECMAScript5新增了Object.create方法,规范了原型式继承。
5、寄生式继承:创建一个仅用于封装继承过程的函数,在内部增强对象。
function createAnother(original) {
const clone = object(original);
clone.sayHi = function () {
console.log('hi');
}
return clone;
}
const person = {
name: 'tom',
friends: ['a', 'b', 'c']
};
const anotherPerson = createAnother(person);
anotherPerson.sayHi();
anotherPerson.friends.push('e');
console.log(anotherPerson.friends); // ['a', 'b', 'c', 'e']
console.log(person.friends); // ['a', 'b', 'c', 'e']
6、寄生组合式继承
- 不必为了指定Child类型的原型而调用Parent类型的构造函数,需要的只是Parent原型的一个副本。
// object对传入其中的对象进行了一次浅复制
function object (o) {
function F() {}
F.prototype = o;
return new F();
}
function inheritPrototype (Sub, Super) {
const prototype = object(Super.prototype); // 创建对象
prototype.constructor = Sub; // 增强对象
Sub.prototype = prototype; // 指定对象
}
function Parent (name) {
this.name = name;
this.colors= ['a', 'b', 'c'];
}
Parent.prototype.getParentName = function () {
console.log(this.name);
}
function Child (name, age) {
// 继承属性
Parent.call(this, name);
this.age = age;
}
// 继承方法
inheritPrototype(Child, Parent);
Child.prototype.getChildAge = function () {
console.log(this.age);
}
const childInstance1 = new Child('tom', 29);
const childInstance2 = new Child('jack', 18);
childInstance1.colors.push('white');
console.log(childInstance1.colors); // ["a", "b", "c", "white"]
console.log(childInstance2.colors); // ["a", "b", "c"]
childInstance1.getParentName(); // 'tom'
childInstance2.getParentName(); // 'jack'
childInstance1.getChildAge(); // 29
childInstance2.getChildAge(); // 18
console.log(childInstance2 instanceof Child); // true
console.log(childInstance2 instanceof Parent); // true
console.log(childInstance2 instanceof Object); // true
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。