Я придумал патч обезьяны, чтобы сделать то, что хотел, выставив функцию lockPrototype
в следующем фрагменте.
Единственная известная мне проблема заключается в том, что если какой-то другой код запускается перед моим кодом, то они могут получить исходные ссылки на Object.setPrototypeOf
и установщик из дескриптора Object.prototype.__proto__
и, таким образом, обойти мой monkey-patch , Но в большинстве случаев я думаю, что это сработает. У нативной реализации такой проблемы не будет.
Прежде чем я приму свой ответ, есть ли другие проблемы с ним?
Вот пример (часть, помеченная 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: