白话理解
- JavaScript基于原型链的继承,访问一个对象的属性时,如果找不到,则会顺着原型链向上层查找,以此类推,直到找不到为止
- 每个构造器都有一个
prototype
属性,代表该构造函数的原型对象
prototype
有个constructor
属性,指向它的构造函数,构造函数的constructor
指向自身
- 实例化对象有个
__proto__
,指向它的原型prototype
- 不管哪种继承方式,
prototype
上的属性都是共享的,所以原型链上最好是增加的是方法,基本属性放到构造函数中,除非你确定你的基本属性是需要共享所有实例对象的
继承的几种方式
构造函数实现继承
- 只能继承构造函数的属性,无法继承原型属性
- 每个新实例都有父类构造函数的副本
- 每个实例都是重新实例化构造函数,不存在共享属性
- 可以通过
Parent.call(this,params)
传递参数到父类构造函数
function Person() {
this.name = "live";
this.friends = ["hello"];
}
Person.prototype.sex = "男";
function Child() {
Person.call(this);
this.age = 12;
}
const child = new Child();
child.friends.push("world");
console.log(child.name, "child");
console.log(child.sex, "child");
console.log(child.friends, "child");
const child2 = new Child();
console.log(child2.friends, "child2");
原型链继承
- 所有实例共享父类实例属性,引用属性被修改时,所有都会被改
- 可以获得父构造函数以及原型属性
- 无法像父类传参
- 原型链继承时,当原型链属性修改,其他实例化的对象也会修改
function Person() {
this.name = "live";
this.friends = ["hello"];
}
Person.prototype.sex = "男";
function Child() {
this.age = 12;
}
Child.prototype = new Person();
const child = new Child()
child.friends.push("world");
console.log(child.name,"child");
console.log(child.sex,"child");
console.log(child.friends,"child");
Person.prototype.sex = "nv";
console.log(child.sex);
const child2 = new Child()
console.log(child2.friends,"child2");
组合继承
- 构造函数继承+
prototype
实例对象继承组合继承
- 构造函数继承可传参
- 实例对象原型链继承,保证属性是每个对象独有的
- 但是会造成两次调用父类构造函数
function Person(name) {
this.name = name;
this.colors = ["red"];
}
Person.prototype.sex = "男";
function Child(name) {
Person.call(this,name);
}
Child.prototype = new Person();
const child = new Child("live");
child.colors.push("blue");
console.log(child.name,"child");
console.log(child.colors,"child");
const child2 = new Child("lily");
child2.colors.push("yellow");
console.log(child2.name,"child2");
console.log(child2.colors,"child2");
寄生继承
- 可以将最开始的对象扩展后,返回被继承
- 通原型链继承一样,此时无法获取到构造函数属性
- 寄生继承直接指向父类的
prototype
,所以不会重复调用父类的情况
function Person() {
this.name = "live";
}
Person.prototype.sex = "nan";
Person.prototype.friends = ["hello"];
function Child() {
this.age = 12;
}
let fn = function() {};
fn.prototype = Person.prototype;
fn.prototype.sayName = function() {
return "name";
}
Child.prototype = new fn();
const child = new Child();
child.friends.push("world");
console.log(child.friends,"child");
console.log(child.name,"child");
console.log(child.age,"child");
console.log(child.sex,"child");
console.log(child.sayName());
const child2 = new Child();
console.log(child2.friends,"child2");
寄生组合继承
- 构造函数继承+寄生继承
- 构造函数继承调用父类一次,寄生继承不在调用父类,通过直接委托prototype,所以解决了组合继承两次调用父类的情况
function Person() {
this.name = "live";
}
Person.prototype.sex = "nan";
Person.prototype.friends = ["hello"];
function Child() {
this.age = 12;
}
let f = function () {};
f.prototype = Person.prototype;
f.prototype.sayName = function () {
return "name";
};
Child.prototype = new f();
function Person() {
this.name = "live";
}
Person.prototype.sex = "nan";
Person.prototype.friends = ["hello"];
function Child() {
Person.call(this);
this.age = 12;
}
let fn = function () {};
fn.prototype = Person.prototype;
fn.prototype.sayName = function () {
return "name";
};
Child.prototype = new fn();
const child = new Child();
child.friends.push("world");
console.log(child.friends, "child");
console.log(child.name, "child");
console.log(child.age, "child");
console.log(child.sex, "child");
console.log(child.sayName());
const child2 = new Child();
console.log(child2.friends, "child2");