美文网首页
彻底掌握js原型和原型链

彻底掌握js原型和原型链

作者: 哭不是罪 | 来源:发表于2021-03-21 16:55 被阅读0次

    这块知识点比较抽象,我会尽量用代码+示意图的方式梳理。如果你有疑惑,请耐心看完,可能会有帮助。

    构造函数和普通函数
            function Person() { }
            let zhangsan = new Person()
    

    这块代码创建了一个Person构造函数和一个由Person构造函数创建的zhangsan实例对象。

    其实Person构造函数和普通的函数并没有什么区别,只是在使用的时候用法不同,会有不同的叫法。

    普通函数在进行new操作的时候,它就称为构造函数,否则它就是普通函数。

    另外一点,为了在视觉上便于区分,通常会在定义当做构造函数使用的普通函数时,函数名的首字母用大写。这个不用太纠结。

    prototype和__ proto __
    prototype

    在函数中有一个prototype属性,为方便区分,通常叫做显式原型对象。
    而实例对象则没有。

    这个属性比较简单了,它就是一个对象{}(new Object()),但是这个对象下包含着一个指向函数本身的constructor方法。这个对象是可以随意更改的,尤其在实现继承的时候,注意改变函数的prototype指向。

            function Person(){
              // prototype: { constructor: function Person() }
            }
            let zhangsan = new Person()  
            console.log(Person.prototype.constructor === Person) // true Person有prototype,且Person.prototype.constructor 指向函数本身
            console.log(zhangsan.prototype) // undefined 实例对象没有prototype
            
    
    __ proto __
            function Person() { }
            let zhangsan = new Person()
            console.dir(Person.__proto__) // {...}
            console.dir(zhangsan.__proto__) // {...}
    

    无论是Person构造函数还是由该函数创建的zhangsan实例对象都会有一个叫做"__ proto __"的属性。它们是怎么来的呢?

    函数/实例在创建的时候都会有一个__ proto __属性,它是由创建该函数/实例对象的父级构造函数的prototype赋值而来的。

    为了方便区分,__ proto __通常叫做隐式原型

    zhangsan是由Person函数创建出来的,zhangsan的父级构造函数就是Person。

    那Person是由谁创建的呢,Person的父级构造函数是谁呢?

    其实函数也是有父级构造函数创建出来的,这个父级构造函数就是Function。Function是一个系统内置的函数。

    很多系统内置的函数也是由它创建的,如Array()、String()、Boolean()、Date()、Object()等等

            // function Person(){}  ==> var Person = new Function()
            // var str = '123' ==> var str = new String('123')
            // var obj = {} ==> var obj = new Object()
            // ......
    
            var Person = new Function()
            let zhangsan = new Person()
            // 这样看是不是就很像了呢。
            console.dir(Person.__proto__ === Function.prototype) // true
            console.dir(zhangsan.__proto__ === Person.prototype) // true
    
            // 先就__proto__讨论,上面就发生了类似于下面的过程
            // 创建Person时
            // function Person() {
            //     __proto__: Function.prototype
            // }
            
            // new Person时
            // function Person() {
            //     var this = {
            //         __proto__: Person.prototype
            //     }
            //     return this
            // }
    

    上面关于prototype 和 __ proto __ 的来龙去脉说完了,我觉得是有必要在前面就把它们讲清楚,因为原型和原型链就是围绕着这两个属性展开的。

    原型链
    function Person() { }
            Person.prototype.job = 'student'
            let  zhangsan = new Person()
            console.log(zhangsan.job)  // student
            console.log(zhangsan) // {__proto: {job:student}}
            console.log(zhangsan.toString()) // "[object Object]"
    

    从打印结果可以看出,zhangsan自身并没有job这个属性,倒是只有一个__ proto __ 属性,但是为什么zhangsan能访问到job呢?

    对象访问变量的时候会首先从自身找,如果自身找不到就从自身的__ proto __ (隐式原型)属性指向的父级函数的prototype(显式原型)中寻找,如果还找不到就沿着该显示原型的__ proto __继续寻找,形成的这个链状关系就是原型链,直到到达原型链的顶端Object.prototype(null)。

    示例中,zhangsan.__ proto __ 是在new Person创建zhangsan的时候,由Person的prototype赋值而来

            // new Person时
            // function Person() {
            //     var this = {
            //         __proto__: Person.prototype
            //     }
            //     return this
            // }
    

    zhangsan.__ proto __ 下面是有一个job属性的,所以zhangsan能访问job

    zhangsan.job(no value) ==> zhangsan.__proto__ .job ==> Person.prototype.job (has value) ==> success
    

    对照此图会更容易理解

    aaa.png

    这个图看似复杂,其实记住根本的两句话,并不难理解。
    函数和实例对象都会有一个__ proto __,它们的来源其实很简单

    谁创建了函数,谁就得把自己的prototype属性赋给新创建函数的 __ proto __
    谁创建了实例对象,谁就得把自己的prototype属性赋给新对象的__ proto __

    还以上面的代码举例,
    Person创建了zhangsan,Person的prototype就得赋给zhangsan.__ proto __ ,所以zhangsan.__ proto __ === Person.prototype

    Function创建了Person,Function的prototype就得赋给Person.__ proto ,所以Person. proto __ === Function.prototype

    Person.prototype也是一个对象(类似{}),谁创建了它呢,Object创建了它(new Object()),那么Person.prototype.__ proto __ === Object.prototype

    说了这么多,感觉还是苍白无力,这块比较抽象,需要多实践对比,自己画画图理解一下会好很好。

    个人总结,如有错误,请指正。

    相关文章

      网友评论

          本文标题:彻底掌握js原型和原型链

          本文链接:https://www.haomeiwen.com/subject/redncltx.html