我可以强制不更改对象的原型吗?
注意! 有一些要求:
因此,除了原型是永久性的之外,我不想添加任何其他限制(Object.seal/freeze/preventExtensions
之类的工具对对象施加了更多限制)。
为了实现这一目标,我是否必须对Object.prototype.__proto__
和Object.setPrototypeOf
进行猴子补丁?
答案 0 :(得分:4)
一个选项是Object.preventExtensions() (请注意,这会将整个对象锁定为扩展名,而不是仅锁定原型而不被修改):
'use strict';
const obj = {};
Object.preventExtensions(obj);
Object.setPrototypeOf(obj, { possibleNewPrototype: 'foo' });
'use strict';
const obj = {};
Object.preventExtensions(obj);
obj.__proto__ = { possibleNewPrototype: 'foo' };
答案 1 :(得分:0)
我想出了一个猴子补丁来做我想要的事情,在下面的代码片段中公开了一个lockPrototype
函数。
我知道的唯一问题是,如果某些其他代码在我的代码之前运行,那么它们可以从Object.setPrototypeOf
描述符中获取对Object.prototype.__proto__
和setter的原始引用,从而可以解决我的猴子补丁。但是对于大多数情况,我认为它会起作用。本机实现不会有此问题。
在我接受自己的答案之前,它还有其他问题吗?
这是示例(标记为lockPrototype.js
的部分是实现,标记为test.js
的部分是如何使用):
// lockPrototype.js /////////////////////////////////////////////////////
const oldSetPrototypeOf = Object.setPrototypeOf
const lockedObjects = new WeakSet
Object.setPrototypeOf = function(obj, proto) {
if (lockedObjects.has(obj))
throw new TypeError("#<Object>'s prototype is locked.")
oldSetPrototypeOf.call(Object, obj, proto)
}
const __proto__descriptor = Object.getOwnPropertyDescriptor(Object.prototype, '__proto__')
Object.defineProperty(Object.prototype, '__proto__', {
...__proto__descriptor,
set(proto) {
if (lockedObjects.has(this))
throw new TypeError("#<Object>'s prototype is locked.")
__proto__descriptor.set.call(this, proto)
},
})
// export
function lockPrototype(obj) {
// this behavior is similar to Object.seal/freeze/preventExtensions
if (typeof obj !== "object" && typeof obj !== "function")
return obj
lockedObjects.add(obj)
}
// test.js //////////////////////////////////////////////////////////////
// import {lockPrototype} from './lockPrototype'
const a = {}
const b = {}
const c = {}
const proto = { n: 5 }
lockPrototype(b)
lockPrototype(c)
Object.setPrototypeOf(a, proto) // works fine
console.log('a.n', a.n) // 5
setTimeout(() => {
console.log('b.n:', b.n) // undefined
setTimeout(() => {
console.log('c.n', c.n) // undefined
})
c.__proto__ = proto // throws
})
Object.setPrototypeOf(b, proto) // throws
(提琴:https://jsfiddle.net/trusktr/Lnrfoj0u)
您应该在Chrome devtools控制台中看到的输出是: