Почему hasOwnProperty ведет себя по-разному для функций и экземпляров конструктора? - PullRequest
3 голосов
/ 04 мая 2019

hasOwnProperty, похоже, ведет себя по-разному в зависимости от того, вызывается ли он в функции-конструкторе или экземпляре, в зависимости от использования этого или 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, но не знаю, почему функции конструкторане «владеть» их методами.

Ответы [ 2 ]

2 голосов
/ 04 мая 2019

Поскольку Animal и Animal2 являются функциями конструктора, а функция имеет свойство name, которое является именем функции.Если вы посмотрите на Animal.name или Animal2.name, вы увидите, что это Animal и Animal2.И поскольку ни Animal, ни Animal2 не имеют свойства 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;
0 голосов
/ 04 мая 2019

Как уже упоминалось другими, вы сбиты с толку, поскольку у функции уже есть свойство 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
  }
);

Если вам нужны рядовые, вы можете использовать WeakMap:

// 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());
...