原型和原型链的理解_想理解JavaScript原型链,你得先知道原型(prototype)是什么...

本文借鉴medium 中的一篇文章 (Prototypes in JavaScript)

https://medium.com/better-programming/prototypes-in-javascript-5bba2990e04b​medium.com

之前看过很多文章,都没有通俗易懂的把原型和原型链介绍清楚,甚至让人看得云里雾里。本人第一次写文章,有错误的地方请指正。

构造器函数(Constructor Function)问题

相信大家对JavaScript 的构造器函数已经很熟悉了,下面代码将声明一个构造器函数以及通过 "new" 表达式创造两个Human实列。

function 

151718ebc6e2d002b25228df89f6ab4b.png
两个对象在内存中的储存

代码运行后JavaScript 引擎会创建两个的Human实列并分配内存空间,而其实这两个实列中 "name", "age" 的值都是相等的,“sayName" 函数也拥有同样的功能,这样的操作无疑是对内存的浪费。

原型对象(Prototype Object)

了解了构造函数后,我们再来了解一下原型对象。

每当JavaScript 的函数 (注意不是实列,这里为Human函数)被创建后,JavaScript引擎会添加一个名为 "prototype"的属性到这个函数对象中。那这个属性到底有啥用呢?

首先, ptototype属性会指向一个对象,这个对象我们称为原型对象Prototype Object.

这个对象中有一个名为 'constructor'的属性, 这个属性又指向原本的函数对象。

49da7182cb45e88726001158d8908354.png

你可以通过 Human.prototype 来访问原型对象。(__proto__)这里不做讨论

0bc1d1117ddda6973b4317687da43528.png

你可能会奇怪,为啥要创建这个原型对象? 针对之前的构造器函数问题,你是不是有些头绪了呢?

通过构造函数创造对象时,他们三者的关系如下:

5474d4a085c84ad135c76e7212eb3066.png

可以预想到, person1._proto_和 Human.prototype 的值是相等的,因为他们指向的是同一对象。

Human.prototype === person1.__proto__ //true

创建第二个对象:

03b71ac38fe6902978d4b11e8b450601.png

所以说, 原型对象是所有实列共享的。

原型对象所存在的问题

我们可以往原型对象里添加,删除,修改属性。 例如

Human.prototype.sayHello= function () {
              console.log("hello")
}

Human.prototype.gender = "male" 

let person = new Human()
console.log(person.gender) // 输出 "male"

let person1 = new Human()
console.log(person1.gender) //输出 "male"

到这里就碰到了另外一个问题, 既然原型对象是所有实列共享的, 那么当某个实列修改原型属性时会对所有的实列产生影响吗??

答案是: 修改值类型的时候不会产生影响, 修改引用类型的时候会产生影响。

当我们使用JS里对象的属性时,JS引擎会首先搜寻对象本身的属性,如果没有,再按照原型链搜寻原型对象里面的属性。

当属性是值类型时

function Human() {
}
Human.prototype.gender = "male" 

let person1 = new Human()
console.log(person.gender) // 输出 "male"

let person2 = new Human()
console.log(person1.gender) //输出 "male"

person.gender = "female"

console.log(person1.gender) // 输出 "female"

console.log(person2.gender) //输出 "male"

当person1修改gender属性时, 会在本身对象内创建一个新的属性 gender, 并将其值设为 "female" 所以不会影响person2。

当属性是引用类型时

function Human() {

}

Human.prototype.friends = ["kelly", "joe", "kian"]

let person1 = new Human()
let person2 = new Human()

console.log(person1.friends) //["kelly", "joe", "kian"]
console.log(person2.friends) //["kelly", "joe", "kian"]

person1.friends.push("yu")


console.log(person1.friends) //["kelly", "joe", "kian","yu"]
console.log(person2.friends) //["kelly", "joe", "kian","yu"]

上面列子中,因为在Human的原型对象中定义了一个数组对象 "friends" ,因为数组是引用类型,所以在其中一个实列中修改引用类型的原型属性会影响其他的实列。

解决方法

先总结一下上面碰到的问题:

  1. 构造器函数问题:用构造器创建的每个对象都会拥有一份函数对象的实列,造成内存浪费。
  2. 原型问题:在其中一个对象中修改原型对象的引用类型的属性,会影响其他对象。

解决方法是: 将公用的方法放在原型中。

function Human (name, age) {
    this.name = name
    this.age = age
    this.firends = ['Kian', 'Joe']
}

Human.prototype.sayHello = function () {
  console.log(`Hi I am ${this.name} I am ${this.age} years old`)
}

const person1 = new Human("Jeff",22)
const person2 = new Human("Wang",25)

console.log(person1.sayHello === person2.sayHello) //true

person1.firends.push("Xiao")

console.log(person1.firends) //['Kian', 'Joe',"xiao"]
console.log(person2.firends) // ['Kian', 'Joe']
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值