在《谈谈JavaScript中的function constructor和new关键字》这篇文章中咱们说明了如何经过函数构造式(function constructor)搭配关键字new来创建对象,但其实这样只讲了一半,在这篇咱们会补齐另外一半,说明function constructor如何用来设定该对象的原型(prototype)。javascript
在JavaScript中的函数也是一种对象,其中包含一些属性像是该函数的名称(Name
)和该函数的内容(Code
),但其实function这里面还有一个属性,这个属性就是prototype
,这个属性会以空对象的型式呈现。java
除非你是把function当作function constructor来使用,不然这个属性就没有特别的用途;但若是你是把它当作function constructor,经过new
这个关键字来执行这个function的话,它就有特别的意义了。git
要进入这个function的prototype
属性只要直接经过 .prototype
就能够了。github
然而,有一点很容易使人困惑的地方,咱们会觉得若是我使用 .prototype
时,就能够直接进入该函数的原型,但实际上并非这样的!json
函数当中prototype
这个属性并非这个函数的prototype
,它指的是全部经过这个function constructor所创建出来的对象的prototype
,听起来有点混乱吧...不要紧,让咱们来看一些代码来帮助咱们理解这一律念。函数
1.function 中的prototype 属性一开始是空对象性能
咱们先执行上篇文章最后所写的代码:ui
function Person ( firstName , lastName ) {
this . firstName = firstName ;
this . lastName = lastName ;
}
var person1 = new Person ( 'Jay' , 'chou' ) ;
console . log ( person1 ) ;
var person2 = new Person ( 'Jane' , 'chou' ) ;
console . log ( person2 ) ;
复制代码
到Google Chrome的console视窗中,咱们输入 Person.prototype
获得的结果会获得一个空对象,以下图:this
2.经过function constructor 所创建的对象会继承该function 中prototype 的内容spa
接着,让咱们在Person.prototype
里面增长一个getFullName
的函数:
function Person ( firstName , lastName ) {
this . firstName = firstName ;
this . lastName = lastName ;
}
Person . prototype . getFullName = function ( ) {
return this . firstName + ' ' + this . lastName ;
}
var person1 = new Person ( 'Jay' , 'chou' ) ;
console . log ( person1 ) ;
var person2 = new Person ( 'Jane' , 'chou' ) ;
console . log ( person2 ) ;
复制代码
在上面代码的第6 - 8行中,咱们为Person.prototype
添加了一个函数,因此当咱们在Google Chrome的console视窗中调用Person.prototype
时,会多了这个函数在内:
刚刚,咱们有提到很重要的一句话,「函数当中prototype
这个属性并非这个函数的prototype
,它指的是全部经过这个function constructor所创建出来的对象的prototype
」。
这句话的意思实际上是说Person.prototype
并非Person.__proto__
,可是全部经过Person
这个function constructor所创建的对象,在该实例对象的__proto__
中,会包含有Person.prototype
的内容。
也就是说,当咱们使用new
这个运算符来执行function constructor时,它会先创建一个空对象,同时将该构造函数中prototype
这个属性的内容(Person.prototype
),设置到该实例对象的prototype
中,即 person1.__proto__ === Person.prototype
的结果为true
。
所以,当咱们在Google Chrome的console中输入person1.__proto__
时,咱们就能够看到刚刚在Person.prototype
所创建的函数getFullName
已经继承在里面了:
因为Person.prototype
中的方法已经被继承到由Person
这个function constructor所创建的实例对象person1
中,因此这时侯,咱们就能够顺利的使用 person1.getFullName
这个方法:
function Person ( firstName , lastName ) {
this . firstName = firstName ;
this . lastName = lastName ;
}
Person . prototype . getFullName = function ( ) {
return this . firstName + ' ' + this . lastName ;
}
var person1 = new Person ( 'Jay' , 'chou' ) ;
console . log ( person1 ) ;
console . log ( person1.getFullName() ) ;
复制代码
能够正确的执行getFullName
这个函数并获得以下的结果:
经过这样的方法,咱们可让全部根据这个函数构造器(function constructor)所创建的对象都包含有某些咱们想要使用的方法。若是咱们有1000个对象是根据这个函数构造器所创建的,那么咱们只须要使用 .prototype
这样的方法,就可让这1000个物件均可以使用到咱们想要执行的某个method
,这样减小了代码的复用。
有的人可能会好奇问,为何咱们不把getFullName
这个方法直接写在函数构造式当中呢?
function Person ( firstName , lastName ) {
this . firstName = firstName ;
this . lastName = lastName ;
this . getFullName = function ( ) {
return this . firstName + ' ' + this . lastName ;
}
}
/* Person . prototype . getFullName = function ( ) { return this . firstName + ' ' + this . lastName ; } */
复制代码
注意!咱们不应把方法放在function constructor 中。
把方法放在函数构造式中这么作虽然仍然能够正确执行并获得结果,可是这么作会有个问题,若是咱们是把这个方法直接写在函数构造式中,那么每个对象都会包含有这个方法,若是咱们有1000 个对象根据这个函数构造式所创建,那么这1000 个对象都会包含这个方法在内,如此将会占据至关多的内存;但若是是创建在prototype
中,咱们只会有一个这样的方法。
因此,为了性能上的考量,一般会把方法(method
)放在构造函数的prototype
中,由于它们能够是通用的;把属性(property
)放在构造函数当中,由于每个对象可能都会有不一样的属性内容,如此将能有效减小内存的问题。
最后,若是感受当前缺乏你要用的方法,能够本身经过这一方法去建立。
例如在json2.js源码中,为Date
、String
、Number
、Boolean
方法添加一个toJSON
的属性。
if (typeof Date.prototype.toJSON !== 'function') {
Date.prototype.toJSON = function (key) {
return isFinite(this.valueOf()) ?
this.getUTCFullYear() + '-' +
f(this.getUTCMonth() + 1) + '-' +
f(this.getUTCDate()) + 'T' +
f(this.getUTCHours()) + ':' +
f(this.getUTCMinutes()) + ':' +
f(this.getUTCSeconds()) + 'Z' : null;
};
String.prototype.toJSON =
Number.prototype.toJSON =
Boolean.prototype.toJSON = function (key) {
return this.valueOf();
};
}
复制代码
若是你要添加内置方法的原型属性,最好作一步判断,若是该属性不存在,则添加。若是原本就存在,就不必再添加了。
若是以为文章对你有些许帮助,欢迎在个人GitHub博客点赞和关注,感激涕零!