为什么hasOwnProperty不能识别对象原型上的函数?

15

我知道JavaScript中的hasOwnProperty方法存在的目的是为了仅识别当前类型的属性,但这里原型链上的某些内容让我感到困惑。

让我们假设我定义了一个名为Bob的类型,并以两种不同的方式为Bob类型分配了两个子函数:

function Bob()
{
    this.name="Bob"; 
    this.sayGoodbye=function()
    {
        console.log("goodbye");   
    }
}

Bob.prototype.sayHello= function()
{
    console.log("hello");   
}

除了在sayGoodbye中访问闭包作用域之外,我觉得属于Bob类的这两个函数应该差不多。然而,当我使用hasOwnProperty查找它们时,就JavaScript而言它们并不相同:

var myBob = new Bob();
console.log( myBob.name ); // Bob, obviously 
console.log( myBob.hasOwnProperty("sayHello"));  // false
console.log( myBob.hasOwnProperty("sayGoodbye")); // true
console.log( "sayHello" in myBob ); // true
这里的作用域是什么?如果没有将 sayHello()sayGoodbye() 属性连接到 Bob 类型上,就无法创建 Bob 类型的实例,那么为什么原型方法在 hasOwnProperty 中被视为第二类公民?Bob.prototype 是一种独立于 Bob 类型存在的类型吗?从中继承了所有东西?

3
hasOwnProperty 只存在于检查对象本身是否具有属性,它明确不会沿着原型链向上查找,因为那样会失去其目的。 - adeneo
1
@glenatron - 真正的答案是因为hasOwnProperty()就是这样设计的。它只检查对象本身的属性,而不会在原型链中查找。我可以理解需要一个不同的函数来告诉你属性是在对象上还是在它自己的原型中,但这不是.hasOwnProperty()的设计初衷。你可以编写这样的函数。 - jfriend00
@jfriend00 - 对于大多数情况,typeof 已经足够使用了。但是,如果需要遍历[[Prototype]]链,则依赖于其中一个getPrototyeOf__proto__constructor属性和constructor.prototoype准确无误。但我并没有看到除了 typeof 提供的好处外,还有其他的好处。 - RobG
@RobG - 这取决于OP真正想做什么。我猜如果他们解释了实际问题,可能有更好的方法完全解决他们的问题。 - jfriend00
2
@glenatron——第一步是认识到作用域与属性访问无关。前者涉及在词法环境中解析变量,而后者涉及对象及其[[Prototype]]链上的属性名称解析。 - RobG
显示剩余5条评论
2个回答

14

我觉得你在混淆几个概念。来看一下MDN中的这句话:

每一个从 Object 继承来的对象都继承了hasOwnProperty方法。 这个方法可以用来判断一个对象是否有指定的属性作为该对象的直接属性;与in 操作符不同,这个方法不会检查对象原型链中的属性

所以这是关键。当你使用new时,JavaScript会将全新的对象分配给this并返回它,这就是实例。在构造函数内声明的任何属性都是自有属性。声明在prototype上的属性不是自有属性,因为它们与相同对象的其他实例共享。

prototype也是一个Object,例如:

Bob.prototype.hasOwnProperty("sayHello"); //=> true

myBob.constructor.prototype.hasOwnProperty("sayHello"); //=> true

1
这是否意味着我可以实例化一个新的 Bob.prototype,它将可以访问 Bob 类型的所有共享属性,但不包括其构造函数?假设我足够醉,以至于这似乎是个好主意。 - glenatron
1
是的,这就是继承的实现方式:NewObject.prototype = Object.create(Bob.prototype) - elclanrs
@glenatron - Bob.prototype已经存在为一个对象。你不需要实例化它来读取其中的内容。 - jfriend00
@elclanrs,这不是我实现继承的方式,但很可能这才是我应该实现继承的方式。通常我只会说James.prototype = new Bob() - glenatron
嗯,是的,在旧浏览器中这样做是可行的,但从某种程度上来说有点不太正规。在这里检查polyfill https://developer.mozilla.org/en- - elclanrs

4

我了解JavaScript中的hasOwnProperty方法仅用于识别当前类型的属性。

那是不正确的。hasOwnProperty用于识别对象的自有属性,即对象本身的属性。它不考虑对象[[Prototype]]链上的继承属性。

例如:

var foo = {name: 'foo'};

// Check for an own property
foo.hasOwnProperty('name'); // true

对象foo也从Object.prototype继承了toString方法,但这不是它的“自有”属性:

typeof foo.toString             // function
foo.hasOwnProperty('toString'); // false

关于更长的继承链,我明白了。我具体困惑的是,当一个属性通过 foo.prototype.name 声明时,该属性仅存在于类型 foo 及其子类中。虽然“自有”属性的定义解释了我的错误之处,但如果所有唯一属于 foo 及其子类的属性都被视为 foo 的属性而不仅限于函数声明内部的属性,这并不是不合逻辑的。 - glenatron

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接