我刚刚在JavaScript中遇到了一个有趣的情况。我有一个类,其方法使用object-literal表示法定义多个对象。在这些对象中,正在使用this
指针。从程序的行为,我推断出this
指针指的是调用该方法的类,而不是文字创建的对象。
这似乎是随意的,尽管这是我期望它发挥作用的方式。这是定义的行为吗?跨浏览器安全吗?有没有任何理由可以解释为什么它超出“规范如此说明”的方式(例如,它是否是一些更广泛的设计决策/哲学的结果)?简化代码示例:
// inside class definition, itself an object literal, we have this function:
onRender: function() {
this.menuItems = this.menuItems.concat([
{
text: 'Group by Module',
rptletdiv: this
},
{
text: 'Group by Status',
rptletdiv: this
}]);
// etc
}
答案 0 :(得分:555)
从我的另一篇文章中删除,这里比你想知道这个更多。
在开始之前,这是关于Javascript最重要的事情,并且当它没有意义时重复自己。 Javascript没有类(ES6 class
是syntactic sugar)。如果看起来像一个类,这是一个聪明的把戏。 Javascript包含对象和功能。 (这不是100%准确,函数只是对象,但将它们视为单独的东西有时会有所帮助)
此变量附加到函数。每当您调用一个函数时,此都会被赋予一定的值,具体取决于您调用该函数的方式。这通常称为调用模式。
有四种方法可以在javascript中调用函数。您可以将该函数作为方法,作为函数,作为构造函数调用,并使用 apply 。< / p>
方法是附加到对象
的函数var foo = {};
foo.someMethod = function(){
alert(this);
}
当作为方法调用时, this 将绑定到函数/方法所属的对象。在这个例子中,这将绑定到foo。
如果你有一个独立的功能,这个变量将被绑定到“全局”对象,几乎总是在浏览器的上下文中的 window 对象。
var foo = function(){
alert(this);
}
foo();
这可能会让你沮丧,但不要感觉不好。很多人认为这是一个糟糕的设计决定。由于回调是作为函数而不是作为方法调用的,因此您可能会看到看似不一致的行为。
很多人通过这样做来解决这个问题,嗯,这个
var foo = {};
foo.someMethod = function (){
var that=this;
function bar(){
alert(that);
}
}
您定义一个 的变量,指向 this 。 Closure(它自己的主题)保持那个,所以如果你把bar称为回调,它仍然有一个引用。
注意:在use strict
模式下,如果用作函数,this
不会绑定到全局。 (这是undefined
)。
您还可以将函数作为构造函数调用。根据您正在使用的命名约定(TestObject),这也可能就是您正在做的事情,并且正在绊倒您。
使用new关键字调用函数作为构造函数。
function Foo(){
this.confusing = 'hell yeah';
}
var myObject = new Foo();
当作为构造函数调用时,将创建一个新的Object, this 将绑定到该对象。同样,如果你有内部函数并且它们被用作回调,你将把它们作为函数调用,而这个将被绑定到全局对象。使用那个=这个技巧/模式的var。
有些人认为构造函数/ new关键字是Java /传统OOP程序员抛出的一个骨骼,可以创建类似于类的东西。
最后,每个函数都有一个名为“apply”的方法(是的,函数是Javascript中的对象)。 Apply允许您确定 this 的值,并允许您传入一组参数。这是一个无用的例子。
function foo(a,b){
alert(a);
alert(b);
alert(this);
}
var args = ['ah','be'];
foo.apply('omg',args);
答案 1 :(得分:34)
函数只是一种Object。
所有Function对象都有call和apply个方法,用于执行它们被调用的Function对象。
调用时,这些方法的第一个参数指定在执行函数期间this
关键字将引用的对象 - 如果它是null
或undefined
,则为全局对象,window
用于this
。
因此,调用一个函数......
whereAmI = "window";
function foo()
{
return "this is " + this.whereAmI + " with " + arguments.length + " + arguments";
}
...带括号 - foo()
- 相当于foo.call(undefined)
或foo.apply(undefined)
,有效与foo.call(window)
或{相同{1}}。
foo.apply(window)
>>> foo()
"this is window with 0 arguments"
>>> foo.call()
"this is window with 0 arguments"
的附加参数作为函数调用的参数传递,而call
的一个附加参数可以将函数调用的参数指定为类似Array的对象。
因此,apply
相当于foo(1, 2, 3)
或foo.call(null, 1, 2, 3)
。
foo.apply(null, [1, 2, 3])
如果函数是对象的属性......
>>> foo(1, 2, 3)
"this is window with 3 arguments"
>>> foo.apply(null, [1, 2, 3])
"this is window with 3 arguments"
...通过对象访问函数的引用并使用括号调用它 - var obj =
{
whereAmI: "obj",
foo: foo
};
- 相当于obj.foo()
或foo.call(obj)
。
但是,作为对象属性保存的函数不会“绑定”到这些对象。正如您在上面foo.apply(obj)
的定义中所看到的,由于函数只是一种Object类型,因此可以引用它们(因此可以通过引用传递给函数调用或通过函数调用的引用返回)。当传递对函数的引用时,没有关于它从传递的位置的附加信息随之携带,这就是为什么会发生以下情况:
obj
对我们的函数参考>>> baz = obj.foo;
>>> baz();
"this is window with 0 arguments"
的调用不会为调用提供任何上下文,因此它与baz
实际上相同,因此baz.call(undefined)
最终引用{{1} }}。如果我们希望this
知道它属于window
,我们需要在调用baz
时以某种方式提供该信息,这是obj
或{的第一个参数的位置{1}}和闭包发挥作用。
baz
执行Function时,它会创建一个新范围并引用任何封闭范围。在上面的示例中创建匿名函数时,它会引用它所创建的作用域,即call
的作用域。这被称为“封闭”。
apply
当您尝试访问变量时,将使用“范围链”查找具有给定名称的变量 - 如果当前范围不包含变量,则查看链中的下一个范围,依此类推直到达到全球范围。当返回匿名函数并且function bind(func, context)
{
return function()
{
func.apply(context, arguments);
};
}
完成执行时,匿名函数仍然引用bind
的范围,因此[global scope (window)] - whereAmI, foo, obj, baz
|
[bind scope] - func, context
|
[anonymous scope]
的范围不会“消失”。 / p>
鉴于上述所有内容,您现在应该能够理解范围在以下示例中的工作方式,以及为什么使用特定值bind
将“预绑定”函数传递给它的技术它被称为作品:
bind
答案 2 :(得分:9)
这是定义的行为吗?是吗 跨浏览器安全?
是。是的。
有什么理由可以解释为什么 这是它的方式......
this
的含义很容易推断:
this
,并且使用new
关键字调用该函数,则this
引用将要创建的对象。 this
即使在公共方法中也会继续表示对象。this
,包括嵌套的 protected 函数,则它引用全局范围(在浏览器的情况下是窗口对象)。第二种情况显然是一个设计缺陷,但通过使用闭包来解决它很容易。
答案 3 :(得分:4)
在这种情况下,内部this
绑定到全局对象而不是外部函数的this
变量。
这就是语言的设计方式。
请参阅Douglas Crockford的“JavaScript:The Good Parts”作为一个很好的解释。
答案 4 :(得分:4)
我找到了一个很好的关于 ECMAScript的教程
此值是与执行相关的特殊对象 上下文。因此,它可以被命名为上下文对象(即 激活执行上下文的上下文对象。
任何对象都可以用作上下文的这个值。
此值是执行上下文的属性,但不是a 变量对象的属性。
此功能非常重要,因为与变量相反,此值从不参与标识符解析过程。即在代码中访问它时,它的值直接来自执行上下文,没有任何作用域链查找。输入上下文时,此值仅确定一次。
在全局上下文中,this值是全局对象本身(也就是说,此值等于变量对象)
在函数上下文的情况下,每个函数调用中的此值可能不同
答案 5 :(得分:0)
这里的所有答案都非常有帮助,但是我仍然很难弄清this
所指的是对象解构。因此,我想使用我的代码的简化版本再添加一个答案,
let testThis = {
x: 12,
y: 20,
add({ a, b, c }) {
let d = a + b + c()
console.log(d)
},
test() {
//the result is NaN
this.add({
a: this.x,
b: this.y,
c: () => {
//this here is testThis, NOT the object literal here
return this.a + this.b
},
})
},
test2() {
//64 as expected
this.add({
a: this.x,
b: this.y,
c: () => {
return this.x + this.y
},
})
},
test3() {
//NaN
this.add({
a: this.x,
b: this.y,
c: function () {
////this here is the global object
return this.x + this.y
},
})
},
}