最近看了很多文章,想要更通透的搞懂JS中的prototype
、__proto__
与constructor
属性,从各个博主的文章里摘取了我认为可以有助于理解的一些内容,希望自己能够掌握好这一重要知识点的同时也帮助到大家,具体内容请见下文。
(注意:文中__proto__
属性的两边是各由两个下划线构成。 )
本文通过下面一个简单的例子展开讨论,并配以相关的图帮助理解:
function Foo() { this.name="小红";}; //创建一个函数Foovar f1 = new Foo(); var f2 = new Foo(); //通过new关键字,实例化Foo这个构造函数得到一个实例化对象f1console.log(f1 === f2); //false// 打印起来,看看都是啥吧console.log(fn.prototype); //undefined,可见由构造函数创建出的对象没有prototype属性,但是有__proto__属性console.log(Foo.prototype); //{constructor: ƒ Foo(...), __proto__: Object}console.log(fn.__proto__); //{constructor: ƒ Foo(...), __proto__: Object} fn.__proto__指向构造函数的prototypeconsole.log(Foo.__proto__); //ƒ () { [native code] } 指向了Foo的原型对象Function,大家可以尝试一下Function.prototype是什么结果console.log(fn.constructor); //ƒ Foo() {this.name="小红"} 对象的constructor用于返回创建这个对象的函数(即构造函数)console.log(Foo.constructor); //ƒ Function() { [native code] } Foo也是一个对象,它的构造函数是Function(),所以返回的也是Function(),而Function()较特殊,它的构造函数是他自己,下文会再次强调console.log(Foo.prototype.constructor); //ƒ Foo() {this.name="小红"}console.log(Foo.prototype.__proto__); //{constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ,…}复制代码
虽然是简简单单的两行代码,然而它们背后的关系却是错综复杂的,如下图所示:
别怕,让我们一步步剖析,彻底搞懂它们。
图的说明:右下角为图例,红色箭头表示__proto__
属性指向、绿色箭头表示prototype
属性的指向、棕色箭头表示constructor
属性的指向;蓝色方块表示对象,浅绿色方块表示函数。图的中间部分即为它们之间的联系,图的最左边即为例子代码
首先,我们需要牢记两点:
① __proto__
和constructor
属性是对象所独有
的;
② prototype
属性是函数所独有
的。
但是由于JS中函数也是一种对象
,所以函数也拥有__proto__
和constructor
属性,这点是致使我们产生困惑的很大原因之一。
(例如 获取图中Foo函数的__proto__和constructor的方式:Foo.prototype.__proto__; Foo.prototype.constructor;)
上图有点复杂,我们把它按照属性分别拆开,然后进行分析:
__proto__
属性
__proto__
属性,它是 对象所独有
的,可以看到__proto__
属性都是由 一个对象指向一个对象
,即指向它们的原型对象(也可以理解为父对象)。
那么__proto__
属性的作用是什么呢?
它的作用
就是当访问一个对象的属性时,如果该对象内部不存在这个属性,那么就会去它的__proto__
属性所指向的那个对象(可以理解为父对象)里找,如果父对象也不存在这个属性,则继续往父对象的__proto__
属性所指向的那个对象(可以理解为爷爷对象)里找,如果还没找到,则继续往上找….直到原型链顶端 null
(可以理解为原始人。。。),此时若还没找到,则返回undefined
(可以理解为,再往上就已经不是“人”的范畴了,找不到了,到此为止),由以上这种通过__proto__
属性来连接对象直到null
的一条链即为我们所谓的 原型链
。
prototype
属性
prototype
属性,别忘了一点,就是我们前面提到要牢记的两点中的第二点:
① 函数所独有的
② 它是从一个函数指向一个对象
。
它的 含义 是函数的原型对象
,也就是这个函数(其实所有函数都可以作为构造函数)所创建的实例的原型对象,由此可知:f1.__proto__ === Foo.prototype
,它们两个完全一样。
那prototype
属性的作用
又是什么呢?
它的作用
就是 包含可以由特定类型的所有实例共享的属性和方法,也就是让该函数所实例化的对象们都可以找到公用的属性和方法
。
constructor
属性
constructor
属性也是对象才拥有的,它是从一个对象指向一个函数
,含义 就是指向该对象的构造函数
,每个对象都有构造函数,从图中可以看出 Function
这个对象比较特殊,它的构造函数就是它自己
(因为Function可以看成是一个函数,也可以是一个对象),所有函数最终都是由Function()构造函数得来,所以constructor
属性的终点就是 Function()
。
——————————————————————
总结一下:
-
我们需要牢记两点:
①
__proto__
和constructor
属性是对象所独有的;②
prototype
属性是函数所独有的,因为函数也是一种对象,所以函数也拥有__proto__
和constructor
属性。 -
__proto__
属性的作用就是当访问一个对象的属性时,如果该对象内部不存在这个属性,那么就会去它的__proto__
属性所指向的那个对象(父对象)里找,一直找,直到__proto__
属性的终点null
,然后返回undefined,通过__proto__
属性将对象连接起来的这条链路即我们所谓的原型链
。 -
prototype
属性的作用就是让该函数所实例化的对象们都可以找到公用的属性和方法,即f1.__proto__ === Foo.prototype
。 -
constructor
属性的含义就是指向该对象的构造函数
,所有函数(此时看成对象了)最终的构造函数都指向终点Function()
。
下面根据上述内容整理相关的表格如下:
__proto__ | constructor | prototype | |
---|---|---|---|
属性归属 | 对象独有 | 对象独有 | 函数独有(由于函数也是对象,所以函数也拥有__proto__ 和constructor 属性) |
含义 | 指向它们的原型对象(也可以理解为父对象),最终指向null,实现了原型链 | 指向该对象的构造函数,最终指向Function() | 函数的原型对象 |
指向 | 一个对象指向一个对象 | 一个对象指向一个函数 | 一个函数指向一个对象 |
作用 | 作为原型链的桥梁,帮助向上一层层找到被访问对象的属性,直到null | 返回对创建此对象的函数的引用 | 包含可以由特定类型的所有实例共享 的属性和方法,也就是让该函数所实例化的对象们都可以找到公用的属性和方法。 |
帮助理解
帮助理解 __proto__ 和 prototype
//如果你能耐心看完下列打印内容和注释,相信你会对 prototype、__proto__ 属性理解的更通透function Fun(){}; //创造了一个函数Fun,这个函数由Function生成(Function作为构造函数)var fn=new Fun(); //创建了一个函数fn,这个函数由Fn生成(Fn作为构造函数) console.log(fn.__proto__===Fun.prototype) //true// fn的__proto__指向其构造函数Fun的prototypeconsole.log(Fun.__proto__===Function.prototype) //true// Fun的__proto__指向其构造函数Function的prototypeconsole.log(Function.__proto__===Function.prototype) //true// Function的__proto__指向其构造函数Function的prototype// 构造函数自身是一个函数,他是被自身构造的console.log(Function.prototype.__proto__===Object.prototype) //true// Function.prototype的__proto__指向其构造函数Object的prototype// Function.prototype是一个对象,同样是一个方法,方法是函数,所以它必须有自己的构造函数也就是Objectconsole.log(Fun.prototype.__proto__===Object.prototype) //true// 与上条相同// 此处可以知道一点,所有构造函数的的prototype方法的__都指向__Object.prototype(除了....Object.prototype自身)console.log(Object.__proto__===Function.prototype) //true// Object作为一个构造函数(是一个函数对象!!函数对象!!),所以他的__proto__指向Function.prototypeconsole.log(Object.prototype.__proto__===null) //true// Object.prototype作为一切的源头,他的__proto__是null// 下面是一个新的,额外的例子var obj={}// 创建了一个objconsole.log(obj.__proto__===Object.prototype) //true// obj作为一个直接以字面量创建的对象,所以obj__proto__直接指向了Object.prototype,而不需要经过Function了!!// 下面是根据原型链延伸的内容// 还有一个上文并未提到的constructor, constructor在原型链中,是作为对象prototypr的一个属性存在的,它指向构造函数(由于主要讲原型链,这个就没在意、);console.log(obj.__proto__.__proto__ === null) //trueconsole.log(obj.__proto__.constructor === Object) //trueconsole.log(obj.__proto__.constructor.__proto__===Function.prototype) //trueconsole.log(obj.__proto__.constructor.__proto__.__proto__===Object.prototype) //true console.log(obj.__proto__.constructor.__proto__.__proto__.__proto__===null) //trueconsole.log(obj.__proto__.constructor.__proto__.__proto__.constructor.__proto__===Function.prototype) //true复制代码
帮助理解 constructor和prototype
刚刚有说,constructor
属性是对象才拥有的,它是从一个对象指向一个函数
,含义 就是指向该对象的构造函数
。
prototype
属性是函数所独有
的,是函数的原型对象,是由一个函数指向一个对象。
按照javascript的说法,function定义的方法就是一个Object(对象),而且还是一个很特殊的对象,这个使用function定义的对象与使用new操作符生成的对象之间有一个重要的区别。就是function定义的对象有一个prototype属性,使用new生成的对象就没有这个prototype属性
。
prototype属性又指向了一个prototype对象,注意prototype属性与prototype对象是两个不同的东西,要注意区别。在prototype对象中又有一个constructor属性,这个constructor属性同样指向一个constructor对象,而这个constructor对象恰恰就是这个function函数本身。
有点晕么?请看下图:
function Person(name){ this.name=name; this.showMe=function(){ alert(this.name); } }; var one=new Person('js'); console.log(one.prototype); //undefined 证明了one这个对象没有prototype属性console.log(typeof Person.prototype); //object console.log(Person.prototype.constructor); //function Person(name) {...}; console.log(one.constructor); //function Person(name) {...}; 复制代码
巩固
练习:下面自己动手画一画这段代码的原型图:
function People(name) { this.name = name; this.sayName = function() { console.log('my name is:' + this.name); }}People.prototype.walk = function() { console.log(this.name + 'is walking');}var p1 = new People('小明');var p2 = new People('小红');复制代码
练习:创建一个 Car 对象,拥有属性name、color、status;拥有方法run,stop,getStatus
function Car(name, color, status) { this.name = name; this.color = color; this.status = status;}Car.prototype = { constructor : Car, run: function() { this.status = 'run'; }, stop: function() { this.status = 'stop'; }, getStatus: function() { console.log(this.status); }}var car1 = new Car('BMW', 'red', 'stop');复制代码
参考
本文内容均参考自以下文章(以便追根溯源):
如若发现文中纰漏请留言,欢迎大家纠错,我们一起成长。