How to make a JavaScript object's prototype permanent?

回眸只為那壹抹淺笑 提交于 2019-12-10 15:16:11

问题


Can I enforce that the prototype of an object not be changed?

Note! There are some requirements:

  • The object should behave just like a regular object literal (add/remove/configure/modify properties and descriptors on the object),
  • with literally the only new limitation being that the prototype is permanent.

So other than the prototype being permanent, I don't want to add any other limitations (tools like Object.seal/freeze/preventExtensions impose more limitations on the object).

Would I have to monkey-patch 🐒 Object.prototype.__proto__ and Object.setPrototypeOf in order to achieve this?


回答1:


One option is Object​.prevent​Extensions() (note, this locks the whole object from extensions, doesn't lock only the prototype from being modified):

'use strict';
const obj = {};
Object.preventExtensions(obj);
Object.setPrototypeOf(obj, { possibleNewPrototype: 'foo' });

'use strict';
const obj = {};
Object.preventExtensions(obj);
obj.__proto__ = { possibleNewPrototype: 'foo' };



回答2:


I came up with a monkey-patch to do what I wanted, exposing a lockPrototype function in the following snippet.

The only problem that I am aware of is that if some other code runs before my code, then they can get original references to Object.setPrototypeOf and the setter from the Object.prototype.__proto__ descriptor, and thus work around my monkey-patch. But for most cases I think it would work. A native implementation wouldn't have this problem.

Before I accept my own answer, are there any other problems with it?

Here's the example (the part labeled lockPrototype.js is the implementation, and the part labeled test.js is how it could be used):

// 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

(fiddle: https://jsfiddle.net/trusktr/Lnrfoj0u)

The output that you should see in Chrome devtools console is:



来源:https://stackoverflow.com/questions/55678953/how-to-make-a-javascript-objects-prototype-permanent

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!