谈谈JavaScript中的function constructor和prototype的创建

《谈谈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,听起来有点混乱吧...不要紧,让咱们来看一些代码来帮助咱们理解这一律念。函数

说明函数中的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与Prototype 的实用处

经过这样的方法,咱们可让全部根据这个函数构造器(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源码中,为DateStringNumberBoolean方法添加一个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博客点赞和关注,感激涕零!

相关文章
相关标签/搜索