我刚刚在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
}
5 回答
是 . 是的 .
this
的含义很容易推断:如果在构造函数内部使用了
this
,并且使用new
关键字调用了该函数,this
将引用将要创建的对象 . 即使在公共方法中,this
仍将继续表示对象 .如果
this
在其他任何地方使用,包括嵌套的受保护函数,则它引用全局范围(在浏览器的情况下是窗口对象) .第二种情况显然是一个设计缺陷,但通过使用闭包很容易解决它 .
函数调用
函数只是一种Object .
所有Function对象都有call和apply方法,这些方法执行它们被调用的Function对象 .
调用时,这些方法的第一个参数指定在执行函数期间
this
关键字将引用的对象 - 如果它是null
或undefined
,则全局对象window
用于this
.因此,调用一个函数......
...带括号 -
foo()
- 相当于foo.call(undefined)
或foo.apply(undefined)
,实际上与foo.call(window)
或foo.apply(window)
相同 .call
的附加参数作为函数调用的参数传递,而apply
的单个附加参数可以将函数调用的参数指定为类似Array的对象 .因此,
foo(1, 2, 3)
相当于foo.call(null, 1, 2, 3)
或foo.apply(null, [1, 2, 3])
.如果函数是对象的属性......
...通过对象访问函数的引用并用括号调用它 -
obj.foo()
- 相当于foo.call(obj)
或foo.apply(obj)
.但是,作为对象属性保存的函数不是这些对象的"bound" . 正如您在上面
obj
的定义中所看到的,由于函数只是一种Object类型,因此可以引用它们(因此可以通过引用传递给函数调用或通过函数调用的引用返回) . 当传递对Function的引用时,没有关于它从哪里传递的附加信息,这就是为什么会发生以下情况:对函数引用
baz
的调用与baz.call(undefined)
实际上没有't provide any context for the call, so it',因此this
最终引用了window
. 如果我们希望baz
知道它属于obj
,我们需要以某种方式在调用baz
时提供该信息,这是call
或apply
和闭包的第一个参数发挥作用的地方 .范围链
执行Function时,它会创建一个新范围并引用任何封闭范围 . 当在上面的例子中创建匿名函数时,它引用了它创建的范围,这是
bind
的范围 . 这被称为"closure."当您尝试访问变量时,将使用"scope chain"查找具有给定名称的变量 - 如果当前范围不包含变量,则查看链中的下一个范围,依此类推,直到达到全局范围为止 . 当返回匿名函数并且
bind
完成执行时,匿名函数仍然具有对bind
范围的引用,因此bind
's scope doesn' t "go away" .鉴于上述所有内容,您现在应该能够理解范围在以下示例中的工作方式,以及为什么在调用"pre-bound"时使用特定值
this
传递函数的技术在调用时会起作用:在这种情况下,内部
this
绑定到全局对象而不是外部函数的this
变量 . 这是语言的设计方式 .请参阅Douglas Crockford撰写的“JavaScript:The Good Parts”作为一个很好的解释 .
从我的另一个帖子中解放出来,这里比你想知道的更多 .
在我开始之前,这里's the most important thing to keep in mind about Javascript, and to repeat to yourself when it doesn' t有意义 . Javascript没有类(ES6
class
是syntactic sugar) . 如果看起来像一个类,这是一个聪明的伎俩 . Javascript有 objects 和 functions . (那不是100%准确,函数只是对象,但有时可以将它们视为单独的东西有帮助)此变量附加到函数 . 无论何时调用函数,都会给出一定的值,具体取决于您调用函数的方式 . 这通常称为调用模式 .
有四种方法可以在javascript中调用函数 . 您可以将函数作为方法,函数,构造函数和apply来调用 .
作为一种方法
方法是附加到对象的函数
当作为方法调用时,它将绑定到函数/方法所属的对象 . 在这个例子中,这将绑定到foo .
作为一个功能
如果你有一个独立的函数,这个变量将被绑定到"global"对象,几乎总是在浏览器的上下文中的窗口对象 .
This may be what's tripping you up ,但不要为什么你看到看似不一致的行为 .
很多人通过这样做来解决这个问题
您定义一个指向此的变量 . Closure(一个它自己的主题)保持这种状态,所以如果你把bar称为回调,它仍然有一个引用 .
注意:在
use strict
模式中,如果用作函数,this
不会绑定到全局 . (这是undefined
) .作为构造函数
您还可以将函数作为构造函数调用 . 根据您正在使用的命名约定(TestObject),这也是 may be what you're doing and is what's tripping you up .
使用new关键字将函数作为构造函数调用 .
当作为构造函数调用时,将创建一个新的Object,并将绑定到该对象 . 同样,如果你有内部函数,那么它们将作为函数调用它们,并且它将被绑定到全局对象 . 使用那个=这个技巧/模式的var .
有些人认为构造函数/ new关键字是Java /传统OOP程序员抛出的骨骼,作为创建类似于类的东西的方法 .
使用Apply方法
最后,每个函数都有一个名为"apply"的方法(是的,函数是Javascript中的对象) . Apply允许您确定它的值,并允许您传入一组参数 . 这是一个无用的例子 .
我发现了一个很好的关于ECMAScript的教程
任何对象都可以用作上下文的这个值 .
此功能非常重要,因为与变量相反,此值永远不会参与标识符解析过程 . 即在代码中访问它时,它的值直接来自执行上下文,没有任何作用域链查找 . 输入上下文时,此值仅确定一次 .
在全局上下文中,this值是全局对象本身(这意味着,此值等于变量对象)
在函数上下文的情况下,每个函数调用中的该值可能不同
参考Javascript-the-core和Chapter-3-this