关于js的原型链,__proto__,prototype的理解

Posted fast-bullet

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了关于js的原型链,__proto__,prototype的理解相关的知识,希望对你有一定的参考价值。

首先声明参考博文,感谢

http://blog.sina.com.cn/s/blog_6c62f4330102wq0u.html

http://blog.csdn.net/leadn/article/details/51781539

等作者,还有一些其他链接这里就不粘贴了

 

js中关于prototype的文章不少,今天自己写一点自己的理解

 

不喜欢上来就粘概念,因为如果概念能够理解,也不需要写这篇文章了。

关于原型链里面的东西,主要就是prototype,__proto__这两个属性难以理解,今天我们就来看一下怎么玩耍这两个属性

先上代码

:先用js自定义一个对象,同时创建这个对象

  function Man(){   // 定义一个对象

  }
  var m = new Man() ; //创建一个对象
ok,到此我们的原材料就准备好了
 
接下来,不就是两个属性么,我们都来测试一下就好了
代码要是拼接字符串打印的效果不好,所以直接打印
console.log(m.__proto__)   //对象的__proto__属性
console.log(m.prototype) //对象的prototype属性
console.log("========================")
console.log(Man.__proto__) //函数的__proto__属性
console.log(Man.prototype) //函数的prototype属性

 结果

 技术分享图片

我们就直接直观的看结果就好了

  1.明显的m.prototype打印为undefined

  2.m.__proto__和Man.prototype打印的结果是一样的,可以自己手动证明(m.__proto__ == Man.prototype 返回 true)

  3.还有一个Man.__proto__打印的东西好奇怪 function() ,无所谓,暂时不管他

 

这个时候该上概念了

   prototype是构造函数的属性,指的就是构造函数的原型在生成实例的时候,js会根据构造函数的prototype属性将该属性下的对象生成为父类,只有构造函数这个属性才有这种效果哦~如果一个构造函数没有指定该属性,那么该属性下的__proto__会默认的指向原生Object的原型对象,该属性会变成一个对象,其中constructor属性指向本身。(这句直接粘来的,暂时看不懂就看我标红就行)   

   __proto__是对象具有属性可称为隐式原型,一个对象的隐式原型指向构造该对象的构造函数的原型,这也保证了实例能够访问在构造函数原型中定义的属性和方法。

  开始解释

    1.为什么m.prototype打印为undefined?

      废话,prototype是构造函数的属性,m是个对象,自然没有这个属性了,所以打印undefined

    2.m.__proto__和Man.prototype打印的结果是一样的

      他俩其实说的就是一个对象,看上面的蓝字,都是指向原型,我这里用的同一个对象,自然相等啊

    3.不解释,等着

 

那么这两个东西怎么用呢?

  你再看概念,prototype是构造函数的属性,__proto__是对象具有属性

  其实,只要区分清楚函数和对象,就能调用对应的属性,达到你想要的结果

  技术分享图片

  别跟我说函数是对象,容易混淆,函数就是函数

  到此,你只要能够知道的这么两点就行了

    1.只有函数可以调用prototype属性

     2.对象可以调用__proto__,但是不能调用prototype属性

 

  接下来说原型链

     其实,你只要理解了上面的两点,原型链就简单多了

   开始

     先回顾上面概念:看上面绿字(在生成实例的时候,js会根据构造函数的prototype属性将该属性下的对象生成为父类),简单讲,就是函数的prototype属性指向了他爸爸。

   
  再来看prototype的字面意思:原型,啥意思呢,就是说对象在创建之初,是根据原型的样子来创造的(就像女娲造人,照着自己的样子造,也可以理解为照着图纸造大楼)
,也就是说原型是谁,他就象谁。

  那么,上面的Man.prototype怎么指向了自己的函数对象呢?
     没有指定过prototype的函数就像孤儿一样,没有爸爸,怎么办呢,自己就是自己的爸爸(自己照顾自己呗),既然不能长得像别人,那就像自己好了

  原型链,要想链起来肯定得有多个对象啊,(其实所有的对象都来自Object,这里怕引起误会,就不用Object了)
  我们再声明一个对象People
    function People(){
        this.name = "Bullet";
    }

      然后让people成为Man的爸爸(这里就是实现js的继承,这种继承代码不够健壮,可以自己百度其他的继承方式,但这也算一种继承方式)

      很简单,不是说函数的prototype属性指向了他爸爸么,那么我就

  

   Man.prototype = new People(); //这里也只能用Man.prototype,因为对象是没有prototype属性的,这里后面要用new 是因为prototype指向的是一个对象哦

    这样,构造Man的时候就不照着Man构造了,而是照着People构造,既然照着People构造,People有的Man也得有啊。这样就实现了继承

    

  (测试继承的代码)

        function People(){
            this.name = "Bullet";
        }
        

        function Man(){

        }
        Man.prototype = new People(); //让Man认People做爸爸
var m = new Man(); console.log(m.name); //这里就能继承父类的name属性了,会打印Bullet

    

    那么,有了继承,如果子类的属性没有自定义的话,就会使用父类的属性,(就像这里可以读到父类的name属性一样)合情合理啊,但是Man到底怎么找到People的name属性的呢

    这就要循着原型链寻找了

    我们这里要找m的name属性,m本身是没有name的,怎么办?找他爸爸要啊,可他爸爸怎么找呢?我们只设定了Man.prototype = new People();,但是我m是个对象,有没有prototype属性,怎么办呢?

    还能怎么办?,我是个对象,我虽然没有prototype但是我有__proto__属性啊

    m.__proto__ == Man.prototype

    反之亦然啊

    Man.prototype = 爸爸对象
    m.__proto__ == Man.prototype
    m.__proto__ = 爸爸对象

    恭喜m对象顺利找到爸爸!!!这样就可以用爸爸的属性了呦

 

    结论

      所以子类找父类就像这样

      首先我们根据prototype 来实现继承,但是子类找父类时却要通过__proto__属性。

      没办法子类没有prototype啊,你能拿我怎么样?

      子对象.__proto__这样就找到了父类,

      原来__proto__属性就是对象用来找爸爸的属性啊!!!

     

      

      接着,找到的父类是个对象啊,就能继续__proto__找到爷爷,一致这么找下去

      所以  对象.__proto__.__proto__.__proto__.__proto__....是能一直点到尽头的(null)

      这个.__proto__.__proto__.__proto__.__proto__....组成的就是一条原型链

 

      

      这时,有人会有疑问

 

        函数.prototype.prototype...可以一直点么

 

      肯定不行啊,都说了 函数.prototype 获取(或设置)的是个对象,对象没有prototype属性,所以点到第二层就已经挂掉了(undefined

      

      console.log(Man.prototype.prototype);  // 这里一定为undefined  Man.prototype指向的是一个对象,对象没有prototype属性

       这也又一次解释了问题一

        1.为什么m.prototype打印为undefined?

            废话,prototype是构造函数的属性,m是个对象,自然没有这个属性了,所以打印undefined

 

      两者可以结合,我们可以

      函数.prototype.__proto__.__proto__.__proto__....是能一直点到尽头的,最后为null

 

 最后,我们解释最后一个问题

    3.为什么Man.__proto__打印的东西好奇怪 ,是一个function()

      首先他不是undifined,说明函数是有__proto__这个属性 ,怎么强行解释一波呢?

      很简单,我先道个歉,对不起,因为函数他确实是个对象,刚才让你区分是为了好解释,现在你再想起来吧(因为万物皆对象,函数当然也是对象)

 

      所以Man这个函数是可以使用__proto__属性的

     

    上面又说了__proto__相当于找爸爸的属性,

    我们使用Man.__proto__返回function() 说明function()是这个函数的爸爸,我们还能再继续.__proto__,能够找到Object,说明fanction也是Object的一个子类

        console.log(Man.__proto__);           //找到爸爸function()
        console.log(Man.__proto__.__proto__); //找到爷爷Object{}

 

    解释一:

      这里为什么不是找到People,原因是因为这里是一个Man函数对象,函数是通过function()来构造的,上面的继承关系是对对象(就是m)来讲的,

      所以Man函数的爸爸是function,而new Man()对象的爸爸是People,不要混淆对象和函数的区别就好(虽然我知道函数也是个对象)

    解释二:

      网上经常可以看到这张好乱的对象关系

      技术分享图片

  你慢慢看原图,我再给你画一个(虽然很丑)

  

  技术分享图片

 

       使用Object这个超类来找父类的时候会返回null(传说中的无中生有),可以自行百度,上面参考链接中有提到返回null的原因

      

 

    最后总结

    对象找爸爸用__proto__属性来找,这个大家都有,万物皆对象

    函数因为可以构造,所以可以使用prototype来找他是根据谁构造出来的,但是只能找一层,因为函数是根据对象构造出来的,继续找就只能用__proto__

 

    所以,        一个对象.__proto__ == 这个对象的函数.prototype

    翻译成人话就是                        对象他爸  == 构造这个对象的对象 

 

 

    记住只要分清对象和函数两个东西,使用__proto__和prototype就不会再有难度了

 

    头一次写博客,大家可以随意喷啊,促进进步,哈哈哈

 

 

 















以上是关于关于js的原型链,__proto__,prototype的理解的主要内容,如果未能解决你的问题,请参考以下文章

关于JS面向对象中原型和原型链以及他们之间的关系及this的详解

关于JS面向对象中原型和原型链以及他们之间的关系及this的详解

关于JS中原型链中的prototype与_proto_的个人理解与详细总结(代码示例+高清无码大图!——自备瓜子花生)

js 原型链

JS 原型链

js 最容易理解的原型链 prototype和_proto_