hasOwnProperty 在对照父对象属性进行检查时返回 true

hasOwnProperty returns true, when checked against parent object properties

本文关键字:进行检查 返回 true 属性 对象 hasOwnProperty      更新时间:2023-09-26

My JavaScript code:

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)。一个使用构造函数模式从另一个继承,如 MDN 中所述。

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

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

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

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

我现在能想到的唯一一件事,为什么你应该将它们附加到原型对象,是因为效率原因,正如这里解释的那样,"定义类方法"部分。为了不为每个构造实例添加闭包。

类型应该在原型上声明,但不能声明其初始值依赖于构造函数的参数或构造时的某些其他状态的实例变量。您可以重写这两个属性/函数,而不考虑它们的声明位置。

此外,在原型上设置 getter 和 setter,

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

因此,使用吸气剂和二传手是没有意义的。您可以直接访问公共变量。

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

我什么时候需要hasOwnProperty,如果实际上应该在原型上声明公共道具/函数,那么原型又会输出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是没有意义的。

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

这就是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 的实例)的引用。

使用原型,

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

关于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 继承)之上添加更多行为。

关于为什么使用hasOwnProperty...

OP说:

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

互联网上有很多代码在做'options.mimeType =

options.mimeType || "application/json" , right, because in JavaScript undefined evaluates to false (if options doesn't own a mimeType property returns undefined')。

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

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

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

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

我将假设"构造函数"模式,您的意思是在构造函数中分配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上下文中定义。