原型总结

扫码查看

1. 构造函数

构造函数实际上就是一个函数,但是为了区分普通函数和构造函数,常常将构造函数的首字母大写。

构造函数和普通函数的区别在于:直接调用的函数是普通函数,使用new生成实例的函数是构造函数。

每个构造函数都有一个prototype属性,

实例对象可通过它的constructor访问到它的构造函数:obj.constructor == Con.prototype.constructor == Con

2. 原型

每个对象都有一个原型对象,对象以原型对象为模板,从原型继承属性和方法。

每个实例都有一个__proto__属性,实例对象可以通过__proto__访问到它的原型对象;每个构造函数都有一个prototype属性p.__proto__和Parent.prototype都指向同一个对象:原型Parent.prototype。Parent和Parent.prototype之间存在一个循环引用。三者关系如下图所示:

需要说明的是:__proto__和prototype不是一回事,对象上并没有prototype属性! 

3. 原型链

每个对象都有一个原型对象,通过__proto__属性指针指向上一个原型,继承原型的属性和方法,同时该原型也有它的原型对象,这样一层一层最终指向null。这个关系被称作原型链

因此当我们找一个对象的属性和方法时不仅仅会在该对象上找,还会去找它的原型对象上的属性和方法,一直向上找直到找到名称匹配的属性和方法或者是直到null。如下图所示:

 那么需要明确一个问题:原型链的实现是基于__proto__的!从下图可以看出原型的prototype是指向原型链中的某一处的。

所以这里随之而来带来一个问题:如果动态的设置函数的prototype是会影响后续的原型链指向的。看下例:

        function A() {
            return 'a'
        }
        A.prototype.method = function () {
            return 'method A'
        }

        function B() {
            return 'b'
        }
        B.prototype.method = function () {
            return 'method B'
        }

        console.dir(A.prototype)  // Object
        A.prototype = B;  // 将A.prototype指向B,而B是个函数
        var obj = new A();
        console.dir(A.prototype)   // f B()
        obj.method()  // Uncaught TypeError: obj.method is not a function

 console.dir输出结果如下:

可以看到,如果直接输出A.prototype,则得到Object;但是当执行A.prototype = B 之后,A.__proto__是指向一个匿名函数f(),A.__proto__.__proto__才指向Object。A.prototype = B这个操作实际上是改变了A的原型链指向并不会执行B的原型!!!所以不要这么写!!!

那如果我们想让A继承B该怎么写呢?换句话说,那当实现原型链继承的时候应该如何写呢?

4. 原型链继承

原型链继承的本质:重写原对象,代之以一个新的实例。也就是说,原型实际上会变成另一个构造函数的一个实例~

以上题为例,我们需要将上例中的A.prototype = B指向B的一个实例,即A.prototype = new B(),这样就可以实现原型链继承例,A也能够继承B原型上的所有属性和方法。上例的输出结果如下:

这时,执行B.method()也可以得到结果了。

因此

原型链继承的问题:

  • 当原型属性上有引用类型的值被实例操作时,这个操作会影响到多个实例,因为原型上的属性会被所有的实例共享!
  • 从上一个截图可以看到A.prototype = new B()执行后,A.prototype上丢失了constructor属性,解决办法:自己重写constructor属性:A.prototype.constructor = B,将其指向B!

        

  • 如果要使用原型链继承,并且想要给子类型添加新的方法或属性时,必须将添加操作放在原型替换操作之后,即在A.prototype = new B()之后。这里还有一个属性遮蔽的问题是:此时如果给A添加一个新方法newMethod并且在A的父级B上有一个同名的方法newMethod,那么在调用该方法时是调用A上的newMethod,而不是调用B上的这个方法!如果就想要调用B上的newMethod怎么办呢?可以使用__proto__实现:obj.__proto__.__proto__.newMethod()

5.  Object.prototype/Function.prototype/function Function/function Object

Object.prototype

  • 它是Object的原型,是ECMAScript规范创造的一个对象
  • 不考虑null的情况下,它是原型链的顶端
  • 所有原型对象的__proto__都指向Object.prototype
  • Object.prototype.__proto__指向的是null

Function.prototype

  • 它是一个函数(对象),但是它没有prototype属性,因为:Function.prototype.prototype输出undefined
  • Function.prototype.__proto__指向的是Object.prototype
        console.log(Function.prototype) // ƒ () { [native code] }
        console.log(Object.__proto__ == Function.prototype)  // true
        console.log(Object.__proto__ == Object.prototype)   // false

function Object()

  • Object作为构造函数出现时,生成的对象实例的__proto__指向Object.prototype。
  • Object.__proto__指向的是Function.prototype
        console.log(Object.__proto__)  // ƒ () { [native code] }
        console.log(Object.__proto__ == Function.prototype)  // true
        console.log(Object.__proto__ == Object.prototype)   // false

function Function()

  • Function构造函数是一个对象,它的__proto__指向Function.prototype
  • Function是作为众多function出来的函数的基类,因此所有的函数的__proto__都指向Function.prototype,包括构造函数Function()和Object()的__proto__

QUESTION:关于Function对象是不是由Function构造函数创建出来的?

下面给出有关原型链的一张万能图!

12-30 22:34
查看更多