背景
近几天在读《你不知道的 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>’.那有解吗?我就是需要在新对象中有一个可修改的同名属性可以吗?答案是可以,使用
💡 上述特性确实令人奇怪,我的理解是 Javascript 语言是在使用过程中不断修订完善,所以会出现一些有点不太符合预期的特性。Object.defineProperty在far对象上面定一个count属性即可。 -
原型链属性是一个
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 语言里面就是长整形。修改引用本身就是修改一个长整形。
修改这个对象的属性呢?那就看这个对象的这个属性的配置了,依照上面的三种情况判断即可。