一、原型链的由来
在java,c#等语言中,所有的对象类型都可以从下到上追溯到object上,而javascript中也有这种机制-----原型链。
JS的原型链类似 数据结构的链表, prototype类似节点,__proto__类似 next 指针,这里在网上找了个图片。
二、__proto__ 和 prototype 的关系
prototype 是函数所特有的属性,在ES6之前,我们在JS中定义一个函数,相当于在JAVA,C#中定义了一个类,我们要给这个函数添加方法和属性,
可以直接在这个函数的 prototype上添加,例如:
function MyFunc() {} //声明之后, MyFunc就有了一个prototype的属性。 MyFunc.prototype.say = function () { console.log('hello, my name is ' + this.name); } MyFunc.prototype.name = 'xiao ming' var f = new MyFunc(); //f 这时默认有了一个__proto__属性, 指向的就是 MyFunc.prototype f.say(); 实际上相当于 f.__proto__.say();
//f.__proto__.say() 也是一样的。
在上的代码中,定义了一个函数 MyFunc,那么 MyFunc就有了一个 prototype 属性, 同时也有了一个__proto__属性。
MyFunc.prototype 和 MyFunc.__proto__ 可不是一回事!后面再解释。
当 var f = new MyFunc() 后, f 就有了一个 __proto__属性,这个__proto__就指向 MyFunc.prototype,
当调用 f.say()时,JS发现没 f 本身没有say()方法,就去查找 f 的 __proto__ 所指向的prototype有没有,如果有就 调用 f.__proto__.say(), 否则就去找
f.__proto__.__proto__上有没有,如果还没有,就继续 f.__proto__.__proto__.__proto__, 一直追到 __proto__为null 就不找了。
前面说过, prototype中也有一个__proto__属性,那么 f.prototype的__proto__又指向哪儿?
指向Object !
三、站在最顶端的 Object
先看看Object是什么:
console.log(Object); //function Object()
是一个函数,既然是函数,再看下Object.prototype,和Object.prototype.__proto__
console.log(Object.prototype); //Object { … } console.log(Object.prototype.__proto__); //null
从上面的代码输出可以看到,Object的prototype已经到头了,换句话说,如果某个方法或属性,在Object.prototype中还找不到那就找不到了。
四、函数的 __proto__
先看下面的代码
function MyFunc() {} console.log(MyFunc.call); //function call() console.log(MyFunc.apply); //function apply()
MyFunc 居然有 call 和 apply 方法,这是哪来的?我们并没有在 MyFunc 中定义这两个方法。
function MyFunc() {} console.log(MyFunc.prototype.__proto__.call); //undefined console.log(MyFunc.prototype.__proto__.apply); //undefined console.log(MyFunc.__proto__.call); //function call() console.log(MyFunc.__proto__.apply) //function apply()
原来在MyFunc.__proto__指向的prototype中有这两个方法, 这个找法是不是和 new 实例的方式一样,先从实例自身找,找不到再去__proto__所指向的prototype中去找。
Function.prototype.fn = function () { console.log('Function.call '); } function MyFunc() {} console.log(MyFunc.__proto__); //function () console.log(MyFunc.__proto__.__proto__); //object {} console.log(MyFunc.__proto__.__proto__.__proto__); //null console.log(MyFunc.__proto__ === Function.prototype); //true MyFunc.fn(); //Function.call
五、一些不同点
Function.prototype.fn = function () { console.log('Function.call '); } Object.prototype.fn = function () { console.log('Object.call '); } function MyFunc() {} MyFunc.fn(); //Function.call var f = new MyFunc(); f.fn(); //Object.call 如果不在Object上定义 Object.prototype.fn, 刚会调用失败。
函数的执行 与 我们代码 new 的实例执行方式还是有差异的。相当于JS解释器内部new了后再执行,所以运行我们自定义的函数或 执行Object.xxx的时, __proto__ 都向
Function.prototype。
Function.prototype.fn = function () { console.log('Function.call '); } Object.prototype.fn = function () { console.log('Object.call '); } function F() {} console.log(F.prototype); F.fn(); //Function.call
执行上面的代码可以看到,函数的执行是先找F.__proto__指向的 prototype 上是不是有fn, 如果有,就执行 __proto__.fn()。如果没找到,就会执行F.__proto__.__proto__.fn(),
就会输出 Object.call.