var name = 'Todd'; var scope1 = function () { // name is available here var scope2 = function () {// name is available here too var scope3 = function () {// name is also available here!}; }; };
唯一需要注意的事情是词汇域不后项起作用,下面的方式词汇域是不起作用的:
// name = undefined var scope1 = function () { // name = undefined var scope2 = function () {// name = undefined var scope3 = function () {var name = 'Todd'; // locally scoped}; }; };
能返回对name的引用,但是永远也无法返回变量本身。
5、作用域链
函数的作用域由作用域链构成。我们知道,每个函数可以定义嵌套的作用域,任何内嵌函数都有一个局部作用域连接外部函数。这种嵌套关系我们可以称为链。域一般由代码中的位置决定。当解释(resolving)一个变量,通常从作用域链的最里层开始,向外搜索,直到发现要寻找的变量、对象或者函数。
6、闭包(Closures)
闭包和词法域( Lexical Scope)很像。返回函数引用,这种实际应用,是一个可以用来解释闭包工作原理的好例子。在我们的域内部,我们可以返回对象,能够被父域使用。
var sayHello = function (name) { var text = 'Hello, ' + name; return function () { console.log(text);}; };
这里我们使用的闭包,使得我们的sayHello内部域无法被公共域访问到。单独调用函数并不作任何操作,因为其单纯的返回一个函数。
sayHello('Todd'); // nothing happens, no errors, just silence...
函数返回一个函数,也就意味着需要先赋值再调用:
var helloTodd = sayHello('Todd'); helloTodd(); // will call the closure and log 'Hello, Todd'
好吧,欺骗大家感情了。在实际情况中可能会遇到如下调用闭包的函数,这样也是行的通的。
sayHello2('Bob')(); // calls the returned function without assignment
Angular js 在$compile方法中使用上面的技术,可以将当前引用域传入到闭包中
$compile(template)(scope);
意味着我们能够猜出他们的代码(简化)应该如下:
var $compile = function (template) { // some magic stuff here// scope is out of scope, though... return function (scope) {// access to `template` and `scope` to do magic with too}; };
闭包并不一定需要返回函数。单纯在中间词汇域量的范围外简单访问变量就创造了一个闭包。
7、作用域和this关键字
根据函数被触发的方式不一样,每个作用域可以绑定一个不同的this值。我们经常使用this,但是我们并不是都了解其具体指代什么。 this默认是执行最外层的全局对象,windows对象。我们能够很容易的列举出不同触发函数绑定this的值也不同:
var myFunction = function () { console.log(this); // this = global, [object Window]}; myFunction(); var myObject = {}; myObject.myMethod = function () { console.log(this); // this = Object { myObject }}; var nav = document.querySelector('.nav'); // <nav> var toggleNav = function () { console.log(this); // this = <nav> element}; nav.addEventListener('click', toggleNav, false);
在处理this值的时候,也会遇到问题。下面的例子中,即使在相同的函数内部,作用域和this值也会不同。
var nav = document.querySelector('.nav'); // <nav> var toggleNav = function () { console.log(this); // <nav> element setTimeout(function () { console.log(this); // [object Window]}, 1000); }; nav.addEventListener('click', toggleNav, false);
发生了什么?我们创建了一个新的作用域且没有在event handler中触发,所以其得到预期的windows对象。如果想this值不受新创建的作用域的影响,我们能够采取一些做法。以前可能也你也见过,我们使用that创建一个对this的缓存引用并词汇绑定:
var nav = document.querySelector('.nav'); // <nav> var toggleNav = function () { var that = this; console.log(that); // <nav> element setTimeout(function () { console.log(that); // <nav> element}, 1000); }; nav.addEventListener('click', toggleNav, false);
这是使用this的一个小技巧,能够解决新创建的作用域问题。
8、使用.call(), .apply() 和.bind()改变作用域
有时候,需要根据实际的需求来变化代码的作用域。一个简单的例子,如在循环中如何改变作用域:
var links = document.querySelectorAll('nav li'); for (var i = 0; i < links.length; i++) { console.log(this); // [object Window]}
这里的this并没有指向我们的元素,因为我们没有触发或者改变作用域。我们来看看如何改变作用域(看起来我们是改变作用域,其实我们是改变调用函数执行的上下文)。