【问题标题】:Why does hasOwnProperty behave differently for constructor functions and instances?为什么 hasOwnProperty 对于构造函数和实例的行为不同?
【发布时间】:2019-05-03 22:44:39
【问题描述】:

hasOwnProperty 的行为似乎有所不同,具体取决于它是在构造函数还是实例上调用,具体取决于对包含的成员使用 this 或 let。

function Animal(_name) {

    let name = _name;

    this.getName = function() {
        return name;
    }

};

function Animal2(_name) {

    this.name = _name;

    let getName = function() {
        return name;
    }

}

let a = new Animal("greg");
let a2 = new Animal2("tim");

console.log(a.hasOwnProperty("name"));
console.log(a2.hasOwnProperty("name"));
console.log(Animal.hasOwnProperty("name"));
console.log(Animal2.hasOwnProperty("name"));
console.log("");
console.log(a.hasOwnProperty("getName"));
console.log(a2.hasOwnProperty("getName"));
console.log(Animal.hasOwnProperty("getName"));
console.log(Animal2.hasOwnProperty("getName"));

这会输出以下内容:

false
true
true
true

true
false
false
false

为什么会这样?我了解在构造函数中使用“let”模拟“私有”成员,这可以解释为什么 a.hasOwnProperty("name") 和 a2.hasOwnProperty("getName") 都返回 false,但不知道为什么构造函数不要“拥有”他们的方法。

【问题讨论】:

  • 您在这里使用属性name 有点污染了您自己的研究,这些功能一直都有(function test() {}).name 是“测试”,@987654325 @ 是 ””。如果我们在 var 中捕获它们(例如var f = function...),那么在第一种情况下,名称仍然是“test”,而在第二种情况下,现在是您使用的 var 的名称。真正的问题是:为什么在 2019 年使用 hasOwnProperty,因为类是一回事,我们不再需要从函数中构建对象?
  • 你错了,Let 不是'私人成员',它只是一个 局部变量 。 javascript中没有私有元素,可能是下一个JS版本,但目前没有
  • @Mike'Pomax'Kamermans 是的,我可能应该检查一下 Animal.name 属性实际上是什么,哈哈。使用 hasOwnProperty 有什么替代方法?我没有意识到它已经过时了。是的,我知道类,我只是随意决定使用构造函数。但我想从现在开始我会使用类语法:)
  • 'emulate' 也是错误的。 JavaScript 是一种基于原型的基于对象的语言,而不是基于类的语言。一切都不等价。
  • hasOwnProperty 不会过时,无论是今天还是明天。

标签: javascript function object ecmascript-6 hasownproperty


【解决方案1】:

因为AnimalAnimal2 是构造函数——而函数有一个属性name,它是函数的名称。如果您查看Animal.nameAnimal2.name,您会看到它是AnimalAnimal2。而且因为AnimalAnimal2 都没有属性getName,所以只有Animal 的实例,其他三个对getName 的检查返回false。

function Animal(_name) {
    let name = _name;
    this.getName = function() {
        return name;
    }
};

function Animal2(_name) {
    this.name = _name;
    let getName = function() {
        return name;
    }
}
console.log(Animal.name);
console.log(Animal2.name;

【讨论】:

  • 好的,谢谢,但为什么 Animal 和 Animal2 有 getName 作为属性?构造函数和实例对象有区别吗?
  • 是的,有 - AnimalAnimal2 没有 getName 因为它们是构造函数,并且在构造函数中创建的任何内容仅适用于实例,例如使用构造函数创建的所有对象。
  • @loldabigboi 他们没有它,因为您没有将它添加到他们的原型中,例如:Animal.prototype.getName = function() { return this.name })(您应该改用class-syntax,这会将它添加到以更干净的方式制作原型)
  • “拥有”我更只是意味着 hasOwnProperty 返回 true。我上面链接的代码显然输出 false,因为 hasOwnProperty 没有查看对象的原型。
  • 但是请注意,在现代 JS 中,不要这样做:如果你想要一个对象类,只需编写一个类代码,除非你真的知道为什么不能使用它反而。在这种情况下,这首先不是一个问题=)
【解决方案2】:

正如其他人已经提到的,您会感到困惑,因为函数已经具有 name 属性。它实际上有长度、名称、参数、调用者和原型

console.log(Object.getOwnPropertyNames(function(){}));

创建对象的方式不是如何在 Javascript 中进行。 例如:对象的每个实例都有自己的函数,这会浪费内存和性能,因为它不能有效地优化。 (如果你只有两个对象也没关系,但很快你就会有 1000 个对象,最好从一开始就学会这样做)

function Animal(_name) {
  let name = _name;

  this.getName = function() {
    return name;
  }
};

const animal1 = new Animal('name1');
const animal2 = new Animal('name1');
console.log ( animal1.getName === animal2.getName ) // returns false

在 Jacscript 中,对象是基于原型的。这是创建定义构造函数的旧语法:

function Animal(name) {
  this.name = name;
};

Animal.prototype.getName =
  function() {
    return this.name;
  }

const animal1 = new Animal('name1');
const animal2 = new Animal('name1');
console.log ( 'Function', animal1.getName === animal2.getName ); // returns true
console.log ( 'Property', Animal.prototype.hasOwnProperty('getName') ); //returns true

在构造函数上使用hasOwnProperty 只会对构造函数上定义的属性返回true。在您自己的示例中,getName 在您运行构造函数之前没有定义,然后属性是在对象的实例上定义的,而不是在构造函数上。

在第二个例子中,它仍然没有在构造函数上定义,而是在原型上。

但是,如果您愿意,您可以将方法和值放在构造函数上。它们被称为static,因为它们可以在没有实例的情况下访问。

这是一个示例,使用旧语法(带有一些新示例)

function MyOldClass() {
  // this is the construcsyntax
  console.log('old class');
}

MyOldClass.prototype.myInstanceMethod1 =
  function myInstanceMethod1() {
    console.log('instance method 1');
  }

// More efficient way to add multiple items
Object.assign(
  MyOldClass.prototype,
  {
    // anonymous function
    myInstanceMethod2: function (){
      console.log('instance method 2');
    },
    // named function (propery name and functio name can be different)
    myInstanceMethod3: function myName(){
      console.log('instance method 3');
    },
    // new shorthand syntax (both propery name and function name is the same)
    myInstanceMethod4(){
      console.log('instance method 4');
    },
    // It is posible to add values to the prototype (not possible with new syntax)
    myInstanceValue1 : 1,
    myInstanceValue2 : { prop1 : 1 }
  }
);

Object.assign(
  MyOldClass,
  {
    myStaticMethod() {
      console.log('my new static');
    },
    myStaticValue1 : 1
  }
);

console.log('Static method', MyOldClass.hasOwnProperty('myStaticMethod') ); // returns true
console.log('myInstanceMethod1', MyOldClass.prototype.hasOwnProperty('myInstanceMethod1') ); // returns true
console.log('myInstanceMethod2', MyOldClass.prototype.hasOwnProperty('myInstanceMethod2') ); // returns true
console.log('myInstanceMethod3', MyOldClass.prototype.hasOwnProperty('myInstanceMethod3') ); // returns true
console.log('myInstanceMethod4', MyOldClass.prototype.hasOwnProperty('myInstanceMethod4') ); // returns true

// Create two instances
const object1 = new MyOldClass(), object2 = new MyOldClass();

// Comparing methods on the instances. Is the same since it is comming from the prototype.
console.log( 'myInstanceMethod1', object1.myInstanceMethod1 === object2.myInstanceMethod1 );

// Comparing values on the instancees. Is the same since it is comming from the prototype.
console.log( 'myInstanceValue1 (pre change)', object1.myInstanceValue1 === object2.myInstanceValue1 );

// Changing the value on the prototype: all instances that use this prototype will have the new value
MyOldClass.prototype.myInstanceValue1 = 2;                                                                                                                              console.log( 'myInstanceValue1 changed prototype', object1.myInstanceValue1, object2.myInstanceValue1 );

// Changing the value on the instance, will create a new propery on the instance if it doesn't exist.                                                                   object1.myInstanceValue1+=3;
// Now they have different values: object1 has its own propery, while object 2 still uses the prototype.
console.log( 'myInstanceValue1 changed instance', object1.myInstanceValue1, object2.myInstanceValue1 );

// Changing on the prototype.
MyOldClass.prototype.myInstanceValue1 = 10;
// object1 still uses its own property, but object 2 have the new value since it uses the prototype
console.log( 'myInstanceValue1 changed prototype', object1.myInstanceValue1, object2.myInstanceValue1 );

// Deletes the value from object1. It will now use the prototype value.
delete object1.myInstanceValue1;
console.log( 'myInstanceValue1 after delete 1', object1.myInstanceValue1, object2.myInstanceValue1 );

// Deleting myInstanceValue1 from the instance (it if don't exists) will not delete it from the prototype
delete object1.myInstanceValue1;
console.log( 'myInstanceValue1 after delete 2', object1.myInstanceValue1, object2.myInstanceValue1 );

使用新语法的相同定义

class MyNewClass {
  constructor() {
    console.log('new class');
  }
  myInstanceMethod1(){
    console.log('instance method 1');
  }
  myInstanceMethod2(){
    console.log('instance method 2');
  }
  myInstanceMethod3(){
    console.log('instance method 3');
  }
  myInstanceMethod4(){
    console.log('instance method 4');
  }
  static myStaticMethod() {
    console.log('my new static');
  }
}

// The class syntax allows you to define methods, but if you want to add values
// your can do that the old way:
MyNewClass.prototype.myInstanceValue1 = 1;
Object.assign(
  MyNewClass.prototype,
  {
    myInstanceValue2 : { prop1 : 1 }
  }
);

Object.assign(
  MyNewClass,
  {
    myStaticValue1 : 1
  }
);

如果需要私有,可以使用 Wea​​kMap:

// Private values using WeakMap ( weakly referenced: when the instance is garbage collected,
// the private data will also be deleted )

const MyClassPrivates = new WeakMap;
class MyClass {
  constructor (name) {
    MyClassPrivates.set(this, { "name" : name });  // initializes the private data
  }
  getName() {
    const privates = MyClassPrivates.get(this); // get all private data
    return privates.name;
  }
  setName(name) {
    const privates = MyClassPrivates.get(this); // get all private data
    privates.name = name;
    return privates.name;
  }
}

const instance = new  MyClass('Elsa');
Object.freeze(instance);
console.log(instance.getName());
instance.setName('Anna');
console.log(instance.getName());

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-12-06
    • 1970-01-01
    • 2020-06-03
    • 2017-11-09
    • 2020-09-16
    • 2020-07-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多