原型链上的属性是否可以被修改?
date
Feb 28, 2022
slug
原型链上的属性是否可以被修改?
status
Published
tags
JavaScript
summary
type
Post
背景
近几天在读《你不知道的 Javascript》,读到一个之前理解有点偏差的知识点:原型链上的属性是否可以被修改?
我之前的理解是:会在对象本地创建这个属性并覆盖原型链上的属性。读到书中相关章节发现,这个理解不够全面,还需要考虑原型链上属性的设置,具体整理如下:
修改原型链属性的几种情况
- 原型链属性
writable = true
这种情况是最常见的,也是我之前理解的那样。看示例:
const foo = {
count: 1,
}
const far = Object.create(foo)
console.log(far.count) // 1
console.log(Object.getOwnPropertyNames(far)) // []
far.count = 2
console.log(foo.count) // 1
console.log(far.count) // 2
console.log(Object.getOwnPropertyNames(far)) // [ 'count' ]
总结一下:读的时候,读取的原型链上的属性;修改的时候会在本对象上定义一个同名属性,并且屏蔽掉原型链上的属性。
- 原型链属性
writable = false
这种情况,大多数人估计也没碰到(我就是),直接看示例:
const foo = {
count: 1,
}
// 这里修改了 writable 配置
Object.defineProperty(foo, 'count', {
writable: false,
})
const far = Object.create(foo)
console.log(far.count) // 1
console.log(Object.getOwnPropertyNames(far)) // []
far.count = 2
console.log(foo.count) // 1
console.log(far.count) // 1
console.log(Object.getOwnPropertyNames(far)) // []
可以看到,修改是没有生效的!
⚠️ 另外请注意,如果在严格模式
'use strict';
下,上面的代码还会报错:TypeError: Cannot assign to read only property 'count' of object '#<Object>’.
那有解吗?我就是需要在新对象中有一个可修改的同名属性可以吗?答案是可以,使用
Object.defineProperty
在 far
对象上面定一个 count
属性即可。上述特性确实令人奇怪,我的理解是 Javascript 语言是在使用过程中不断修订完善,所以会出现一些有点不太符合预期的特性。
- 原型链属性是一个
setter
这种情况也是很少碰到,设置了
setter
和 getter
可能原型对象上并没有这个属性,但是可以读取和设置。看示例:const foo = {
count: 1,
set rule(val) {
console.log(`set rule ${val}`)
}
}
Object.defineProperty(foo, 'count', {
writable: false,
})
const far = Object.create(foo)
far.rule = 'limit 1' // set rule limit 1
console.log(far.rule) // undefined
上面这个只设置了
setter
没有设置 getter
,可以看出来,并没有在本对象上面定义同名属性去覆盖。最后,我再补充说明一下,上述三种情况,属性都是基本类型,如果这个属性是对象会怎么样?
其实对象属性本身存的是一个引用,可以看成指针,指针存储的就是一个地址,类比一下:c 语言里面就是长整形。修改引用本身就是修改一个长整形。
修改这个对象的属性呢?那就看这个对象的这个属性的配置了,依照上面的三种情况判断即可。