【问题标题】:Why is hasOwnProperty being invoked generically here?为什么在这里一般调用 hasOwnProperty?
【发布时间】:2017-05-19 04:28:09
【问题描述】:

Axel Rauschmayer 的 Speaking Javascript: An In-Depth Guide for Programmers 中提到了以下功能:

function getDefiningObject(obj, propKey) {
    obj = Object(obj); // make sure it’s an object
    while (obj && !{}.hasOwnProperty.call(obj, propKey)) {
        obj = Object.getPrototypeOf(obj);
        // obj is null if we have reached the end
    }
    return obj;
}

正如作者所说,它的目的是“[迭代]对象obj的属性链[并返回]第一个具有自己属性的对象,其键为propKey或@987654324 @如果没有这样的对象”。

我理解这里的整体推理,但我不明白为什么要完成 {}.hasOwnProperty.call(obj, propKey) 而不仅仅是 obj.hasOwnProperty(propKey)。有什么想法吗?

【问题讨论】:

  • 可能是obj 不是您想象的结构,而obj.hasOwnProperty 不是Object.prototype.hasOwnProperty

标签: javascript oop object


【解决方案1】:

通常,开发人员会在内置类型上使用call,以确保他们获得方法的正确本机行为,而不是某些被覆盖的行为。

这是一种我们真的不应该使用的保护措施,但由于 JavaScript 中的对象具有很强的可塑性,它保证了我们得到我们想要的行为。

想象一下,如果有人(有意或无意地)创建了这样的对象:

function SuperObject(){
   this.foo = function(){
     // Something here
   };

   this.hasOwnProperty = 42;
}

然后,你出现了(没有看到对象的实现)并写道:

var mySuperObject = new SuperObject();
console.log("Does 'mySuperObject' instance have its own 'foo' property? " +
            mySuperObject.hasOwnProperty("foo"));

你会得到这个:

function SuperObject(){
  this.foo = function(){
    // Something here
  };
    
  this.hasOwnProperty = 42;
}


var mySuperObject = new SuperObject();

// Safe way:
console.log("Does 'mySuperObject' instance have its own 'foo' property? " +
             {}.hasOwnProperty.call(mySuperObject, "foo"));

// Unsafe way (fails):
console.log("Does 'mySuperObject' instance have its own 'foo' property? " +
             mySuperObject.hasOwnProperty("foo"));

所以,我们得到这样的代码:

// This creates a new "fresh" (and unaltered) object with "object literal"
// syntax that you know doesn't override "hasOwnProperty"
{}.hasOwnProperty.call(obj, propKey)

// This performs the same task, but uses the native prototype object
// of the built-in Object type that we also know hasn't been altered:
Object.prototype.hasOwnProperty.call(obj, propKey) 

它强制在原生Object 的原型链上查找hasOwnProperty,同时:

obj.hasOwnProperty(propKey)

依赖于该属性在对象的特定实例上可用且正确 (obj)。

其他流行的例子是 Array 类型:

// These work the same as the Object examples above, but just 
// do it for the Array type:
[].forEach.call(); // Array literal syntax
Array.prototype.forEach.call(); // Explicit array syntax

【讨论】:

  • 伙计,JavaScript 搞砸了
  • @Sadiq 看起来确实是这样,但是一旦你了解它,它就会非常灵活和强大。
  • @Sadiq 如果您习惯于其他语言,请将其视为在基类上调用静态方法,而不是可以被覆盖的实例方法
  • 这个比喻有道理,我只是不知道为什么要这样做
  • 情况可能会更糟。它可能是 PHP。
【解决方案2】:

因为如果像这样定义对象,obj.hasOwnProperty 会严重下降:

var obj = { hasOwnProperty: 123 };
var obj = Object.create(null);

使用{}.hasOwnProperty,您可以从Object.prototype 获得对所需函数的引用,您无需担心它会在原型链中的某个位置被遮蔽,或者该对象不是从Object.prototype 继承的。

这是一个设计问题,因为hasOwnProperty本来应该是一个静态方法,但是现在修复它已经来不及了。另一种解决方案是手动实现HasOwnProperty

Object.getOwnPropertyDescriptor(obj, prop) !== undefined

【讨论】:

    【解决方案3】:

    如果您想在一个对象上执行Object.prototype.hasOwnProperty,并且您尝试使用obj.hasOwnProperty(prop),那么您正在做出一些假设:

    • 对象有一个名为"hasOwnProperty" 的键。如果您使用Object.create(null) 创建对象,这很容易失败。
    • 对象在obj.hasOwnProperty 处具有函数。如果对象有一个已添加名为"hasOwnProperty" 的键,这很容易失败。您可能认为这是一种罕见的情况,但将查询字符串解析为对象等内容并不少见,并且查询字符串是用户提供的。

    使用Object.prototype.hasOwnProperty.call(obj, prop),您可以合理地保证在作为输入传入的对象上调用您想要的方法。

    出于实用目的,通常会看到以下内容:

    function has(obj, prop) {
      return Object.prototype.hasOwnProperty.call(obj, prop);
    }
    

    【讨论】:

    • 我考虑了第一个要点,但请注意那里有一个 obj && 警卫
    • @Sadiq,Object.create(null) 不是null,它是一个没有原型链且真实的对象,所以obj && 将不起作用。
    • 那他为什么要这么做?
    • @Sadiq, obj && 根据您提出的问题的上下文,对传入的任何对象都没有影响。 null 可以被传入,这就是obj && 的保护。
    猜你喜欢
    • 2017-07-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多