【问题标题】:hasOwnProperty returns true, when checked against parent object propertieshasOwnProperty 在检查父对象属性时返回 true
【发布时间】:2015-03-14 21:07:48
【问题描述】:

我的 JavaScript 代码:

console.clear();

function BaseClass(nname) {
  var name = nname;

  this.bc_PublicProperty = "DefaultValue_BaseClass";

  this.bc_getName = function GetName() {
    return name;
  };

  this.bc_setName = function SetName(nname) {
    name = nname;
  };
}

function SubClass(nname) {
  BaseClass.call(this, nname);

  this.sc_PublicProperty = "DefaultValue_SubClass";

  this.sc_getName = function GetName() {
    return name;
  };

  this.sc_setName = function SetName(nname) {
    name = nname;
  };
}

SubClass.prototype = Object.create(BaseClass.prototype);
SubClass.prototype.constructor = SubClass;

var bc = new BaseClass("Anton");
var sc = new SubClass("Bernd");

console.log("hasOwnProperty check on subclass object 'sc' returns true for Method 'bc_getName'");

for (var pro in sc) {
  console.log("Is " + pro + " own property of subclass:  --> " + Object.hasOwnProperty.call(sc, pro));

}

我有两个对象(BaseClass 和 SubClass)。一个使用构造函数模式从另一个继承,如explained on MDN

现在,当我遍历子类对象的所有属性时,它们都为 hasOwnProperty 返回 true,即使对于父方法/属性,除了构造函数。

这是否意味着在使用构造函数模式时它会中断?

当我将公共属性和方法放在构造函数中时,无论是在 BaseClass 还是 SubClass 中,它们都将始终被“标记”为自己的属性/方法。当我将它们附加到各自的原型时,hasOwnProperty 将为它们输出“false”。

无论您将公共方法/属性放在原型还是构造函数本身,它们都可以在子类(--> SubClass2,--> SubClass3)中使用。

我现在唯一能想到的,为什么要将它们附加到原型对象,是因为效率原因,正如here,“定义类方法”-部分所述。为了不为每个构造的实例添加闭包。

值类型应该在原型上声明,但不是实例变量,其初始值取决于构造函数的参数,或构造时的其他状态。 无论声明的位置如何,您都可以覆盖这两个属性/函数。

同时在原型上设置 getter 和 setter,例如设置或获取私有变量,是没有意义的,因为私有变量必须是公共的,以便附加到原型的 getter 和 setter 可以访问.

因此,使用 getter 和 setter 是没有意义的。您可以直接访问公共变量。

我现在必须稍微调整一下我的问题:

我什么时候需要hasOwnProperty,如果实际上应该在原型上声明公共props/functions,这反过来将全部输出Object.hasOwnProperty(obj,"prop/func") --> false。给我一个用例,在有意义的时候使用 hasOwnProperty。

console.clear();

var l = function(v) {
  console.log(v);
};

function BaseClass(nname) {

  this.bc_nameProp = nname;
  this.bc_ownFunc = function() {
    l("bc_ownFunc of BaseClass called");
  };

  this.bc_getName = function GetName() {
    return this.bc_nameProp;
  };
}

BaseClass.prototype.bc_getName = function GetName() {
  return this.bc_nameProp;
};

BaseClass.prototype.bc_PublicProperty = "DefaultValue_BaseClass";

BaseClass.prototype.bc_setName = function SetName(nname) {
  bc_nameProp = nname;
};

function SubClass(nname) {

  BaseClass.call(this, nname);

  this.sc_setName = function SetName(nname) {
    bc_nameProp = nname;
  };

  this.bc_getName = function GetName() {
    return "xyz";
  };

}

SubClass.prototype = Object.create(BaseClass.prototype);
SubClass.prototype.constructor = SubClass;

SubClass.prototype.sc_getName = function GetName() {
  return this.bc_nameProp;
};
SubClass.prototype.sc_PublicProperty = "DefaultValue_SubClass";

var bc = new BaseClass("Anton");
var sc = new SubClass("Bernd");

l("-----  iterating over BaseClass properties  ------");
l("");
for (var pro in bc) {
  l("Is " + pro + " own property of BaseClass:  --> " + Object.hasOwnProperty.call(bc, pro));

}
l("");
l("-----  iterating over SubClass properties  ------");
l("");
for (var p in sc) {
  l("Is " + p + " own property of subclass:  --> " + Object.hasOwnProperty.call(sc, p));

}

l(bc.bc_getName());
l(sc.bc_getName());
l(sc.sc_getName());

解决方案

当我问这个问题时,我的观点是,在 JavaScript 中使用经典继承时,我可以使用 hasOwnProperty 区分我的子类的哪些属性/函数直接属于它。这是不可能的,因为父原型的所有属性/功能都被复制到子类的原型中:

SubClass.prototype = Object.create(BaseClass.prototype);

使用 hasOwnProperty 时,附加到原型的所有属性/函数都返回“false”。

如果您在 BaseClass 和 SubClass 的构造函数中声明了公共属性/函数,则在为子类上的这些属性调用 hasOwnProperty 时,所有这些属性都会返回“true”。

使用以下语句将这些复制到子类:

BaseClass.call(this, nname);

因此,在迭代 SubClass 类型的 obj 的所有属性时使用 hasOwnProperty,将对原型级别声明的所有属性/函数输出 false,对构造函数级别声明的所有属性/函数输出 true。

现在,我明白了为什么在这个用例中使用 hasOwnProperty 没有意义。

【问题讨论】:

  • 你可以嵌入sn-ps!我已删除链接并将您的代码复制粘贴为 sn-p ;)
  • 正如答案中指出的那样;你没有使用原型,只在原型、构造函数和继承上设置了更多的实例特定成员:stackoverflow.com/a/16063711/1641941
  • 这个演示只是为了展示子类对象的所有属性/函数的hasOwnProperty函数的结果。 sn-p 也包含错误,因为我来回移动代码。但它仍然显示了我想要显示的内容 ;-)
  • @MatíasFidemraizer,我已经更新了我的问题..
  • @Legends 我在我的回答中添加了一个更新,顺便说一句,你一次问自己很多事情,而且我相信你正在努力寻找三足猫。 . :D

标签: javascript prototypal-inheritance hasownproperty coding-efficiency


【解决方案1】:

检查您是否在SubClass 的构造函数上调用BaseClass.call(this),这意味着您将BaseClass 属性和函数添加到SubClass 实例,因为this SubClass.

这就是hasOwnProperty 为所有属性返回true 的原因。

错误的原型...

说到底,您并没有利用 JavaScript 中的原型。

必须属于某个原型的任何实例的函数应该在原型本身中定义。

    var A = function() {};

    A.prototype = {
       doStuff: function() {}
    };

实际上,在构造时定义函数的唯一好处是您可以确保对象始终定义一些函数/属性,但是一旦对象已经创建,您就可以确保这一点。

对我来说,以下定义属性的方式之间存在非常小的差异:

var A = function() {};
var instance = new A();
instance.text = "Hello, World!";

// or...

var A = function() {
    this.text = "Hello, World!";
};

var instance = new A();

第一个代码示例在构造函数被调用后定义了一个text 属性,而第二个在构造函数内部执行它,但在这两种情况下thisinstance 都是对同一对象的引用(即A的实例)。

使用原型,您可以确保从某个构造函数创建的所有对象都将共享相同的成员,并且这些成员将被继承并使用prototype chain 使用。

关于 OP 的更新...

OP说了很多,但总结说:

[...] 我现在必须稍微调整一下我的问题:我什么时候需要 hasOwnProperty,如果实际上应该声明公共道具/功能 在原型上,这反过来将全部输出 Object.hasOwnProperty(obj,"prop/func") --> false。给我一个用例, 在有意义的情况下,使用 hasOwnProperty。

你走错了路……为什么要问自己什么时候需要hasOwnProperty问自己什么时候需要可重用性较低的简单对象,或者什么时候需要实际的可重用性。 hasOwnProperty与此问题无关。

当您使用文字对象时(即使用{} 语法声明的原样)?当您需要字典、参数映射、值对象时...这里您喜欢hasOwnProperty,因为通常您接收参数的代码将如下所示:

function X(uri, options) {
   if(typeof options != "undefined") {
       // For example, we set a default value if options has no
       // mimeType property...
       options.mimeType = options.hasOwnProperty("mimeType") ? options.mimeType : "application/json";
   }
}

您何时使用原型来使用复杂对象?当您定义行为并且您需要在您的应用程序甚至多个应用程序中重用它时,您还需要在通用需求之上添加更多行为(hello inheritance)。

关于为什么使用hasOwnProperty...

OP 说:

但是,如果您想检查 财产的存在?不应该是:options.mimeType = 选项.mimeType || “应用程序/json”;

网上有很多代码在做`options.mimeType =

options.mimeType || “应用程序/json”, right, because in JavaScript undefinedevaluates tofalse(ifoptionsdoesn't own amimeTypeproperty returnsundefined`)。

以我的拙见,我会使用hasOwnProperty,因为它返回一个boolean,如果它存在或者它存在并且它具有未定义的值。

例如,选项可以定义为{ mimeType: undefined },有时您想知道该属性是否存在,即使它的值是undefinedundefined as false 处理 如果它未定义,无论是否存在,都执行 X 的情况,而 hasOwnProperty我想确保它具有该属性与否

那么我为什么要使用options.hasOwnProperty 而不是其他方法?。简单:因为,既然语言提供了一个工具来验证某个属性是否存在于某个对象中,我为什么需要一个技巧? object.hasOwnProperty 返回truefalse,我确定该属性是否存在,即使该属性具有undefined 值。

使用options.hasOwnProperty("mimeType"),我可以抛出一个Error,如果它存在并且它有一个undefined 值。为什么我更喜欢这个?因为我喜欢 fail-fast 概念:如果你给了我undefined 值的属性,我倾向于认为你的代码中有一些错误。定义与否,我的朋友!

【讨论】:

  • 好的,现在我明白了,所以在我的示例代码中这是没有意义的,但在您的示例代码中它可以
  • 但是如果你想检查一个属性的存在,你为什么要在这里使用“hasOwnProperty”呢?不应该是:options.mimeType = options.mimeType || “应用程序/json”;
  • 我完全同意,但是如果options是另一个类的子类并且mimetype是在父类的原型中定义的,现在即使它存在,它也会返回false....呵呵呵呵我是现在开玩笑。如果您知道,这就是要走的路,options 是一个简单的对象“{}”。感谢您的详尽解释!
  • @Legends 当我读到你的评论时,我的头快要爆炸了!!哈哈哈 ;) 好的,不客气
【解决方案2】:

我将假设“构造函数”模式,您的意思是在构造函数中分配this.fn = function (){};this.val = true;

BaseClass.call 上下文中的this 更改为子类,因此其中的所有分配都是在子类本身上进行的。严格来说,这实际上根本没有进行任何形式的继承。让我在代码中解释一下。

SubClass.prototype = Object.create(BaseClass.prototype);

在这一行中,您将继承子类上BaseClass 的原型,这通常意味着继承。但是,BaseClass 的原型与 Function 对象的原型完全相同,SubClass 已经从该对象继承。 this.___ 赋值不会添加到对象的原型中,只会添加到该对象的实例中。要添加到原型中,您需要按照BaseClass.prototype.___ = 'foo'; 的方式执行一些操作。但是,您不想在构造函数中执行此操作,因为每次创建新对象时都会发生该分配。

另外,在您的代码中,调用 SubClass.getName 将引发错误。 name 未在该上下文中定义,仅在 BaseClass 上下文中定义。

【讨论】:

  • 是的,我知道。但我只想显示子类对象上所有属性/函数的 hasOwnProperty 的结果。构造函数模式 --> link
猜你喜欢
  • 2021-12-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-15
  • 1970-01-01
  • 2013-10-19
相关资源
最近更新 更多