原型链继承
将父类的实例作为子类的原型
function Parent() {
this.name = ['koa', 'express'];
}
Parent.prototype.getName = function() {
console.log(this.name);
};
function Child() {}
Child.prototype = new Parent();
var child1 = new Child();
child1.name.push('egg');
child1.getName();
var child2 = new Child();
child2.getName();
// 输出
// ["koa", "express", "egg"]
// ["koa", "express", "egg"]
- 父类方法可以复用
- 父类的引用属性会被所有子类共享
- 子类创建实例不能向父类传递参数
构造函数继承
将父类构造函数内容复制给子类构造函数
function Parent(name) {
this.name = ['koa'];
if (name) {
this.name.push(name);
}
this.getName = function() {
console.log(this.name);
};
}
function Child(name) {
Parent.call(this, name);
}
var child1 = new Child();
child1.name.push('express');
child1.getName();
var child2 = new Child();
child2.getName();
var child3 = new Child('egg');
child3.getName();
// 输出
// ["koa", "express"]
// ["koa"]
// ["koa", "egg"]
- 可以在 Child 中向 Parent 传参
- 类的引用属性不会被共享
- 都在构造函数中定义,每次创建实例都会初始化方法或属性
- 父类方法不能复用
组合继承
构造函数继承与原型链继承结合
function Parent(name) {
this.name = name;
this.age = [18, 16, 23];
}
Parent.prototype.getData = function() {
console.log(this.name, this.age, this.sex);
};
function Child(name, sex) {
Parent.call(this, name);
this.sex = sex;
}
Child.prototype = new Parent();
Child.prototype.constructor = Child;
var child = new Child('koa', 1);
child.age.push(26);
child.getData();
var child2 = new Child('express', 0);
child2.getData();
// 输出
// koa [18, 16, 23, 26] 1
// express [18, 16, 23] 0
- 父类的方法可以复用
- 可以在 Child 中向 Parent 传参
- 类的引用属性不会被共享
- 调用了两次父类的构造函数,第一次是 new Parent,第二次是 call 的时候,会覆盖子类的同名参数
原型继承
对参数对象的一种浅复制
var objCopy = {};
objCopy.create = function(o) {
function C() {}
C.prototype = o;
return new C();
};
var person = {
age: 18,
sex: [1],
getData: function() {
console.log(this.age, this.sex);
}
};
var person1 = objCopy.create(person);
var person2 = objCopy.create(person);
person1.age = 24;
person1.sex.push(0);
person1.getData();
person2.getData();
// 输出
// 24 [1,0]
// 24 [1,0]
- 父类方法可复用
- 父类的引用属性会被所有子类共享
- 子类创建实例不能向父类传递参数
// Sub是子类,Sup是超类
function _extend(Sub, Sup) {
// 定义一个空函数
var F = function() {};
// 空函数原型为超类原型
F.prototype = Sup.prototype;
// 实例化空函数,把超类原型引用传递给子类原型
Sub.prototype = new F();
// 重置子类原型构造函数为子类本身
Sub.prototype.constructor = Sub;
// 在子类保存超类原型,避免子类与超类耦合
Sub.sup = Sup.prototype;
// 检测超类原型的构造函数是否为原型本身,如果是原型本身,重置超类原型构造函数为超类本身
if (Sup.prototype.constructor === Object.prototype.constructor) {
Sup.prototype.constructor = Sup;
}
}
寄生继承
使用原型式继承一个目标对象的浅复制,增强这个浅复制的能力
var objCopy = {};
objCopy.create = function(o) {
function C() {}
C.prototype = o;
return new C();
};
function clone(o) {
var clone = Object.create(o);
clone.getData = function() {
console.log(this.age);
};
return clone;
}
var person = {
age: 16
};
var child1 = clone(person);
child1.getData();
// 输出
// 16
寄生组合继承
创建子类实例,然后对其增强
var objCopy = {};
objCopy.create = function(o) {
function C() {}
C.prototype = o;
return new C();
};
function inherit(child, parent) {
// 创建了父类原型的浅复制
var prototype = Object.create(parent.prototype);
// 修正了原型的构造函数
prototype.constructor = child;
// 将子类原型替换为prototype
child.prototype = prototype;
}
function Parent(name) {
this.name = name;
}
Parent.prototype.getName = function() {
console.log(this.name);
};
function Child(name, age) {
Parent.call(this, name);
this.age = age;
}
inherit(Child, Parent);
Child.prototype.getAge = function() {
console.log(this.age);
};
var child = new Child('he', 23);
child.getName();
child.getAge();
// 输出
// he
// 23
- 对父类原型的复制,不包括父类的构造函数,没有两次调用父类的构造函数
- 父类的方法可以复用
- 可以在 Child 中向 Parent 传参
- 类的引用属性不会被共享
ES6 class extends
先把父类实例的属性和方法加到 this 上,然后再用子类的构造函数修改 this,如果要继承父类的属性和方法,就要在子类 constructor()中调用 super()
new 操作符做了什么
class A {
constructor() {
this.data = 'A';
}
getData() {
console.log(this.data);
}
}
class B extends A {
constructor() {
super();
this.data = 'B';
}
}
var f = new A();
var c = new B();
extend 的实现
__proto__是什么
class A {}
class B {}
// 设置一个指定对象的原型到另一个对象
Object.setPrototypeOf = function(child, parent) {
child.__proto__ = parent;
return child;
};
// B的实例继承A的实例
Object.setPrototypeOf(B.prototype, A.prototype);
// B继承A的静态属性
Object.setPrototypeOf(B, A);
class 做了什么
function Point(x, y) {
this.x = x;
this.y = y;
}
Point.prototype.toString = function() {};
var p = new Point(1, 2);
// 等价于===>
class Point {
constuctor(x, y) {
this.x = x;
this.y = y;
}
toString() {}
}
ES6 的 class 继承与 ES5 的 prototype 的区别
- extends 是 ES5 继承的语法糖,不是新方法
- ES6 的继承是基于父类实例的
- ES5 是使用构造函数赋值,没有__proto__指向,ES6 的子类构造函数的原型的__proto__是指向父类的构造函数的
参考
js 继承、构造函数继承、原型链继承、组合继承、组合继承优化、寄生组合继承
JavaScript 高级程序设计
原型污染:最新:Lodash 严重安全漏洞背后你不得不知道的 JavaScript 知识
确认评论邮箱
邀请制评论