关于原型与原型链

原型

在js中所有的函数都有一个特别的属性prototype : 显式原型属性

  • 它默认指向一个Object空对象
  • 原型对象中有一个对象constructor,它指向函数对象
  • 给原型对象添加属性(一般是方法),实例对象可以访问

而所有的实例对象都有一个特别的属性__proto__ : 隐式原型属性

  • 在实例对象中调用属性时,先在本身内部找,若没有,去自己的隐式原型对象(构造函数的显式原型)

原型链

显式原型与隐式原型的关系

  • 函数的prototype: 定义函数时被自动赋值, 值默认为{}, 即用为原型对象
  • 实例对象的__proto__: 在创建实例对象时被自动添加, 并赋值为构造函数的prototype值
  • 原型对象即为当前实例对象的父对象
  • 所有函数都是Function的实例(包括它本身)
  • Object的原型对象是原型链的尽头

原型链

  • 所有的实例对象都有__proto__属性, 它指向的就是原型对象
  • 这样通过__proto__属性就形成了一个链的结构—->原型链
  • 当查找对象内部的属性/方法时, js引擎自动沿着这个原型链查找
  • 当给对象属性赋值时不会使用原型链, 而只是在当前对象中进行操作

这样通过隐式原型的指向就形成了原型链,但是原型链也是有尽头的

当你使用构造函数创建了一个对象时,这个对象的proto属性指向的是构造函数的prototype,而由于prototype是一个空的Object对象,所以它的proto指向为Object.prototype。而Object是没有缔造者的,所以Object.prototype的proto为null。

即为:

obj --> func(构造函数).prototype --> Object.prototype --> null

所以原型链的尽头为null,归于虚无了属于是。

继承

通过原型链这一特性,我们就可以自己实现一个继承

  • 原型链继承 : 得到方法

    1
    2
    3
    4
    5
    6
    function Parent(){}
    Parent.prototype.test = function(){};
    function Child(){}
    Child.prototype = new Parent(); // 子类型的原型指向父类型实例
    Child.prototype.constructor = Child
    var child = new Child(); //有test()
    • 方法
      • 定义父类型的构造函数
      • 给父类型的原型添加方法
      • 定义子类型的构造函数
      • 创建父类型的对象赋值给子类型的原型
      • 将子类型原型的构造属性设置为子类型
      • 给子类型原型添加方法
      • 创建子类型对象:可以调用父类型方法
    • 关键
      • 子类型的原型为父类型的一个实例对象
    • 注意
      • 为子类型的原型添加constructor属性指向子类型构造函数
  • 借用构造函数 : 得到属性

    1
    2
    3
    4
    5
    6
    function Parent(xxx){this.xxx = xxx}
    Parent.prototype.test = function(){};
    function Child(xxx,yyy){
    Parent.call(this, xxx);//借用构造函数 this.Parent(xxx)
    }
    var child = new Child('a', 'b'); //child.xxx为'a', 但child没有test()
    • 方式
      • 定义父类型构造函数
      • 定义子类型构造函数
      • 在子类型构造函数中调用父类型构造函数
    • 关键
      • 在子类型构造函数中通用call()调用父类型构造函数
  • 组合

    1
    2
    3
    4
    5
    6
    7
    function Parent(xxx){this.xxx = xxx}
    Parent.prototype.test = function(){};
    function Child(xxx,yyy){
    Parent.call(this, xxx);//借用构造函数 this.Parent(xxx)
    }
    Child.prototype = new Parent(); //得到test()
    var child = new Child(); //child.xxx为'a', 也有test()
  • new一个对象背后做了些什么?

    • 创建一个空对象
    • 给对象设置__proto__, 值为构造函数对象的prototype属性值 this.proto = Fn.prototype
    • 执行构造函数体(给对象添加属性/方法)

最后通过原型链与借用构造函数的组合方式我们就可以得到一对带有继承关系的构造方法了。