【问题标题】:Was there a way to create an object without a prototype prior to ES5?在 ES5 之前有没有办法在没有原型的情况下创建对象?
【发布时间】:2016-07-07 03:25:34
【问题描述】:

在 ES5 之前有没有办法在没有原型的情况下创建对象?

即类似Object.create(null) (ES5)

我认为这样的事情可能会起作用,但最终语句意外返回true

function withPrototype(p) {
  function temp(){}
  temp.prototype = p;
  return new temp();
}

Object.getPrototypeOf(withPrototype(null)) === Object.prototype; // true

Object.getPrototypeOf 是 ES5。我在这里用它来展示。

【问题讨论】:

标签: javascript ecmascript-3


【解决方案1】:

没有。在ES3 spec 中搜索[[Prototype]] 表明,将[[Prototype]] 更改为任意给定值的唯一方法是通过[[Construct]]。

但是,它仅在该值是对象时才有效。如果不是(包括null),[[Prototype]] 将被设置为Object.prototype 的初始值。

13.2.2 [[构造]]

当函数对象 F 的 [[Construct]] 属性被调用时, 采取以下步骤:

  1. 创建一个新的原生 ECMAScript 对象。
  2. 将 Result(1) 的 [[Class]] 属性设置为 "Object"
  3. 获取F的prototype属性值。
  4. 如果 Result(3) 是对象,请将 Result(1) 的 [[Prototype]] 属性设置为 Result(3)。
  5. 如果 Result(3) 不是对象,请将 Result(1) 的 [[Prototype]] 属性设置为原始 Object 原型对象,如中所述 15.2.3.1.
  6. 调用 F 的 [[Call]] 属性,提供 Result(1) 作为 this 值,并提供传入 [[Construct]] 的参数列表作为参数值。
  7. 如果 Type(Result(6)) 是 Object,则返回 Result(6)。
  8. 返回结果(1)。

【讨论】:

  • 那么 MDN 的 polyfill 不能为 null 工作? developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
  • @BenAston 是的,它不适用于null。而不是错误的typeof prototype != 'object',它过滤掉可调用对象并且不过滤掉null,polyfill应该检查Object(prototype) === prototype。这是检查某物是否为对象的可靠方法。
  • @BenAston:哦,天哪,有人missed the reason and threw it out
  • 注意:他们可能使用了typeof,希望它能过滤掉宿主对象,因为在 [[Prototype]] 中是否支持它们取决于实现。但是typeof hostObject 的结果是依赖于实现的,所以不能保证它们会被过滤掉。 Object(prototype) === prototype 不会过滤掉它们。
  • @BenAston typeof 的问题在于可调用对象是对象,但 typeof 表示 "function"Object(prototype) === prototype 是测试某物是否为对象的更好方法。因此,如果你传递一个可调用对象,polyfill 会抛出一个错误,但它能够正确处理这种情况。
【解决方案2】:

正如@Oriol 所表明的,没有“官方”(符合规范)的方式来做到这一点。

但是,确实有一个对象没有原型——Object.prototype 本身。

15.2.4 对象原型对象的属性

Object原型对象内部[[Prototype]]属性的值为null,内部[[Class]]属性的值为"Object"

您可以通过实例化一个新环境(ES6 术语中的“领域”)来潜在地“创建”这样一个对象,例如通过<iframe>,捕获它的Object.prototype,剥离它的属性,瞧——你得到了一个新的空对象。

function getNoProtoObject(callback) {
  var iframe = document.createElement('iframe');
  iframe.onload = function() {
    var obj = iframe.contentWindow.Object.prototype;
    document.body.removeChild(iframe);

    // Remove all built-in enumerable properties.
    for (var name in obj) {
      delete obj[name];
    }

    // Remove known built-in non-enumerable properties, which may vary.
    delete obj['constructor'];
    delete obj['hasOwnProperty'];
    delete obj['isPrototypeOf'];
    delete obj['propertyIsEnumerable'];
    delete obj['toLocaleString'];
    delete obj['toString'];
    delete obj['toSource'];
    delete obj['valueOf'];
    delete obj['watch'];
    delete obj['unwatch'];
    delete obj['__defineGetter__'];
    delete obj['__defineSetter__'];
    delete obj['__lookupGetter__'];
    delete obj['__lookupSetter__'];
    delete obj['__proto__'];

    callback(obj);
  };
  iframe.src = 'about:blank';
  iframe.style.display = 'none';
  document.body.appendChild(iframe);
}

getNoProtoObject(function(o) {
  console.log(o);                        // Object {  }
  console.log(Object.getPrototypeOf(o)); // null
  console.log(o.__proto__);              // undefined
});

【讨论】:

  • 虽然 Oriol 的答案包含了对我最有用的信息,但这个答案似乎是最终答案。
  • 这种方法有什么不符合规范的?
  • @BenAston:ES3 对 iframe 一无所知。它是一个 DOM API,而不是 ES API——当然,你总是可以拥有一个带有宿主对象的 ES 实现,它们可以做任何他们想做的事情,所以没有什么是“不可能的”:-)
  • 问题可能是如果某些实现向Object.prototype 添加了额外的自己的不可枚举属性(不确定这是否会违反规范)。即使可配置,由于不可枚举和缺少Object.getOwnPropertyNames,也很难删除它们。
  • 同步即可:var iframe = document.createElement('iframe'); document.body.appendChild(iframe); var obj = iframe.contentWindow.Object.prototype; document.body.removeChild(iframe);
猜你喜欢
  • 1970-01-01
  • 2012-07-25
  • 2017-07-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多