jjw

写给自己的博客。 记录学习的点滴以备查。
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

js原型链解析

Posted on 2022-02-20 15:47  jjw  阅读(97)  评论(0编辑  收藏  举报

一、原型链的由来

在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.