再解 JavaScript 原型与原型链

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

5.png


前言


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、性能优化时,底层的知识是肯定有大用途的。


~ 本文完,感谢阅读!


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




相关文章
|
2月前
|
JavaScript 前端开发
js开发:请解释原型继承和类继承的区别。
JavaScript中的原型继承和类继承用于共享对象属性和方法。原型继承通过原型链实现共享,节省内存,但不支持私有属性。
27 0
|
26天前
|
设计模式 JavaScript 前端开发
【JavaScript】深入浅出JavaScript继承机制:解密原型、原型链与面向对象实战攻略
JavaScript的继承机制基于原型链,它定义了对象属性和方法的查找规则。每个对象都有一个原型,通过原型链,对象能访问到构造函数原型上的方法。例如`Animal.prototype`上的`speak`方法可被`Animal`实例访问。原型链的尽头是`Object.prototype`,其`[[Prototype]]`为`null`。继承方式包括原型链继承(通过`Object.create`)、构造函数继承(使用`call`或`apply`)和组合继承(结合两者)。ES6的`class`语法是语法糖,但底层仍基于原型。继承选择应根据需求,理解原型链原理对JavaScript面向对象编程至关重要
33 7
【JavaScript】深入浅出JavaScript继承机制:解密原型、原型链与面向对象实战攻略
|
17天前
|
JavaScript
js奥义:原型与原型链(1)
js奥义:原型与原型链(1)
|
27天前
|
JavaScript 前端开发
JavaScript进阶-原型链与继承
【6月更文挑战第18天】JavaScript的原型链和继承是其面向对象编程的核心。每个对象都有一个指向原型的对象链,当查找属性时会沿着此链搜索。原型链可能导致污染、效率下降及构造函数与原型混淆的问题,应谨慎扩展原生原型、保持原型结构简洁并使用`Object.create`或ES6的`class`。继承方式包括原型链、构造函数、组合继承和ES6的Class继承,需避免循环引用、方法覆盖和不当的构造函数使用。通过代码示例展示了这两种继承形式,理解并有效利用这些机制能提升代码质量。
|
10天前
|
JavaScript C++
js【详解】原型 vs 原型链
js【详解】原型 vs 原型链
8 0
|
24天前
|
JavaScript 前端开发
JS原型链
JS原型链
15 0
|
2月前
|
前端开发 JavaScript
前端 js 经典:原型对象和原型链
前端 js 经典:原型对象和原型链
32 1
|
2月前
|
JavaScript 前端开发
在JavaScript中,函数原型(Function Prototype)是一个特殊的对象
【5月更文挑战第11天】JavaScript中的函数原型是一个特殊对象,它为所有函数实例提供共享的方法和属性。每个函数在创建时都有一个`prototype`属性,指向原型对象。利用原型,我们可以向所有实例添加方法和属性,实现继承。例如,我们定义一个`Person`函数,向其原型添加`greet`方法,然后创建实例`john`和`jane`,它们都能调用这个方法。尽管可以直接在原型上添加方法,但推荐在构造函数内部定义以封装数据和逻辑。
35 2
|
2月前
|
JavaScript 前端开发
前端 JS 经典:原型和原型链
前端 JS 经典:原型和原型链
28 0
|
2月前
|
JavaScript 前端开发
JavaScript 原型链继承:掌握面向对象的基础
JavaScript 原型链继承:掌握面向对象的基础