了解Javascript模块和插件中的复杂范围

时间:2015-08-27 18:13:13

标签: javascript jquery scope

我知道Stack上有很多关于JS Scope的问题......但我遇到了一个我无法解决的特定问题。我有一个看起来像这样的Javascript模块(虽然大大简化了):

module.exports = {
  $company: $('#id_company'),
  $companyCtrl: null,
  $jobType: $('#id_job_type'),
  $jobTypeCtrl: null,

  init: function() {
    var _this = this;
    this.$companyCtrl = this.$company.selectize({
      onChange: function(value) {
        _this.companyChanged(value);
      }
    })[0].selectize;
  },

  companyChanged: function() {
    // Company changed has been fired and does a few things
    // before it calls this:
    this.updateJobType();
  },

  updateJobType: function() {
    var _this = this;
    $.ajax({
      url:'/ajax-url',
      data: {
        'id': this.companyID
      }
    })
    .done(function(data) {
      // If our job type selectize() instance hasn't been setup,
      // then create it now
      if (_this.$jobTypeCtrl === null) {
        // ------------
        // PROBLEM BLOCK 
        _this.$jobTypeCtrl = _this.$jobType.selectize({
          onChange: function(value) {
            if (_this.currentModel !== 'wire_add') {
              _this.jobTypeChanged(value);
            }
          }
        })[0].selectize;
        // ------------
      }

      // Reload and re-enable input
      _this.$jobTypeCtrl.reloadFromOriginalInput();
      _this.$jobTypeCtrl.enable();
    });
  },
}

现在,这是我不明白的,如果我移动那个"问题块"在Ajax调用之外,并将其重新放入init(),它工作正常。但是,据我所知,在它的当前位置中,范围(_this = this)是完全相同的因为它会在init函数中出现。

更具体地说,我遇到的问题是" onChange"当代码在Ajax处理程序中时,处理程序永远不会触发,但插件实例仍然是创建的,否则应该。但是,如果我将它移动到init(),onChange处理程序将触发而不对代码进行任何其他更改

任何帮助让我绕过这个的人都会非常感激。谢谢!

1 个答案:

答案 0 :(得分:1)

我有一个类似的问题,你开始用对象追逐你自己的尾巴。

使用模块的强大之处在于它们有自己的上下文。因此,一旦编译,该文件就知道内部存在哪些变量和函数;这消除了跟踪this从功能到功能的反弹的需要,一旦你涉及异步回调,这就变成了一场噩梦。

我建议在顶部和函数中使用vars重写模块,这样就可以更轻松地调用任何函数,而无需从此处,那里和任何地方传递正确的_this/self上下文。

这是一个未经测试的重写:

module.exports = {
  var $company = $('#id_company'),
      $companyCtrl = null,
      $jobType = $('#id_job_type'),
      $jobTypeCtrl = null;

function init() {
    $companyCtrl = $company.selectize({
      onChange: function(value) {
        companyChanged(value); // <== invoke any function and treat them as black-box code
      }
    })[0].selectize;
}

function companyChanged() {
    // Company changed has been fired and does a few things
    // before it calls this:
    updateJobType();
}

function updateJobType() {
    $.ajax({
      url:'/ajax-url',
      data: {
        'id': companyID
      }
    })
    .done(function(data) {
      // If our job type selectize() instance hasn't been setup,
      // then create it now
      if ($jobTypeCtrl === null) {
        // ------------
        // PROBLEM BLOCK 
        $jobTypeCtrl = $jobType.selectize({
          onChange: function(value) {
            if (currentModel !== 'wire_add') {
              jobTypeChanged(value);
            }
          }
        })[0].selectize;
        // ------------
      }

      // Reload and re-enable input
      $jobTypeCtrl.reloadFromOriginalInput();
      $jobTypeCtrl.enable();
    });
  }
}