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构造函数创建出来的?
下面给出有关原型链的一张万能图!