关于JavaScript作用域你想知道的一切(3)

9、.call() and .apply()
.call()和.apply()方法非常友好,其允许给一个函数传作用域来绑定正确的this值。对上面的例子我们通过如下改变,可以使this为当前数组里的每个元素。

var links = document.querySelectorAll('nav li'); for (var i = 0; i < links.length; i++) { (function () { console.log(this); }).call(links[i]);}

能够看到刚将数组循环的当前元素通过links[i]传递进去,这改变了函数的作用域,因此this的值变为当前循环的元素。这个时候,如果需要我们可以使用this。我们既可以使用.call()又可以使用.apply()来改变域。但是这两者使用还是有区别的,其中.call(scope, arg1, arg2, arg3)输入单个参数,而.apply(scope, [arg1, arg2])输入数组作为参数。

非常重要,需要注意的事情是.call() or .apply()实际已经已经取代了如下调用函数的方式调用了函数。

myFunction(); // invoke myFunction
可以使用.call()来链式调用:

myFunction.call(scope); // invoke myFunction using .call()
10、.bind()
和上面不一样的是,.bind()并不触发函数,它仅仅是在函数触发前绑定值。非常遗憾的是其只在 ECMASCript 5中才引入。我们都知道,不能像下面一样传递参数给函数引用:

// works nav.addEventListener('click', toggleNav, false); // will invoke the function immediately nav.addEventListener('click', toggleNav(arg1, arg2), false);

通过在内部创建一个新的函数,我们能够修复这个问题(译注:函数被立即执行):

nav.addEventListener('click', function () { toggleNav(arg1, arg2);}, false);

但是这样的话,我们再次创建了一个没用的函数,如果这是在循环中绑定事件监听,会影响代码性能。这个时候.bind()就派上用场了,在不需要调用的时候就可以传递参数。

nav.addEventListener('click', toggleNav.bind(scope, arg1, arg2), false);

函数并没被触发,scope可以被改变,且参数在等着传递。

11、私有和公开作用域
在许多的编程语言中,存在public和private的作用域,但是在javascript中并不存在。但是在JavaScript中通过闭包来模拟public和private的作用域。

使用JavaScript的设计模式,如Module模式为例。一个创建private的简单方式将函数内嵌到另一个函数中。如我们上面掌握的,函数决定scope,通过scope排除全局的scope:

(function () {// private scope inside here})();

然后在我们的应用中添加一些函数:

(function () { var myFunction = function () {// do some stuff here}; })();

这时当我们调用函数的时候,会超出范围。

(function () {var myFunction = function () { // do some stuff here}; })(); myFunction(); // Uncaught ReferenceError: myFunction is not defined

成功的创建了一个私有作用域。那么怎么让函公有呢?有一个非常好的模式(模块模式)允许通过私有和公共作用域以及一个object对象来正确的设定函数作用域。暂且将全局命名空间称为Module,里面包含了所有与模块相关的代码:

// define module var Module = (function () { return {myMethod: function () { console.log('myMethod has been called.');}}; })(); // call module + methods Module.myMethod();

这儿的return 语句返回了公共的方法,只有通过命名空间才能够被访问到。这就意味着,我们使用Module 作为我们的命名空间,其能够包含我们需要的所有方法。我们可以根据实际的需求来扩展我们的模块。

// define module var Module = (function () { return {myMethod: function () {}, someOtherMethod: function () {}};})(); // call module + methods Module.myMethod(); Module.someOtherMethod();

那私有方法怎么办呢?许多的开发者采取错误的方式,其将所有的函数都至于全局作用域中,这导致了对全局命名空间污染。 通过函数我们能避免在全局域中编写代码,通过API调用,保证可以全局获取。下面的示例中,通过创建不返回函数的形式创建私有域。

var Module = (function () { var privateMethod = function () {}; return { publicMethod: function () {}};})();

这就意味着publicMethod 能够被调用,而privateMethod 由于私有作用域不能被调用。这些私有作用域函数类似于: helpers, addClass, removeClass, Ajax/XHR calls, Arrays, Objects等。

下面是一个有趣事,相同作用域中的对象只能访问相同的作用域,即使有函数被返回之后。这就意味我们的public方法能够访问我们的private方法,这些私有方法依然可以起作用,但是不能够在全局左右域中访问。

var Module = (function () { var privateMethod = function () {}; return {publicMethod: function () { // has access to `privateMethod`, we can call it: // privateMethod();}};})();

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wgwwgf.html