JavaScript 中的 new 究竟干了什么,跟原型链又有一些什么联络?

原文:https://legacy.ofcrab.com/press/javascript-new.html

假如按面向对象的思绪去讲 JavaScript 的 new,照样很难去明白,我们能够从另一个方向去明白一下它。

你这些人类

我是一位顺序员,也是一个人,我能够:

  • 有一个响亮亮的称号
  • 在某一天诞生
  • 是个男子
  • 我能行走
  • 我还能跑步
  • 还能腾跃
  • 能措辞
  • 我还能写代码

那末,在 JavaScript 中,我们能够像下面如许表达我:

const me = {
  name: '大胡子农同工潘半仙',
  birth: '1988-08-08',
  sex: 'male',
  walk: function (speed, direction, duration) {
    // 以 speed 的速率向 direction 方向行走 duration 长的时刻
  },
  run: function (speed, direction, duration) {
    // 像跑步一样,速率
  },
  jump: function (high, direction, angle) {
    // 以 angle 角度向 direction 方向跳 high 高
  },
  speak: function (letters) {
    // 说出 letters 这些词
  },
  coding: function (language, line) {
    // 写顺序呢
  }
}

你们这些人类

固然,这个天下上不能够只要我一个顺序员,更不能够只要我一个人,就像我们这个小公司,就有七八百人,好像一切这些人的数据都保留在数据库内里:

namesexbirth
潘韬male1988-08-08
高明male1985-08-09
春雨male1999-08-08

我们从数据库中查询出上面这几条纪录,在 JavaScript 能够示意为一个二维数据,然后要竖立出这三个人来,多是下面如许的:

const people = DB.query()
// people = [['潘韬', 'male', '1988-08-08'], [...], [...]]
for (let i = 0; i < people.length; i++) {
  let [name, sex, birth] = people[i]
  people[i] = {
    name,
    sex,
    birth,
    walk: function () {},
    run: function () {},
    jump: function () {},
    speak: function () {},
    coding: function () {}
  }
}

反复的资本占用

上面人人已发现,像上面如许去竖立三个对象, walkrunjumpspeakcoding 这五件能做的事变(要领),实在做法都一样,然则我们却反复的去形貌该怎样做了,实在就占用了许多资本,所以,我们能够会像下面如许革新一下:

const walk = function walk () {}
const run = function run () {}
const jump = function jump () {}
const speak = function speak () {}
const coding = function coding () {}

for (let i = 0; i < people.length; i++) {
  let [name, sex, birth] = people[i]
  people[i] = {
    name,
    sex,
    birth,
    walk,
    run,
    jump,
    speak,
    coding
  }
}

差别的人共用雷同的资本(要领)

然则这个天下不止有人类

对,人类比拟于这个天下上的别的生物来说,数目根本就值得一提,假如像上面如许,能够种种差别物种能做的事变都邑要定义出差别的函数,爬动一定不是人类会去做的事变,但许多别的生物会做,那末为了代码治理轻易,我们把人能做的一切事变都放在一个对象内里,如许就相当于有了一个定名空间了,不会再跟别的物种相冲突:

const whatPeopleCanDo = {
    walk: function () {},
    run: function () {},
    jump: function () {},
    speak: function () {},
    coding: function () {}
}
for (let i = 0; i < people.length; i++) {
  let [name, sex, birth] = people[i]
  people[i] = {
    name,
    sex,
    birth,
    ...whatPeopleCanDo
  }
}

原型

然则,有的人能够我们并不知道他的 sex 信息是多少,有的也有能够不知道 birth 是多少,然则我们愿望在竖立这个人的时刻,能给不知道的数据一些初始数据,所以, whatPeopleCanDo 并不能完整的表达出一个人,我们再革新:

const peopleLike = {
    name: '',
    sex: 'unknown',
    birth: '',
    walk: function () {},
    run: function () {},
    jump: function () {},
    speak: function () {},
    coding: function () {}
}
for (let i = 0; i < people.length; i++) {
  let [name, sex, birth] = people[i]
  people[i] = {
    ...peopleLike,
    name: name || peopleLike.name,
    sex: sex || peopleLike.sex,
    birth: birth || peopleLike.birth
  }
}

如许一来,我们就能够为不知道的属性加一些默认值,我们称 peopleLike 这个东东就为原型,它示意了像人类如许的物种有哪些属性,醒目什么事变。

原型链

虽然上面已比最最先的版本好得多了,然则照样能有很大的革新空间,我们如今像下面如许改一下:

const peoplePrototype = {
    name: '',
    sex: 'unknown',
    birth: '',
    walk: function () {},
    run: function () {},
    jump: function () {},
    speak: function () {},
    coding: function () {}
}
for (let i = 0; i < people.length; i++) {
  let [name, sex, birth] = people[i]
  people[i] = {
    name: name || peoplePrototype.name,
    sex: sex || peoplePrototype.sex,
    birth: birth || peoplePrototype.birth,
    __proto__: peoplePrototype
  }
}

我们不再把人类原型内里的一切要领都绑定到某个人身上,而是像上面如许,用一个特别的字段 __proto__ 来指定:我的原型是 peoplePrototype 这个对象,同时,我们还制订了一个划定规矩:假如你想要求我的某个要领,在我本身身上没有,那就去我的原型上面找吧,假如我的原型上面没有,那就去我的原型的原型上面去找,直到某个位置,没有更上层的原型为止

像上面如许竖立的 people 对象,有本身的属性,然则当我们去接见 people.speak() 要领的时刻,实在接见的是 people.__proto__.speak(),这是我们的划定规矩。

更文雅的竖立新新人类

我们总不能在须要竖立新人的时刻,都像上面如许,本身去写一个对象,然后再手工指定它的原型是什么,所以,我们能够竖立一个函数,特地用来天生人类的:

const peoplePrototype = {
    name: '',
    sex: 'unknown',
    birth: '',
    walk: function () {},
    run: function () {},
    jump: function () {},
    speak: function () {},
    coding: function () {}
}
const makePeople = function makePeople(name, sex, birth) {
  let people = {}
  people.name = name || peoplePrototype.name
  people.sex = sex || peoplePrototype.sex
  people.birth = birth || peoplePrototype.birth
  people.__proto__ = peoplePrototype
  return people
}

people = people.map(makePeople)

如今如许我们只须要引入 makePeople 这个函数就能够随时随地竖立新人了。

更文雅一点的革新

明显,上面如许并非最好的方法,定义了一个原型,又定义了一个原型对象,我们能够把这两个合并到一同,所以,就能够有下面如许的完成了:

const People = function People (name, sex, birth) {
  let people = {}
  people.name = name || People.prototype.name
  people.sex = sex || People.prototype.sex
  people.birth = birth || People.prototype.birth
  people.__proto__ = People.prototype
  return people
}

People.prototype = {
    name: '',
    sex: 'unknown',
    birth: '',
    walk: function () {},
    run: function () {},
    jump: function () {},
    speak: function () {},
    coding: function () {}
}

我们直接把竖立人类的谁人函数叫作 People,这个函数有一个属性叫 prototype,它示意用我这个函数竖立的对象的原型是什么,这个函数做的事变照样之前那些事儿,竖立暂时对象,设置对象的属性,绑定一下原型,然后返回。

奇异的 this

我们除了人,另有别的动物,比方 TigerFish等,按上面的体式格局,在 Tiger() 或许 Fish() 函数内里都邑竖立差别的 tiger 或许 fish 称号的暂时对象,如许太贫苦,我们把这类函数竖立出来的对象,都能够一致叫作“这个对象” ,也就是 this object,不在体贴是人是鬼,一致把一切的暂时对象都叫 thisObject 或许更简朴的就叫作:这个,即 this

const People = function People (name, sex, birth) {
  let this = {}
  this.name = name || People.prototype.name
  this.sex = sex || People.prototype.sex
  this.birth = birth || People.prototype.birth
  this.__proto__ = People.prototype
  return this
}

固然,上面的这一段代码是有题目的,只是设想一样,如许是否是可行。

new

到如今为止,我们发现了全部代码的演化,是时刻引出这个 new 了,它来干什么呢?它背面接一个相似上面这类 People 的函数,示意我须要竖立一个 People 的实例,它的发现就是为了处理上面这些一切反复的事变,有了 new 以后,我们不须要再每一次定义一个暂时对象,在 new 的上下文关联中,会在 People 函数体内自动为竖立一个暂时变量 this,这个就示意即将被竖立出来的对象。同时,关于运用 new 竖立的实例,会自动的绑定到竖立函数的 prototype 作为原型,还会自动为 People 竖立一个 constructor 函数,示意这个原型的竖立函数是什么,所以,我们能够改成下面如许的了:

const People = function People (name, sex, birth) {
  this.name = name || People.prototype.name
  this.sex = sex || People.prototype.sex
  this.birth = birth || People.prototype.birth
}

People.prototype.name = ''
People.prototype.sex = 'unknown'
People.prototype.birth = ''
People.prototype.walk = function () {}
People.prototype.run = function () {}
People.prototype.jump = function () {}
People.prototype.speak = function () {}
People.prototype.coding = function () {}

people = people.map(p => new People(...p))

总结

new 究竟干了什么?当 new People() 的时刻

  1. 竖立暂时变量 this,并将 this 绑定到 People 函数体里
  2. 实行 People.prototype.constructor = People
  3. 实行 this.__proto__ = People.prototype
  4. 实行 People 函数体中的自定义
  5. 返回新竖立的对象
    原文作者:大胡子民工潘半仙
    原文地址: https://segmentfault.com/a/1190000018908403
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞