3

再解 JavaScript 原型与原型链

 2 years ago
source link: https://segmentfault.com/a/1190000041362719
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

再解 JavaScript 原型与原型链

发布于 2 月 2 日

JavaScript 原型与原型链虽然是一个老生常谈的话题,但依然困惑着很多人,今天我再来从另一个角度谈谈这个问题。

先看这样一段代码:

let obj = {}
obj.__proto__.haha = 'gogo'
console.log(obj.haha) // "gogo"

运行一下上面的代码,输出结果为 gogo

针对这个结果,有以下疑问:

  • obj 哪来的 __proto__属性?
  • 为什么添加到 __proto__上的属性,可以直接通过 obj 拿到?

第一个问题

js 中每个对象都有一个“原型”,原型一般可以通过 __proto__访问到:

let obj = {}
console.log(obj.__proto__)
// {constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, …}

可以这么理解:原型,也是一个对象

  • 每个“人”都有一个“爸爸”;
  • 但“爸爸”也是一个“人”;
  • “爸爸”除了是某个人的爸爸外,与其他人并没有本质的区别;
  • 爸爸,也是普通人。
  • “原型”是一个普通的对象;
  • 爸爸也有他的爸爸,原型也有它的原型。

第二个问题

对象与人不同的是:

  • 人不可以随便拿爸爸的东西
  • 而对象可以随便拿原型里的东西

比如,当你向一个对象,索要一个属性时:

  • 如果这个对象没有你要的属性,它就会让它的原型(爸爸)给你
  • 如果它爸也没有,那它爸就会找它爸的爸

比如下面这个例子:

let obj = {
  __proto__: {
    __proto__: {
      haha: 'gogo'
    }
  }
}
console.log(obj.haha) // "gogo"

因为 obj 本身没有 haha 这个属性,所以它会去自己的 __proto__ 中查找,如果还没有找到,那就会向它的 __proto__.__proto__ 中去找,直到找到 haha 属性或者 __proto__ 链返回 null 为止。

再换一种写法:

let 爷爷 = {
  haha: 'gogo'
}
let 爸爸 = {
  __proto__: 爷爷
}
let obj = {
  __proto__: 爸爸
}
console.log(obj.haha) // "gogo"

查找 haha 属性的过程是: obj -> 爸爸 -> 爷爷 ,像不像是一条链子呢?这就是原型链。

关于 prototype

有这样一句话:

类是对象的模板

你与我,都是人,“人”是类,是模板。

你与我,都属于“人”类,有很多共性:

  • 会睡觉
    ……

这些共性是人类共有的。

当然,你与我做为独立的对象,肯定也存在差异,比如:我的名字叫 X,你的名字叫 Y 。这些不同点,是对象“私有”的。

看一段 js 创建对象的代码(注意注释部分):

function Person(name) {
  this.name = name
}
Person.prototype.吃饭 = function() {
  console.log('吃吃吃')
}
Person.prototype.睡觉 = function() {
  console.log('睡睡睡')
}

let 我 = new Person('我')
我.吃饭() // "吃吃吃"
console.log(Person.prototype === 我.__proto__) // true
// 可以看出,在实例化的过程中,类的 prototype 成为对象的原型  

let 你 = new Person('你')
你.睡觉() // "睡睡睡"
console.log(我.__proto__ === 你.__proto__) // true
// 同一类的多个实例(对象),共享一个原型?

我.__proto__.吃饭 = function() {
  console.log('再吃一点')
}
console.log(我.吃饭 == 你.吃饭) // true
你.吃饭() // "再吃一点"
// 没错,同一类的多个实例,共享一个原型

类比于人类社会,就是:

  • 你的兄弟姐妹和你“共享”一个爸爸
  • 当你的爸爸烫了个头时,你弟弟的爸爸也烫头。这个过程中,不是两个爸爸同时烫头,而是本来就一个爸爸。

重要结论:

  • 实例化的过程中(也就是“当 new 一个对象的时候”),类的 prototype 成为对象的原型
  • 同一个类的多个实例(也就是“对象”),共享一个原型

原型是 js 底层的东西,不懂原型,几乎不影响工作。

类似“原型有什么用”的问题,就像“砖块(或水泥)对盖楼有什么用”。其实在写代码的过程中,几乎不会用到原型的知识。但是如果遇到了问题、出现了 bug、性能优化时,底层的知识是肯定有大用途的。

~ 本文完,感谢阅读!

学习有趣的知识,结识有趣的朋友,塑造有趣的灵魂!

大家好,我是〖编程三昧〗的作者 隐逸王,我的公众号是『编程三昧』,欢迎关注,希望大家多多指教!


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK