1、区分类继承和实例化的差别

首先来看两个例子:
非常常用的js继承是这个样子的:

1
B.prototype = new A()

这时候特别容易和实例化给混淆了:

1
b = new A()

感觉特别的像有木有!这个时候开始好奇new到底干了什么(理解原理非常重要)

new一个对象的时候发生了什么

1
b = new A()
  • 创建空对象
  • 把this指向该空对象,同时还继承了该函数的原型,即临时对象的proto指向构造函数的prototype
  • 执行构造函数中的代码(为这个对象添加属性),也就是赋值。
  • 返回新对象
    1
    2
    3
    4
    5
    6
    7
    function New (A) { 
    var b = { '__proto__': A.prototype }; /*第一步*/
    return function () {
    A.apply(b, arguments); /*第二步*/
    return b; /*第三步*/
    };
    }

那么怎么区分什么时候是类继承,什么时候是实例化呢?
无论是实例化还是继承,本质上都是拥有A的属性,所以要像清楚js是怎么查找一个东西的属性:

当查找一个对象的属性时,JavaScript会向上遍历原型链,直到找到给定名称的属性为止。—–出自JavaScript秘密花园

用代码表示如下:

1
2
3
4
5
6
7
8
9
function getProperty(obj, prop) {
if (obj.hasOwnProperty(prop)){
return obj[prop]
}else if (obj.__proto__ !== null){
return getProperty(obj.__proto__, prop)
}else{
return undefined
}
}

嗯,这下可以解释两者区别了:
B.prototype = new A():B.prototype要找自己属性的时候,先看看自己有没有–>看看自己的proto(也就是A.prototype)有没有–>一路往上。
b = new A()b找属性的时候,先看看自己有没有–>看看自己的proto(也就是A.prototype)有没有–>一路往上。

2、实例化

众所周知,JavaScript里面所有的数据类型都是对象,我们需要一种机制,将所有的对象联系起来,因此,new命令引入了
JavaScript,用来从原型对象生成一个实例对象。在JavaScript语言中,new命令后面跟的不是类,而是构造函数。
具体来说
两个相关的概念:

  • 类:比如 人类
  • 实例:比如 王小二
    那么,王小二的父母孕育他到他直到出生的过程,就叫:实例化
    1
    2
    3
    4
    5
    function Human(name){
    this.name = name;
    }

    var wangxiaoer = new Human('王小二'); //这一步叫作 实例化

3、继承

众所周知,JavaScript的继承是实现继承,而没有java中的接口继承。这是因为JavaScript中函数没有签名,而实现继承依靠的是原型链来实现的。

原型继承到底继承了什么?

实际上就是继承了构造函数原型链两个东西。其中构造函数继承使用apply()实现的,这就意味着仅仅是把父类里面的属性复制了
一遍,对其进行任何的更改,都不会影响其他的实例,在继承之后对父类进行任何更改也不会影响其子类。而对于原型链的修改,
则是表示子类和父类公用原型链上的属性和方法,对于原型链的更改只能从上游源头进行修改,当然子类可以重写父类的方法,不过这样做实际上
就是先给子类增添一个重名的方法,而导致JS引擎先调用此方法而不去调用原型链的方法。

原型继承的方法

有四种方式可以实现构造函数的继承:

  • 1.调用apply或者call方法
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    function Animal({  
        this.species = '动物'  
    }  
    Animal.prototype.getName = function({  
        console.log('我是动物')  
    }  
      
    function Cat({  
        Animal.apply(thisarguments)  
    }  
    var cat = new Cat()  
    cat.species    // 动物  
    cat.getName()  // undefined

这种方法可以继承父类构造函数的属性,但是无法继承prototype属性,即父类中共享的方法和属性。

  • 2.改写prototype对象
    1
    2
    Cat.prototype = new Animal()  
    Cat.prototype.constructor = Cat

这是最常用的方法来模拟单继承,缺点是始终要保留Animal的对象,如果Animal对象比较大时,会消耗部分内存(其实很少),并且没有实现多继承。这种方法构造函数和prototype都会被继承。

  • 3.直接继承prototype
    1
    2
    Cat.prototype = Animal.prototype  
    Cat.prototype.constructor = Cat

缺点是当修改了Cat.prototype上的方法时会影响Animal.prototype,也无法访问到构造函数的属性。这种方法可以继承prototype属性,但是无法继承构造函数的属性

  • 4.利用空对象作中介
    1
    2
    3
    4
    var F = function(){}  
    F.prototype = Animal.prototype  
    Cat.prototype = new F()  
    Cat.prototype.constructor = Cat

缺点是无法继承父类封装的属性无法访问到构造函数里面的属性,但是可以继承prototype的属性
若要实现封装属性和共享同时继承到子类中,就需要同时结合上面的1和4,请使用jqury的extend方法或者其他深拷贝方法。

参考:
js继承与实例化-博客专用马甲
Javascript继承机制的设计思想-阮一峰
JavaScript 继承的那些事- Ahonn