确保“安全”的最佳实践是什么;这";Javascript中的上下文?

确保“安全”的最佳实践是什么;这";Javascript中的上下文?,javascript,this,Javascript,This,下面是一个带有公共和私有方法(fiddle:)的简单Javascript类的示例 从公共函数调用私有函数会导致“this”成为窗口对象。如何确保使用类上下文而不是窗口调用我的私有方法?这是不可取的吗?我想最常用的方法是将this的值缓存(存储)在本地上下文变量中 function Example() { var that = this; // ... function privateFunction() { console.log(that); }

下面是一个带有公共和私有方法(fiddle:)的简单Javascript类的示例


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

我想最常用的方法是将
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);

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

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

如果您不使用ECMAScript 5,我建议您使用下划线(或LoDash)函数。

除了这里给出的其他答案之外,如果您没有支持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
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();
瞧——
这个
神奇地是外部上下文的
这个
,而不是内部上下文

如果您的浏览器中缺少类似ES5的功能,您甚至可以获得类似ES5的功能(如果您已经拥有了它,则不会有任何作用):

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


与ES5类似的完全兼容(尽可能)的代码,检查参数类型等,可以在中找到。如果您正在使用函数执行一些不同的高级操作,则可能会遇到一些差异,因此如果您想使用这些功能,请在链接中阅读这些差异。

另一种方法是使用“应用”明确设置“this”应绑定的方法

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
另一种方法是使用“调用”:


“apply”和“call”都将要绑定“this”的对象作为第一个参数,并将一组参数作为第二个参数传递给正在调用的方法。

除了让别人告诉您代码修复之外,还需要了解javascript中this
的值是如何确定的。在javascript中,此由以下方式确定:

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

  • 如果在没有任何对象引用(如
    function()
    )的情况下直接调用函数,则
    将设置为全局对象(
    浏览器中的窗口
    ),或在严格模式下,将其设置为
    未定义

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

  • 如果使用
    .call()
    .apply()
    调用函数,就像在
    函数.call(xxx)
    中一样,那么您可以通过传递给
    .call()
    .apply()
    的参数来准确确定此
    设置为什么。您可以阅读有关MDN的更多信息

  • 如果使用
    function.bind(xxx)
    这将创建一个小存根函数,确保使用所需的
    this
    值调用函数。在内部,这可能只使用了
    .apply()
    ,但它是一个快捷方式,用于在调用单个回调函数时(当您不是该函数的直接调用方时),该函数将具有正确的
    this

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

  • 对这些不同的方法有一个很好的总结

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

    如果要在方法中将其明确设置为当前值
    this
    ,则可以这样做:

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

    其他方法,如使用闭包和defined
    var=this
    最适合用于回调函数,因为您不是函数的调用方,因此不能使用1-4。在你的特殊情况下,没有理由这样做。我想说使用
    .call()
    是一种更好的做法。然后,您的函数实际上可以使用
    这个
    ,并且可以像私有方法一样运行,这似乎就是您所寻求的行为。

    我想说,将
    自身
    分配给
    这个
    是一种常见的技术:

    function Example() {
        var self = this;
    
        function privateFunction() {
            console.log(self);
        }
    
        self.publicFunction = function() {
            privateFunction();
        };
    }
    
    使用
    apply
    (正如其他人所建议的)也可以,尽管在我看来有点复杂


    这可能超出了这个问题的范围,但我也建议您考虑一种不同的JavaScript方法,您实际上根本不使用
    this
    关键字。我在ThoughtWorks的前同事皮特·霍奇森(Pete Hodgson)写道,他解释了一种方法。

    我认为正确的方法是使用原型,因为它毕竟是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
    

    这里有一个小把戏

    可以在这里找到管理
    范围的小技巧:另一个解决方案:与
    问题无关,您不能从内部
    函数示例()
    周围移除立即调用的匿名函数包装器吗?在我看来,如果您只声明
    function Example()
    di,您会得到相同的结果
    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