为什么 hasOwnProperty 对构造函数和实例的行为不同?
Why does hasOwnProperty behave differently for constructor functions and instances?
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" 模拟 'private' 成员,这可以解释为什么 a.hasOwnProperty("name") 和 a2.hasOwnProperty("getName") 两者return 错误,但不知道为什么构造函数不 'own' 它们的方法。
因为 Animal
和 Animal2
是构造函数 - 并且一个函数有一个 属性 name
这是函数的名称。如果您查看 Animal.name
或 Animal2.name
,您会看到它是 Animal
和 Animal2
。因为 Animal
和 Animal2
都没有 属性 getName
,只有 Animal
的实例,其他三个检查 getName
return 错误。
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;
正如其他人已经提到的,您感到困惑,因为函数已经具有 名称 属性。它实际上有 长度、名称、参数、调用者和原型 。
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
returns 对于在构造函数上定义的属性为真。在你自己的例子中 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());
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" 模拟 'private' 成员,这可以解释为什么 a.hasOwnProperty("name") 和 a2.hasOwnProperty("getName") 两者return 错误,但不知道为什么构造函数不 'own' 它们的方法。
因为 Animal
和 Animal2
是构造函数 - 并且一个函数有一个 属性 name
这是函数的名称。如果您查看 Animal.name
或 Animal2.name
,您会看到它是 Animal
和 Animal2
。因为 Animal
和 Animal2
都没有 属性 getName
,只有 Animal
的实例,其他三个检查 getName
return 错误。
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;
正如其他人已经提到的,您感到困惑,因为函数已经具有 名称 属性。它实际上有 长度、名称、参数、调用者和原型 。
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
returns 对于在构造函数上定义的属性为真。在你自己的例子中 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());