简体   繁体   English

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

[英]Understanding Complex Scope in Javascript Modules and Plugins

I know there's a lot of questions on Stack about JS Scope... but I ran into a specific problem that I'm unable to wrap my head around. 我知道Stack上有很多关于JS Scope的问题...但是我遇到了一个无法解决的具体问题。 I have a Javascript module that looks something like this (albeit dramatically simplified): 我有一个看起来像这样的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();
    });
  },
}

Now, here's what I don't understand, if I move that "PROBLEM BLOCK" outside of the Ajax call, and put it back up into init(), it works fine. 现在,这就是我不了解的东西,如果我将“ PROBLEM BLOCK”移到Ajax调用之外,然后将其放回init(),则可以正常工作。 However, as far as I can tell, in it's current location , the scope (_this = this) is the exact same as it would be up in the init function. 但是,据我所知,在当前位置范围 (_this = this)与init函数中的范围 完全相同

And to be more specific, the problem I'm experiencing is that the "onChange" handler never fires when the code is inside of the Ajax handler, but the plugin instance is still created and functions as it otherwise should . 更具体地说,我遇到的问题是,当代码在Ajax处理程序内部时,“ onChange”处理程序永远不会触发, 但是插件实例仍会创建并按其他方式运行 However, if I move it up to the init(), the onChange handler fires without any other changes to the code 但是,如果我将其移至init(),则会触发onChange处理程序,而无需对代码进行任何其他更改

Any help to get me to wrap my head around this would be greatly appreciated. 我们将不胜感激任何帮助,以帮助我包扎一切。 Thank you! 谢谢!

I had a similar issue, where you start chasing your own tail using objects. 我有一个类似的问题,您开始使用对象追逐自己的尾巴。

The power of using modules, is that they have their own context. 使用模块的力量在于它们具有自己的上下文。 So once compiled, the file knows what vars and funcs are residing inside; 因此,一旦编译完成,文件便知道其中包含哪些var和funcs。 this negates the need to track this bouncing from function to function, which becomes a nightmare, once you involve async callbacks. 一旦涉及异步回调,就不需要跟踪各个函数之间的this反弹,这将成为一场噩梦。

I recommend rewriting your module with vars at the top and functions, so it's easier to call any function without trying to pass the correct _this/self context from here, there and everywhere. 我建议用顶部和函数的vars重写模块,因此调用任何函数都更容易,而无需尝试从此处,任何地方和任何地方传递正确的_this/self上下文。

Here's an untested re-write: 这是未经测试的重写:

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();
    });
  }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM