浅谈javascript中的__proto__、constructor、prototype

熟悉面向对象开发的,应该对这3个属性(记住,是属性,不是方法)很熟悉。但是对于这三者属性之间的关系和联系很容易混淆,一开始我也是看的抓头发,通过几篇文章切换看,多看几遍,用了2天时间,我也算是摸清一些门道了。所以就打算做个总结,记录一下,如有不对的地方,欢迎大佬指出。其中有一些内容是摘抄CSDN上一位大佬的文章,个人感觉这是我看过来最容易理解也全面的一篇文章了。

在开始之前,牢记这几点概念:

1、__proto__属性和constructor属性是对象独有的属性

2、prototype属性是函数独有的属性,但是在js中,函数也是一种对象,所以函数也拥有__proto__属性和constructor属性。即函数内置__proto__属性,constructor属性和prototype属性。

这两点一定要记住,心里默念三遍,只要记住这两条,理解后面的内容就不容易混淆了。

__proto__属性(注意前后是两个_ _,这里中间加了空格,)

__proto__思维导向图

__proto__属性都是一个对象指向一个对象(可以看上图,蓝色代表对象,橙色代表函数),即指向它们的原型对象(可以理解为父对象),那么这个属性有什么作用呢?它的作用就是当访问一个对象的属性时,如果该对象内部不存在这个属性,那么就会寻找该对象的__proto__属性,这个__proto__属性指向一个对象(原型对象,也可以理解为父对象),然后继续在父对象里查看是否存在这个属性,如果父对象还是没有找到这个属性,则继续找父对象的__proto__属性所指向的那个对象(可以理解为爷爷对象),如果还是没有则继续通过这种方法往上查找,直到原型链顶端null,若是没找到,则返回undefined。由以上这种通过__proto__属性来链接对象直到null的一条链即为 原型链。

如果上面的没看懂,没关系,我们换种思路来理解(还是以上图例子为例)。F1是Foo构造函数的实例,换句话说,F1就是由Foo构建成的。而构造函数的原型对象是在构造函数的prototype属性上(这个后面会讲到,这里记住就行了),所以F1的原型对象就是构造函数的原型对象,也就是Foo.prototype。这里在强调一遍,只有函数才有prototype属性。而对象是没有prototype属性的。还有区分清函数的原型对象和对象的原型对象。

prototype

prototype属性(原型对象)

prototype属性,它是一个函数指向一个对象。这里提醒大家不要忘记文章开头让熟记的两点,如果忘记了,在翻上去回忆一遍。prototype属性它的含义是函数的原型对象,也是这个构造函数所创建的实例的原型对象(F1.__proto__),由此可知,F1.__proto__===Foo.prototype。那prototype属性的作用又是什么呢?它的作用就是包含由特定类型的所有实例共享的属性和方法,也就是让该函数所实例化的多个对象都可以找到共用的属性和方法。任何函数在创建的时候,都会默认创建该函数的prototype对象。可能这里有点绕,下面我们来看一个例子。

function Person(name){
    this.name=name;
    this.say=function(){
      console.log(this.name)
   }
}
let zhangSan=new Person("张三");
let liSi=new Person("李四");
zhangSan.say();   //张三
liSi.say(); //李四
console.log(zhangSan.say===liSi.say)    //false

这里可以看出,实例化的两个对象都含有say这个方法,但是它们并不同一个方法。也就是说,每个实例化的对象都拥有自己一套独有的属性和方法。这样就会造成了资源的浪费,那怎么让实例化的多个对象调用同一个属性或方法呢。这时 JavaScript 的作者引入了原型对象 [Prototype] 来解决这个问题。

function Person(name){
    this.name=name;
}
Person.protype.say=function(){
   console.log(this.name)
}
let zhangSan=new Person("张三"); 
let liSi=new Person("李四");
zhangSan.say(); //张三
liSi.say(); //李四
console.log(zhangSan.say===liSi.say) //true
console.log(zhangSan.hasOwnPrototype("say"),liSi.hasOwnPrototype("say"))   //false  false

这里需要注意,实例后的对象拥有构造函数上的属性,以及继承prototype上的属性和方法,但是本身并没有portotype上的属性和方法(这也是前面提到的,对象没有prototype属性)。所以在实例后的对象上使用 hasOwnProperty()方法检测继承的属性会输出false。
constructor

constructor属性(构造函数)

constructor属性,它是一个对象指向一个函数。它的含义是指向对象的构造函数,每个对象都有构造函数(本身拥有或继承而来,继承而来的要结合__proto__属性查看会更清楚一些),因为创建对象的前提是需要有constructor(这里我们可以理解为函数),而这个constructor可能是对象自己本身显式定义或者是通过__proto__在原型链中找到的。而单从constructor这个属性来讲,只有prototype对象才有(注意观察上图)。每个函数在创建的时候,js会同时创建一个该函数的prototype对象,而函数创建的对象.__proto__===该函数.prototype即F1.__proto__===Foo.prototype,Foo.prototype.constructor=Foo,所以通过函数创建的对象即使没有constructor这个属性,它也能通过__proto__这个属性找到对于的constructor。所有函数和对象都是由function构造函数得来,所以constructor属性的终点就是function。

main

最后连接起来就是这个样式,黑色箭头代表__proto__,红色箭头代表constructor,绿色代表prototype,而红色虚线则代表自身没有,是通过继承而来的。蓝色代表对象橙色则代表函数。

上面的知识点可能有点繁杂,但是慢慢来,反复的看,然后自己在尝试着画出思维导图,下面做一个总结:

①__proto__和constructor属性是对象所独有的;

② prototype属性是函数所独有的,因为函数也是一种对象,所以函数也拥有__proto__和constructor属性。

③__proto__就是给对象在找不到自己本身的属性时提供一条找的方向。

④prototype原型对象主要用途是函数作为构造函数时使通过该构造函数所创建的对象具有共用的方法(这样就避免在每个实例对象中创建相同的属性或方法了)。

最后感谢这两位大佬的文章,文中的很多内容和例子都是选自这两篇文章;

原文:https://blog.csdn.net/cc18868876837/article/details/81211729;

原文:https://juejin.im/post/5c7b524ee51d453ee81877a7#heading-5

陈健的个人博客,记录生活所见所感、学习笔记。专注于Web前端_SEO教程_读书心得。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

返回主页看更多
狠狠的抽打博主 支付宝 扫一扫