作用域与闭包 讲解 this, var, (function(){})    
知识点 
理解js中var的作用域 
了解闭包的概念 
理解this的指向 
 
课程内容 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 var parent = function () {     var name = "parent_name";     var age = 13;     var child = function () {         var name = "child_name";         var childAge = 0.3;         console.log(name, age, childAge);     };     child();     console.log(name, age, childAge); }; parent(); 
 
     
学过一点别的编程语言肯定都会直觉的认为,内部函数可以访问外部函数的变量,外部不能访问内部函数的变量.因此childAge在parent中会报错.如果声明变量是少了var,就声明就全局变量了. 在Nodejs中,全局变量会被定义在global对象下,在浏览器中,全局变量会被定义在window下. 如果需要定义全局变量,请显示的定义在global或window对象上.    
 
如果使用Sublime Text,可以按照SublimeLinter插件,进行错误检测,默认使用JSHint作为JavaScript校验器 作者使用Mac OS X, Sublime text 2->Preferences->Package Setting->SublimeLinter->Setting - User配置    
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 {     "sublimelinter": "save-only", //运行模式     "sublimelinter_popup_errors_on_save": true,     "sublimelinter_executable_map": {         "javascript": "D:/Program Files/nodejs/node.exe",         "css": "D:/Program Files/nodejs/node.exe"     },     "jshint_options": {         "strict": false,         "quotmark": "single", //只能使用单引号         "noarg": true,         "noempty": true, //不允许使用空语句块{}         "eqeqeq": true, //!==和===检查         "undef": true,         "curly": true, //值为true时,不能省略循环和条件语句后的大括号         "forin": true, //for in hasOwnPropery检查         "devel": true,         "jquery": true,         "browser": true,         "wsh": true,         "evil": true,         "unused": "vars", //形参和变量未使用检查         "latedef": true, //先定义变量,后使用         "globals": {             "grunt": true,             "module": true,             "window": true,             "jQuery": true,             "$": true,             "global": true,             "document": true,             "console": true,             "setTimeout": true,             "setInterval": true         }     },     "csslint_options": {         "adjoining-classes": false,         "box-sizing": false,         "box-model": false,         "compatible-vendor-prefixes": false,         "floats": false,         "font-sizes": false,         "gradients": false,         "important": false,         "known-properties": false,         "outline-none": false,         "qualified-headings": false,         "regex-selectors": false,         "shorthand": false,         "text-indent": false,         "unique-headings": false,         "universal-selector": false,         "unqualified-attributes": false     } } 
 
1 2 3 4 5 SublimeLinter 的运行模式,总共有四种,含义分别如下:     true - 在用户输入时在后台进行即时校验     false - 只有在初始化的时候才进行校验     "load-save" - 当文件加载和保存的时候进行校验     "save-only" - 当文件被保存的时候进行校验 
 
校验引擎,Mac下使用which node获取路径 
 
1 2 3 4 5 "sublimelinter_executable_map": {     "javascript":"/usr/local/bin/node",     "css":"/usr/local/bin/node" } 
 
更多的请参考文档: http://jshint.com/docs/#options  作者英文只有小学一级,搞不定     
     
在JavaScript中,变量的局部作用域是函数级别的.不同于C语言,在C语言中作用域是块级别. js中,函数中声明的变量在整个函数中都可以使用.     
1 2 3 4 5 6 7 8 function foo() {     for (var i = 0; i < 10; i++) {         var value = "hello world";     }     console.log(i);     console.log(value); } foo(); 
 
所以我们养成一个好习惯,提前声明    
闭包 http://coolshell.cn/articles/6731.html   这篇博客可以参考参考
1 2 3 4 5 for (var i = 0; i < 5; i++) {     setTimeout(function() {         console.log(i);     }, 5); } 
 
程序不会按照我们预期的结果打印1 2 3 4 5. 因为setTimeout中的i是对外层i的引用.当setTimeout的代码被解释的时候,运行时只是记录了i的引用,而不是记录值.当setTimeout被触发时取i值,i已迭代为5,所以打印了5次5     
1 2 3 4 5 6 7 for (var i = 0; i < 5; i++) {     (function(idx) {         setTimeout(function() {             console.log(idx);         }, 5);     })(i); } 
 
程序变成这样,结果和预期一样     
一些闭包的例子:     
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 /* //闭包中使用局部变量是引用而非直接拷贝 function s() {     var num = 6;     var sa = function() { console.log(num); };     num++;     return sa; } var sa = s(); sa(); // ==> // 7 */ /* //多个函数绑定同一个闭包,引用同一个变量 function setupSomeGlobals () {     var num = 666;     gAlertNumber = function(){ console.log(num); };     gIncreaseNumber = function() { num++; };     gSetNumber = function(x) { num = x; }; } setupSomeGlobals(); gAlertNumber(); gIncreaseNumber(); gAlertNumber(); gSetNumber(18); gAlertNumber(); // ==> // 666 // 667 // 18 */ /* //当在一个循环中赋值函数时,这些函数将绑定同样的闭包,函数内使用了外部变量的引用 function buildList(list) {     var result = [];     for(var i = 0; i < list.length; i++) {         var item = 'item' + list[i];         result.push(function(){ console.log(item + ' ' + list[i]); });     }     return result; } function testList() {     var fnlist = buildList([1, 2, 3]);     for (var j = 0; j < fnlist.length; j++) {         fnlist[j]();     }; } testList(); */ // ==> // item3 undefined // item3 undefined // item3 undefined /* //外部函数所有局部变量都在闭包内,即使这个变量声明在内部函数定义之后 function sa() {     var sas = function(){ console.log(alice); };     var alice = "Hello world";     return sas; } var hello = sa(); hello(); // ==> // Hello world */ /* // 每次函数调用的时候创建一个新的闭包 function newClosure(someNum, someRef) {     var num = someNum;     var anArray = [1, 2, 3];     var ref = someRef;     return function(x) {         num += x;         anArray.push(num);         console.log('num: ' + num + '\nanArray' + anArray.toString() + '\nref.someVar' + ref.someVar);     }; } closure1 = newClosure(400, { someVar: 'closure 1'}); closure2 = newClosure(400, { someVar: 'closure 2'}); closure1(5); closure2(-10); // ==> // num: 405 // anArray1,2,3,405 // ref.someVarclosure 1 // num: 390 // anArray1,2,3,390 // ref.someVarclosure 2 */ 
 
this 在函数执行时,this总是指向调用该函数的对象.要判断this的指向,其实就是判断this所在的函数属于谁. this出现的场景分为四类:
有对象就指向调用对象 
没调用对象就指向全局对象 
用new构造就指向新对象 
通过apply或call或bind来改变this的所指 
 
函数有所属对象是,通常通过.表达式调用,这时的this自然指向所属对象.    
1 2 3 4 5 6 7 8 var obj = { value : 100}; obj.getValue = function() {     // { value: 100, getValue: [Function] }     // 其实就是obj对象本身     console.log(this);     return this.value; }; console.log(obj.getValue()); 
 
getValue()方法属于对象obj,有obj进行.调用,因此this指向对象obj
1 2 3 4 5 6 7 8 9 10 var obj = { value : 100}; obj.getValue = function() {     var foo = function () {         console.log(this.value); // => undefined         console.log(this);       // 全局global对象     };     foo();     return this.value; }; console.log(obj.getValue()); 
 
在上述代码块中,foo函数虽然定义在getValue的函数体内,但实际上它既不属于getValue也不属于obj.foo并没有绑定在任何对象上,所以当调用时,它的this指针指向了全局对象global.     
js中,我们通过new关键词来调用构造函数,此时this会绑定在该新对象上.     
1 2 3 4 5 var objClass = function () {     this.value = 100; }; var create = new objClass(); console.log(create.value); // => 100 
 
在js中,构造函数、普通函数、对象方法、闭包,这死者没有明确界线.
apply、call调用和bind绑定:指向绑定的对象      
 
apply()方法接受两个参数,第一个是函数运行的作用域,另一个是参数数组(arguments) call()方法第一个参数的意义与apply()方法相同,只是其他的参数需要一个个列举出来 简单来说,call的方式更接近我们平时调用函数,而apply需要我们传递Array形式的数组给它.他们可以互相转换.     
1 2 3 4 5 6 7 8 9 var obj = { value: 100 }; var foo = function () {     console.log(this); }; foo();              //全局变量 global foo.apply(obj);      foo.call(obj); var n = foo.bind(obj); n();