在Javascript中确保“此”上下文的最佳做法是什么?

时间:2013-04-26 00:12:58

标签: javascript this

以下是一个带有公共和私有方法的简单Javascript类的示例(小提琴:http://jsfiddle.net/gY4mh/)。

function Example() {
    function privateFunction() {
       // "this" is window when called.
       console.log(this);
    }

    this.publicFunction = function() {
       privateFunction();
    }
}

ex = new Example;
ex.publicFunction();

从公共部门调用私有函数会导致“this”成为窗口对象。我应该如何确保使用类上下文而不是窗口调用我的私有方法?这会不合适吗?

8 个答案:

答案 0 :(得分:8)

使用闭包。基本上,在函数中声明的任何变量仍然可用于该函数内的函数:

var Example = (function() {
    function Example() {
        var self = this; // variable in function Example
        function privateFunction() {                  
            // The variable self is available to this function even after Example returns. 
            console.log(self);
        }

        self.publicFunction = function() {
            privateFunction();
        }
    }

    return Example;
})();

ex = new Example;
ex.publicFunction();

答案 1 :(得分:3)

另一种方法是使用" apply"明确设置方法"这个"应该受到约束。

function Test() {
    this.name = 'test';
    this.logName = function() {
        console.log(this.name);
    }
}

var foo = {name: 'foo'};

var test = new Test();
test.logName()
// => test
test.logName.apply(foo, null);
// => foo

另一种方法是使用" call":

function Test() {
    this.name = 'test';
    this.logName = function() {
        console.log(this.name);
    }
}

var foo = {name: 'foo'};

var test = new Test();
test.logName()
// => test
test.logName.call(foo, null);
// => foo

两者"申请"并且"打电话"拿你要绑定的对象"这个"作为第一个参数和一个参数数组传递给您调用的方法作为第二个arg。

答案 2 :(得分:3)

值得了解除了让别人告诉你代码修复之外,如何确定javascript中this的值。在javascript中,this通过以下方式确定:

  1. 如果您通过object.method()中的对象属性调用函数,则this将设置为方法内的对象。

  2. 如果直接调用函数而没有任何对象引用(例如function()),则this将设置为全局对象(浏览器中的window)或严格模式,它将设置为undefined

  3. 如果使用new运算符创建新对象,则将调用该对象的构造函数,并将this的值设置为新创建的对象实例。您可以将此视为与上面的第1项相同,创建对象,然后调用其上的构造函数方法。

  4. 如果您使用.call()中的.apply()function.call(xxx)调用函数,那么您可以通过您传递的参数确切地确定this设置的内容到.call().apply()。您可以在MDN上详细了解.call() here.apply() here

  5. 如果使用function.bind(xxx),则会创建一个小的存根函数,以确保使用所需的this值调用函数。在内部,这可能只是使用.apply(),但它是一个快捷方式,当你想要一个回调函数时,它被调用时具有正确的值this(当你不是直接调用者的时候)功能)。

  6. 在回调函数中,回调函数的调用者负责确定this的所需值。例如,在事件处理程序回调函数中,浏览器通常将this设置为处理事件的DOM对象。

  7. 对这些不同的方法here on MDN进行了很好的总结。

    因此,在您的情况下,当您致电privateFunction()时,您正在进行正常的函数调用。因此,正如预期的那样,this的值设置为上面的选项2中。

    如果你想在你的方法中明确地将它设置为this的当前值,那么你可以这样做:

    var Example = (function() {
        function Example() {
            function privateFunction() {
                // "this" is window when called.
                console.log(this);
            }
    
            this.publicFunction = function() {
                privateFunction.call(this);
            }
        }
    
        return Example;
    })();
    
    ex = new Example;
    ex.publicFunction();
    

    当您不是函数的调用者并因此不能使用1-4时,其他方法(如使用闭包和定义的var that = this)最适用于回调函数的情况。在您的特定情况下,没有理由这样做。我会说使用.call()是一种更好的做法。然后,您的函数实际上可以使用this,并且可以表现为私有方法,这似乎是您寻求的行为。

答案 3 :(得分:2)

我想最常用的方法就是将缓存(存储)this的值放在本地上下文变量中

function Example() {
    var that = this;
    // ...
    function privateFunction() {
        console.log(that);
    }

    this.publicFunction = function() {
       privateFunction();
    }
}

更方便的方法是调用Function.prototype.bind将上下文绑定到函数(永远)。但是,这里唯一的限制是,这需要一个支持ES5的浏览器,并且绑定功能稍慢。

var privateFunction = function() {
    console.log(this);
}.bind(this);

答案 4 :(得分:1)

我想说正确的方法是使用原型,因为它毕竟是Javascript的设计方式。所以:

var Example = function(){
  this.prop = 'whatever';
}

Example.prototype.fn_1 = function(){
  console.log(this.prop); 
  return this
}

Example.prototype.fn_2 = function(){
  this.prop = 'not whatever';  
  return this
}

var e = new Example();

e.fn_1() //whatever
e.fn_2().fn_1() //not whatever

这是一个小提琴http://jsfiddle.net/BFm2V/

答案 5 :(得分:0)

如果您不使用EcmaScript5,我建议您使用Underscore(或LoDash)bind功能。

答案 6 :(得分:0)

除了这里给出的其他答案之外,如果您没有ES5就绪的浏览器,您可以使用以下代码创建自己的“永久绑定功能”:

function boundFn(thisobj, fn) {
   return function() {
      fn.apply(thisobj, arguments);
   };
}

然后像这样使用它:

var Example = (function() {
    function Example() {
        var privateFunction = boundFn(this, function() {
            // "this" inside here is the same "this" that was passed to boundFn.
            console.log(this);
        });

        this.publicFunction = function() {
            privateFunction();
        }
    }

    return Example;
}()); // I prefer this order of parentheses

Voilà - this神奇地是外在语境this而不是内在语境!

如果您的浏览器中缺少类似ES5的功能,您甚至可以获得类似ES5的功能(如果您已经拥有它,则无效):

if (!Function.prototype.bind) {
   Function.prototype.bind = function (thisobj) {
      var that = this;
      return function() {
         that.apply(thisobj, arguments);
      };
   }:
}

然后以完全相同的方式使用var yourFunction = function() {}.bind(thisobj);

可以在mozilla Function.prototype.bind找到完全符合(尽可能),检查参数类型等类似ES5的代码。如果您正在使用函数执行一些不同的高级操作,那么可能会有一些差异,如果您想要使用这些路径,请在链接上阅读。

答案 7 :(得分:0)

我想说将self分配给this是一种常用技巧:

function Example() {
    var self = this;

    function privateFunction() {
        console.log(self);
    }

    self.publicFunction = function() {
        privateFunction();
    };
}

使用apply(正如其他人所建议的那样)也有效,但我认为它有点复杂。

这可能超出了这个问题的范围,但我还建议您考虑使用不同的JavaScript方法,而实际上根本不使用this关键字。我在ThoughtWorks的一位前同事Pete Hodgson写了a really helpful article, Class-less JavaScript,解释了一种方法。