• js原型链解析


    一、原型链的由来

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

  • 相关阅读:
    迭代器、生成器
    函数(函数基础、装饰器、递归、匿名函数)
    文件处理
    python对象、引用
    字符编码
    流程控制if、while、for
    编程与编程语言
    Java源码阅读(五)—— AbstractQueuedSynchronizer
    Java并发编程(二) —— volatile
    Java源码阅读(七)—— ReentrantReadWriteLock
  • 原文地址:https://www.cnblogs.com/Jiaojiawang/p/15915619.html
Copyright © 2020-2023  润新知