Javascript 闭包
一、作用域链
内部环境可以通过作用域链访问外部环境中的任何变量和函数,但外部环境不可以访问内部环境的。任何环境都可以向上搜索而进入作用域链中另一个执行环境,访问其变量和函数,但不能通过向下搜索进入另一个执行环境。
var color = "red"; function changeColor(){ var anotherColor = "blue"; function swapColors(){ var tempColor = color; color = anotherColor; anotherColor = tempColor; //这里可以访问的变量tempColor, anotherColor, color (swapColors()局部环境内) } swapColors(); //这里可以访问的变量anotherColor和color (changeColor()局部环境内) } changeColor(); //这里可以访问的变量color (全局环境)
二、闭包
闭包是一个函数有权访问另一个函数作用域中的变量,常见方式是在一个函数中创建另一个函数。个人理解就是一个函数里套着另外一个函数,里面的函数可以访问外面函数的变量等,但外面的不可以访问里面的。
坑1:
function createFunctions(){ var result = new Array() for(var i=0; i <10; i ++){ result[i] = function(){ return i; } } return result; //result[i]返回的是一个函数,当返回时相关的参数和变量都保存在返回的函数中,所以存储的一直是i =10 } var funcs = createFunctions(); for(var i =0; i< funcs.length; i++){ document.write(func[i]() + " "); } // 10 10 10 10 10 10 10 10 10 10
修复:
//方法1:var改成let function createFunctions(){ var result = new Array() for(let i=0; i <10; i ++){ //ES6中var改为let.因为let的作用域是以代码块({})为区域的,出了该代码块失效,而var的作用域是函数内,会自动记忆变量值。 result[i] = function(){ return i; } } return result; }
//方法2:创建另一个匿名函数,让闭包强制符合预期 function createFunctions(){ var result = new Array() for(var i=0; i <10; i ++){ result[i] = function(num){ return function(){ return num; } } } return result; }
//方法3: 同2创建一个匿名函数,专门用来储存原先需要引用的内容(下标) function createFunctions(){ var result = new Array(); for (var i=0; i < 10; i++){ (function(arg){ result[i] = function(){ return arg; }; })(i); } return result; }
坑2:this
闭包中this指向全局对象
var name = "the Window"; var object = { name : "My Object", getNameFun: function(){ return function(){ return this.name; } } } console.log(object.getNameFun()()); //the Window 非严格模式
修复:
//修复:将外部变量包含在一个闭包能直接访问的环境了就可以解决这个问题 var name = "the Window"; var object = { name : "My Object", getNameFun: function(){ var that = this; return function(){ return that.name; } } } console.log(object.getNameFun()()); //My Object
三、性能
如果不是特殊要求,尽量不要使用闭包,因为闭包对脚本性能有负面影响,包括处理速度和内存消耗。
示例(bad):
function MyObject(name, message){ this.name = name.toString(); this.message = message.toString(); this.getName = function(){ return this.name; } this.getMessage = function(){ return this.message; } }
修改:
//1、修改为常规形式 function MyObject(name, message){ this.name = name.toString(); this.message = message.toString(); } MyObject.prototype = { getName: function(){ return this.name; }, getMessage: function(){ return this.message; } }
//2、或者改成 function MyObject(name, message){ this.name = name.toString(); this.message = message.toString(); } MyObject.prototype.getName = function(){ return this.name; } MyObject.prototype.getMessage = function(){ return this.message; }
//3、更简洁 function MyObject(name, message){ this.name = name.toString(); this.message = message.toString(); } (function(){ this.getName = function(){ return this.name; }; this.getMessage = function(){ return this.message; }; }).call(MyObject.prototype);
学习感悟:能不用闭包就不用闭包(函数嵌套函数),复杂又出力不讨好。可能后续会有不同理解……
参考:
1、JavaScript高级程序设计(第3版)