为啥`this`指的是全局对象?
Posted
技术标签:
【中文标题】为啥`this`指的是全局对象?【英文标题】:Why does `this` refer to the global object?为什么`this`指的是全局对象? 【发布时间】:2013-12-30 12:13:06 【问题描述】:我有一个 Backbone Marionette 应用程序,其模块定义如下。当我运行这个应用程序时,控制台日志语句打印出@
作为窗口对象。当list
方法运行时,我认为this
(@
) 会引用List.Controller
对象。我错过了什么?
###
The Header list controller.
###
define [
'cs!app',
'cs!./view',
'cs!modules/header/entities'
], (
App,
View
) ->
App.module 'Header.List', (List, App, Backbone, Marionette, $, _) ->
List.Controller =
list: ->
console.log(@)
headers = App.request 'header:entities'
view = new View.Headers collection: headers
App.headerRegion.show view
setActiveHeader: (headerUrl) ->
headers = App.request 'header:entities'
header = headers.find (header) -> (header.get 'url') == headerUrl
header.select()
headers.trigger 'reset'
App.commands.setHandler 'header:setActive', (headerUrl) ->
List.Controller.setActiveHeader headerUrl
App.Header.List.Controller
更新
这是调用列表方法的模块:
###
The Header module.
###
define [
'cs!app',
'cs!./list/controller'
], (
App,
listController
) ->
App.module 'Header', (Module, App, Backbone, Marionette, $, _) ->
Module.startWithParent = false
App.module 'Routers.Header', (ModuleRouter, App, Backbone, Marionette, $, _) ->
class ModuleRouter.Router extends Marionette.AppRouter
appRoutes:
executeAction = (action, args) ->
action(args)
API =
list: ->
executeAction listController.list
App.Header.on 'start', ->
API.list()
App.addInitializer ->
new ModuleRouter.Router listController: API
App.Header
【问题讨论】:
函数列表的调用在哪里? 我在更新中添加了调用。 【参考方案1】:this
直到函数被调用才被绑定,并且取决于函数的调用方式。您可以将其视为隐式传递给函数的额外参数。
在这种情况下,我认为您正在使用 controller()
调用您的函数。 this
值可以通过将函数作为方法调用(如在foo.bar()
或foo["bar"]()
中)或通过call()
或apply()
显式设置来设置。您的调用都没有这样做,因此这将恢复为全局对象。
来自here。
【讨论】:
我在更新中添加了我的通话。由于我使用的是listController.list,所以我认为this
会绑定到listController。
我刚刚注意到您使用的是requirejs
。 requirejs
似乎没有对 'this' 关键字进行任何绑定。对于没有 requirejs 的相同代码,您将获得控制器 obj。
里德更多:***.com/questions/14650746/…
有趣——我不知道的东西。我需要使用 requirejs,因为这是我组装资产管道的方式。【参考方案2】:
你应该在初始化函数中使用_.bindAll(this)
,只需添加:
initialize:->
_.bindAll(this, "list") // Like @mu mentioned in the comment, function name is required
编辑:
虽然@KiT-O 是正确的,并且调用者可以使用_.bind
函数将函数绑定到Controller。这不应该是调用者的责任,函数需要绑定到其正确的上下文,调用者不应该关心/知道它。
这就是为什么我更喜欢 _.bindAll
解决方案的原因,尽管它向 Backbone 添加了更多样板代码
【讨论】:
我通过将 App.Header.List.addInitializer -> _.bindAll(App.Header.List.Controller, 'list', 'setActiveHeader') 添加到我的 Header 列表控制器中来实现这一点。这可行,但与 KiT O 的解决方案相比似乎很麻烦——我必须维护函数列表。 这不适用于较新版本的下划线:"methodNames are required." @Erik - 我不同意 :),请参阅我解释原因的编辑 @ekeren 如果不是因为我调用该方法的方式,我会同意你的看法。如果您通过 App.Header.List.Controller.list 在上下文中调用该方法,那么一切正常。在我这样做的时候,调用者正在通过 executeAction 部分添加一个间接层,因此也接受了设置上下文的责任。 另外,我的控制器只是一个字典,所以没有调用初始化函数。【参考方案3】:问题是调用对象listController
的方法list
,上下文为window
(全局)。
这是因为您以这种方式调用方法:executeAction listController.list
而从executeAction
这只是调用方法的正常方式:action(args)
您可以将方法绑定到类(使用_.bind
)或使用js Function
的call
或apply
方法(绑定方式更容易):
绑定(_.bind(action, context)
:
executeAction _.bind(listController.list, listController)
调用(或应用)另一个上下文(method.call(context, arg1, ..)
或 method.apply(context, argsArray)
)
executeAction = (context,action, args) ->
action.call(context, args)
API =
list: ->
executeAction listController, listController.list
【讨论】:
我用了你的最后一个例子,它就像一个魅力——我想我明白现在发生了什么。我唯一看到的是 ekerens 选项。 我选择了这个选项。我还在研究与 executeAction 一起使用的额外间接级别的要求。感谢大家的帮助。以上是关于为啥`this`指的是全局对象?的主要内容,如果未能解决你的问题,请参考以下文章