看了<JavaScript高级程序设计>, 也看了一些大佬的博客,最后才发现,还是得运行出你自己关于继承的一些想法,验证一下对不对,这样才能更好的理解继承。 具体方法就是:在别人的代码上修修改改,运行一下。越运行,越清晰。
1,首先,你得明确继承的是什么,继承是怎么继承的。
继承什么: 我们把公有的方法和属性写在原型中,把私有属性写在构造函数里。所以,我们最好的继承就是继承原型对象中的方法和属性。
怎么继承:方法很多,但是最后一定是为了实现
child.prototype包含parent.prototype
2,然后,继承方式大概有这么几种。
- 2.1 原型继承 : 子类的原型是父类的实例对象
function Parent () {
this.names = ['kevin', 'daisy'];
}
Parent.prototype.getsomething = function(){
console.log('parent')
}
function Child () {}
// 继承
Child.prototype = new Parent();
// child1和child2访问的都是父类的names,他们自己是没有names的,共享同一个父类的names(注意,因为names数组是引用类型,才能改变)
var child1 = new Child();
var child2 = new Child();
child1.names.push('yayu');
console.log(child2.names); // 'kevin', 'daisy', 'yayu'
console.log(child1.names === child2.names) //true
// 如果你知道new操作做了什么,你就知道instance作为Parent的实例,自己拥有一个names的副本。
var instance = new Parent()
console.log(instance.names)//'kevin', 'daisy'
我们继承一定是不希望父类的构造函数的私有属性被子类改变,我们希望每个实例都有属于自己的names副本,同时也让子类的原型连接到父类的原型。所以,改进版本看下面
- 2.2,组合继承:在原型继承的基础上增加了借用父类构造函数的功能
function Parent (name) {
this.name = name;
this.colors = ['red', 'blue', 'green'];
}
Parent.prototype.getName = function () {
console.log(this.name)
}
function Child (name, age) {
// 借用了父类的构造函数,为每个由Child创建的实例对象提供父类构造函数的属性
Parent.call(this, name);
this.age = age;
}
// 采用原型继承
Child.prototype = new Parent();
var child1 = new Child('kevin', '18');
// 大家改的都是自己的属性
child1.colors.push('black');
console.log(child1.name); // kevin
console.log(child1.age); // 18
console.log(child1.colors); // ["red", "blue", "green", "black"]
var child2 = new Child('daisy', '20');
console.log(child2.name); // daisy
console.log(child2.age); // 20
console.log(child2.colors); // ["red", "blue", "green"]
// 看看原型链
console.log(child1)

Child.prototype.constructor = Child
alert(Child.constructor === Parent.constructor) //true
我在第二种继承方式上加上两句,改变了子类的构造器,你会发现父类的构造器也跟着改变。还有在上图中可以看到,在子类里面有color属性,没必要去继承父类的,这有些浪费空间 由于有了这两个问题,所以有了下面的方式
- 2.3 寄生组合继承:主要理解空对象的作用
function inheritPrototype (Parent, Child) {
var F = function () {};
F.prototype = Parent.prototype;
Child.prototype = new F();
// 为子类原型增加属性,
Child.prototype.constructor = Child;
}
function Parent (name) {
this.name = name;
this.colors = ['red', 'blue', 'green'];
}
Parent.prototype.getName = function () {
console.log(this.name)
}
function Child (name, age) {
Parent.call(this, name);
this.age = age;
}
// 实现继承
inheritPrototype(Parent, Child)
// 使用F.prototype = Parent.prototype继承的好处 不用继承父类和子类一样的属性(colors),依然可以让子类有自已的colors属性
var child1 = new Child('kevin', '18');
var child2 = new Child('liyi', 22)
child2.colors.push('black')
console.log(child1.colors) // 'red', 'blue', 'green'
console.log(child2.colors) // 'red', 'blue', 'green', 'black'
// 空对象的作用 改变子类的原型,并不同样改变父类原型
console.log(Child.prototype.constructor === Parent) // false
使用空对象作为媒介继承,对于子类来说,相当于在继承来的父类原型上包了一层原型,所以,可以直接在子类的原型上增加属性或者方法的话,只在最上层原型增加方法或者属性,并不改变父类。有图有真相

参考来源:
《JavaScript高级程序设计》
juejin.cn/post/684490…