Javascript scope referencing outer object

蹲街弑〆低调 提交于 2019-12-07 12:03:25

问题


Basically, I use a meta-class framework called Joose for Javascript that allows me to make use of a more elegant class syntax - but I don't know how I might go about referencing the scope of the object from within deeper methods of the class declaration. I also use require.js for dependemcy management...

Here's an example class definition:

   define([
      'jquery',
      'handlebars',
   ], function($, Handlebars){

      var MyClass = Class("MyClass", {

         //inheritance         
         isa: SuperClass,

         //instance vars
         has: {

            hello:{
               is: 'r',
               init: 'Hi There!',
            },

            someVarToBeSetUsingAjax:{
               is: 'rw',
               init: false,
            },
         },

         //methods
         methods: {

            init: function () {

               var self = this;
               self.getAjaxVar();

            },

            getAjaxVar: function() {

               var self = this;

               //HOW CAN I REFERENCE 'self' THROUGHOUT MY OBJECT?

               $.get('ajax/test.html', function(response) {
                 self.someVarToBeSetUsingAjax = response.value;
               });
            },

            //lots more methods...

         }
    });

   return MyClass;

});

Ok, so my issue is - in the AJAX function I have to write var self = this to get my object into the scope of the AJAX call - no problem. But, I find myself doing this for almost every single method in my class declaration! How can I reference self in all of the methods in a clean and efficient way? I know you can use scope in AJAX by setting a parameter, assume it's not just AJAX but other functions that close the scope to the outside.

Thanks.


回答1:


Everytime you nest a function, you have to think about this. But if you dont nest a function, or that function doesn't use this you don't need to think about it.

        init: function () {
           var self = this;
           self.getAjaxVar();
        },

So in this case it's not necessary. This is exactly the same:

        init: function () {
           this.getAjaxVar();
        },

But here:

        getAjaxVar: function() {
           var self = this;

           $.get('ajax/test.html', function(response) {
             self.someVarToBeSetUsingAjax = response.value;
           });
        },

You create an inner function, and you want a reference to the original value of this, so you do have to alias this to self to make it accessible.

There isn't a way to fix this to a value from everywhere in your class.


That said, you do have some options.

Function.prototype.bind() can help.

var func = function() { return this.name };
var obj = { name: 'Bob' };
var boundFunc = func.bind(obj);
boundFunc(); // 'Bob'

bind will return a new function with this always set to a specific object.

So:

        getAjaxVar: function() {
           $.get('ajax/test.html', function(response) {
             this.someVarToBeSetUsingAjax = response.value;
           }.bind(this));
        },

Note this isn't supported in all browsers, you may need a shim for the old ones.

Or just get used to self = this.


I want to give a minor nod to coffeescript as well, because it supports declaration of functions that dont change the context when run.

obj = {
  name: "bob"
  sayHello: ->
    doSomeAjax({
      success: =>
        alert "successfully made " + this.name + " say hello!"
    })
}

obj.sayHello()

-> makes a normal function. But the fat arrow => will instead preserve the value of this inside and outside the function. It's very very handy in callbacks within instance methods. When compiled to JS, it basically does a self = this alias for you, using self within the inner function everytime to reference this. It's pretty slick.

In plain JS though, the most common pattern is simply self = this, stick to it.




回答2:


The answer is to use this. You only need to create a closure (which you do with the var self = this; when you have a function which will be called external to the object. (as you do here with the return from the ajax call).

There is no other way to create a closure.


To be clear (since some will jump on any slight technical hand waving), you don't "need" to create a closure. But I think you should -- JavaScript was designed to work with closures, they work well and they are well understood by other JavaScript programmers.




回答3:


I find myself doing this for almost every single method in my class declaration! How can I reference self in all of the methods in a clean and efficient way?

You cannot. The value of self, the instance referenced by this, is only known on calling the method.

You only could work around that and use one common self variable if you declared that in the constructor (your init method?) and created all the callback functions there as well. Might not be the most memory-efficient method, though:

return Class("MyClass", {
     // …

     methods: {
         init: function () {
             var self = this;
             this.ajaxVarCallback = function(response) {
                 self.someVarToBeSetUsingAjax = response.value;
             };
             // and other functions that use "self"

             this.getAjaxVar();
         },
         getAjaxVar: function() {
             $.get('ajax/test.html', this.ajaxVarCallback);
         },
         // …
     }
});



回答4:


Apart from always keeping all instances of MyClass in scope (or as a global variable), I don't see any solutions. However, you could do away with having to declare self everywhere by using Function.prototype.bind:

$.get('ajax/test.html', function(response) {
  this.someVarToBeSetUsingAjax = response.value;
}.bind(this));

This isn't limited to $.get(), you can use it everywhere you're using callback functions.



来源:https://stackoverflow.com/questions/14267351/javascript-scope-referencing-outer-object

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!