如何使JavaScript对象的原型永久化?

时间:2019-04-14 19:01:02

标签: javascript prototype prototype-programming prototype-chain

我可以强制不更改对象的原型吗?

注意! 有一些要求:

  • 对象的行为应与常规对象文字一样(在对象上添加/删除/配置/修改属性和描述符)
  • 字面上仅 个新限制是原型是永久性的。

因此,除了原型是永久性的之外,我不想添加任何其他限制(Object.seal/freeze/preventExtensions之类的工具对对象施加了更多限制)。

为了实现这一目标,我是否必须对Object.prototype.__proto__Object.setPrototypeOf进行猴子补丁?

2 个答案:

答案 0 :(得分:4)

一个选项是Object​.prevent​Extensions() (请注意,这会将整个对象锁定为扩展名,而不是仅锁定原型而不被修改):

'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控制台中看到的输出是:

enter image description here