为啥`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。 我刚刚注意到您使用的是requirejsrequirejs 似乎没有对 '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 Functioncallapply 方法(绑定方式更容易):

绑定(_.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`指的是全局对象?的主要内容,如果未能解决你的问题,请参考以下文章

this的使用

为啥js的箭头函数的this指向的是全局呢?

我理解的this

js 五 jquery的使用,调用

深入浅出JavaScript之this

深入浅出JavaScript之this