js笔记第三记:继承

129 阅读3分钟

看了<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)

父类和子类都有colors属性,但是由于原型链的原因,我们在自己的对象中找到了属性就会停止查找。但是也存在一些问题。比如说

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…

欢迎提出指正与建议