为什么将变量(在对象作用域之外)设置为等于对象方法返回未定义的结果?
Why does setting a variable (outside of the object scope) equal to an object method return an undefined result?
下面的代码是用JavaScript从面向对象的MDN页面上复制粘贴的。我研究了关于JavaScript的OOP的一般问题。然而,我是一个初学者,对这个代码片段有以下问题:
-
Person.prototype.gender = '';
的目的是什么?如果我把它拿出来并运行代码,我得到相同的结果。 -
为什么调用
genderTeller()
会导致"未定义"警报?从初学者的角度来看,MDN的解释似乎有点单薄。这是范围问题吗?
function Person(gender) {
this.gender = gender;
}
Person.prototype.gender = '';
Person.prototype.sayGender = function () {
alert(this.gender);
};
var person1 = new Person('Male');
var genderTeller = person1.sayGender;
person1.sayGender(); // alerts 'Male'
genderTeller(); // alerts undefined
alert(genderTeller === person1.sayGender); // alerts true
alert(genderTeller === Person.prototype.sayGender); // alerts true
行
Person.prototype.gender = '';
的目的是什么?如果我将其取出并运行代码,我将得到相同的结果。
这似乎为gender
属性建立了一个默认值。使用它,即使在不调用构造函数的情况下创建实例,该属性仍然是设置的:
var person2 = Object.create(Person.prototype);
console.log(person2.gender); // ""
在创建子类型时可能很有用:
function Employee() {}
Employee.prototype = Object.create(Person.prototype);
console.log(new Employee().gender); // ""
为什么调用
genderTeller()
会导致"未定义"警报?
MDN关于this
的文档应该进一步解释,特别是关于"函数上下文"的部分。但是,this
的值是在调用function
时确定的,而不是在何时何地定义的。
通过将person1.sayGender
赋值给genderTeller
,它将与person1
分离。因此,它不再是一个特定对象的"方法"。
相反,它被作为一个常规函数调用,this
的值被默认为全局对象,在浏览器中是window
。
window.gender = 'n/a';
var genderTeller = person1.sayGender;
genderTeller(); // logs: 'n/a'
对于问题1,行
Person.prototype.gender = '';
给gender属性一个默认值,这样即使在创建新的Person
对象时没有给它赋其他值,它也有一个默认值不是undefined
。''
和undefined
之间的区别有时很重要,有时不重要,但它们是有区别的。
对于问题2,当你这样做时:
var person1 = new Person('Male');
var genderTeller = person1.sayGender;
genderTeller(); // alerts undefined
变量genderTeller
包含一个函数指针,并且只有一个函数指针。它与person1
对象没有关联。当你把它作为一个函数调用时,函数内部的this
指针将被设置为全局对象(在浏览器中是window
)或undefined
(如果在严格模式下运行),因此,因为this
不是Person
对象,this.gender
将不包含适当的值,而可能是undefined
。
在这个错误中有一个非常重要的教训。在javascript中,this
指针是根据调用方法的方式来设置的。当你调用genderTeller()
时,你只是调用一个函数,因此与任何特定的Person
对象没有任何关联。this
指针不会指向Person
对象,因此任何对this.xxx
的引用都假定它是Person
对象将不起作用。
当你调用person1.sayGender()
时,这是调用同一个函数的一种非常不同的方式。因为你是通过对象引用来调用它的,javascript会将this
指针设置为person1
对象,并且它会工作。
这两种情况之间的区别是微妙的,但在javascript中非常重要,所以我将尝试更多地解释。
创建person1
对象后,它是一个包含名为sayGender
的属性的对象。该属性包含一个指向sayGender操作的函数的值。sayGender
只是一个包含在属性中的函数(严格来说是在person1
对象的原型链中,但你可以把它看作是该对象的属性)。
当你这样做的时候:
var genderTeller = person1.sayGender;
你正在创建一个名为genderTeller
的新变量,现在它也持有指向同一函数的指针。但是,就像在sayGender
属性中一样,它只是一个函数。它对任何对象都没有固有的绑定。它只有在通过对象引用调用时才会绑定到对象(或者使用.call()
或.apply()
强制对象引用,但这超出了您在这里所做的)。当你调用
genderTeller()
你只是调用一个函数,它没有对象引用,因此当函数运行时,this
不会指向Person
对象。
正如我上面提到的,可以强制对象引用。例如,您可以这样做:
var genderTeller = person1.sayGender;
genderTeller.call(person1);
// .apply() only differs from .call() in how
// arguments are passed to the function
// since you have no arguments to sayGender() it looks the same as .call()
var genderTeller = person1.sayGender;
genderTeller.apply(person1);
var genderTeller = person1.sayGender.bind(person1);
genderTeller();
并且,它将再次工作,因为您在调用函数时强制与person1
对象关联。
参见MDN的.call()
和.apply()
和.bind()
的参考,如果你想要更多的信息,但你通常不需要使用那些只是为了调用对象上的方法,因为只是用obj.method()
的形式调用它创建对象关联,并导致this
指针为你设置适当。
我浏览了这篇文章,并被这对于不熟悉javascript的人来说是多么令人困惑所震惊。
关于#1,我相信这是对JIT编译器的一个提示,"gender"是一个字符串。Javascript JIT编译器喜欢对象属性保持相同类型(在本例中为String)。这篇文章没有描述这一点,或者这条线根本就在那里,这是愚蠢的。也许这是为了证明你可以在"new Person"实例中重写原型属性
关于#2,当你调用object.method()时,会自动调用方法,其中堆栈将"object"填充为"this"。但如果你做var detachedmethod = cuobject。方法,然后调用detachedmethod(),它不会调用绑定为curobject的"this"(相反,在方法体中,this === undefined)。或者"窗口",我不确定:-))
总而言之,这是一堆挑剔的东西,对于javascript的日常使用来说不是很重要,并且可以随着你的前进而拾取。
正确答案如下。
1)原型定义不是Person的默认值。事实上,如果您创建另一个没有性别的Person,您将看到它仍然是未定义的。
var person2 = new Person();
person2.sayGender; // alerts undefined
这里的重点是要说明构造函数定义覆盖了原型定义。
2)调用genderTeller()会导致未定义,因为genderTeller是一个全局函数,恰好与从person1复制的函数定义相同。sayGender方法。genderTeller与window.genderTeller相同。
因此,当您执行genderTeller()时,'this' = window。因为window没有'gender'属性,你得到一个undefined。你可以通过下面的代码
看到它genderTeller(); // returns undefined
gender = 'hi there';
genderTeller(); // returns 'hi there'
希望有帮助。这是一个普朗克。http://plnkr.co/edit/wwc2vYIvH9QdFYStesVW
- 为什么属性存在于对象实例上,即使其原型发生了更改
- undercore返回数组中对象的indes,其中单词存在于对象中的句子中
- 检查数组中的项是否存在于对象内部
- 如何创建行为类似于本机对象的对象
- 如果闭包的行为类似于对象,它们是对象吗?(好像不像)
- 为什么原始变量的行为类似于对象
- 样式未应用于对象
- 未定义相对于对象的行为
- Javascript Array.indexOf() 在应用于对象数组时表现出奇怪的行为
- HTML5 画布 - 相对于对象的中心进行缩放,而不翻译上下文
- 添加相对于对象的速度
- Qunit测试属性存在于对象中
- babel 5和babel 6之间发生的事情导致我导入的模块存在于对象的默认属性之外
- IndexOf和.splice()等效于对象
- 如何检查我的键是否存在于对象数组中
- For in循环只适用于对象中的最后一个键
- Array.prototype.forEach.call等价于对象
- JavaScript -数组的行为类似于对象
- 扩展运算符相当于对象赋值
- 检查key是否存在于对象数组中