js闭包详解

xiaoxiao2021-02-27  153

JS闭包的定义

当一个函数即便在离开了它的词法作用域(Lexical Scope)的情况下,仍然存储并可以存取它的词法作用域(Lexical Scope),这个函数就构成了闭包。由定义我们不难发现闭包和作用域有很多联系。

JS闭包的作用

实现变量的“缓存”(使用不好容易出现内存泄漏)。单列模式代码模块化

JS闭包的表现

闭包是一个函数。嵌套在另一个函数里面,并且调用其变量。被调用参数和变量不会被垃圾回收机制回收 function outer() { var local_var = "I'm local"; //不会被立刻回收 return function inner() { //闭包函数 console.log("local: " + local_var); }; }

JS 闭包的实例讲解(最全面)

变量跨域访问的问题 var global_var = "I'm global"; function A() { var local_var = "I'm local"; console.log("global: " + global_var) } console.log("local: " + local_var); // 引用错误,类型是 ReferenceError: local_var is not defined

这就是上面所提到的js作用域问题了,函数内形成自己的作用块级作用域,可以访问全局变量。反之却不行,全局作用域内,却不能访问块级作用域的变量。但如果我们想访问到呢,第一种方式当然就是设置为全局变量,第二种就是用闭包了,下面看第二种解决方案。

function outer() { var local_var = "I'm local"; //不会被立刻回收 return function inner() { //闭包函数 return local_var; }; } var getLocal_var = new outer(); // 当然你也可以直接new outer()(); var local_varValue = getLocal_var(); 最常见setTimeout函数问题 for (var i = 1; i <= 5; i++) { setTimeout( function timer(){ console.log( i ); }, i*1000 ); }

这个例子相信大家再熟悉不过了,首先timer()形成闭包函数。直觉上都会认为输出为1,2,3,4,5。很明显这个结论是错误,实际结果为6,6,6,6,6。 首先是js执行为单线程,所以当循环执行完之后才会开始执行setTimeout函数。所以最后执行的时候i已经变成6了,可能有人会问,每一个闭包不应该都会有自己的一份拷贝吗?恭喜你,你的理解是对的,但这个方法却没有这样做,它都是调用了i这个变量,也就是说没有保存自己的那一个i。那么问题也就好解决了,我们给它传一个属于自己i。

for (var i=1; i<=5; i++) { (function(j){ setTimeout( function timer(){ console.log( j ); }, j*1000 ); })(i); } 来看一下阮一峰老师的思考题   var name = "The Window";   var object = {     name : "My Object",     getNameFunc : function(){     var this_ = this; // 把第二个集合在一起了       return function(){         return this.name+'加'+this_.name;       };     }   };   alert(object.getNameFunc()());

相信大家已经知道答案 this. name = The Window ,this_.name = My Object;,首先我们需要解析的是object.getNameFunc()()到底是怎么运行的呢?

首先 object.getNameFunc = function(){     var this_ = this;       return function(){         return this.name+'加'+this_.name;       };     } 其次 object.getNameFunc() = function(){         return this.name+'加'+this_.name;       };

而最后的object.getNameFunc()()相信大家已经知道它执行的是哪一个函数了。这边还会涉及到一个this指向的问题。永远指向调用它的函数,如果没有就指向window。所以this_ 指向了object 对象 。而 this 没有指向任何对象,所以指向了window,这也就是答案的由来。

复杂的面试题 function fun(n,o) { // 第一个fun console.log(o) return { fun:function(m){ // 第二个fun return fun(m,n); // 还是第一个 fun } }; } var a = fun(0); a.fun(1); a.fun(2); a.fun(3);//undefined,?,?,? var b = fun(0).fun(1).fun(2).fun(3);//undefined,?,?,? var c = fun(0).fun(1); c.fun(2); c.fun(3);//undefined,?,?,?

可能很多人知道答案,却不是很理解,仔细看下面的分析,主要我们需要认清fun指向的是哪一个,最终执行的函数是什么

1: fun(0) 最后返回的是什么,作用是什么? a = { fun:function(m){ // 这是返回 return fun(m,n); } } 而作用就是将n置为了0,可能有人会问,执行完不应会被回收吗?当然不是这样,这个n = 0这个参数被下面的函数所引用,所以不会被垃圾回收机制销毁,这就是闭包的作用之一。 a.fun(1) = fun(0).fun(1); a.fun(2) = fun(0).fun(2); …… 实际上就是在执行 fun(1,n) fun(2,n) fun(3,n) 当然是有返回值的,我们这边不讨论,又因为n被赋值为0,所以其实就是在执行 fun(1,0) fun(2,0) fun(3,0) 这样相信大家都知道打印为0 0 0了吧2:fun(0).fun(1)相信大家都知道为0,但是我们还需要关注一下这个结果的返回值是什么? fun(0).fun(1) 最后执行的为fun(1,0) {n = 0,o = 0} 所以返回值依然是 { fun:function(m){ // 这是返回 return fun(m,n); } 所以fun(0).fun(1).fun(2) 就是在执行fun(2,1) 打印1 n = 2 ,o = 1,以此类推 fun(0).fun(1).fun(2).fun(3) 打印23:和第二题一样,fun(0).fun(1) 依次打印 underfined 和0 c.fun(2) = fun(0).fun(1).fun(2) 根据第二题的分析为1,c.fun(3) = fun(0).fun(1).fun(3),这边不在是2而是1,虽然第二次的n已经变成了2,但是因为你又重新执行了fun(0).fun(1) n重新被赋值为1了,所以最后执行的就是fun(3,1) 打印1

闭包常见用法

单利模式 var Singleton = (function () { var instance; function createInstance() { return new Object("I am the instance"); } return { getInstance: function () { if (!instance) { instance = createInstance(); } return instance; } }; })(); function run() { var instance1 = Singleton.getInstance(); var instance2 = Singleton.getInstance(); console.log("Same instance? " + (instance1 === instance2)); } 代码模块化(闭包模拟私有方法) function CoolModule() { var something = "cool"; var another = [1, 2, 3]; function doSomething() { console.log( something ); } function doAnother() { console.log( another.join( " ! " ) ); } return { doSomething: doSomething, doAnother: doAnother }; } var foo = CoolModule(); foo.doSomething(); // cool foo.doAnother(); // 1 ! 2 ! 3 对象与方法关联 body { font-family: Helvetica, Arial, sans-serif; font-size: 12px; } #test-h1 { font-size: 1.5em; } #test-h2 { font-size: 1.2em; } #test-h3 { font-size: 1.2em; } 可能以前我们需要点击来改变body字体的大小的方法为 document.getElementById('size-12').onclick = function(){ document.body.style.fontSize ='12px'; }; 我们常见的做法就是,响应事件然后执行的函数,但如果需要改变大小的样式有很多,比如12px一个按钮,14px一个按钮,16px一个按钮,如果以前的方式来写就很麻烦,而闭包可以为我们提供便利 function makeSizer(size) { return function() { document.body.style.fontSize = size + 'px'; }; } var size12 = makeSizer(12); var size14 = makeSizer(14); var size16 = makeSizer(16); document.getElementById('size-12').onclick = size12; document.getElementById('size-14').onclick = size14; document.getElementById('size-16').onclick = size16; 是不是很简单呢?

ES6 解决闭包

块级作用域let 替代 var 了解详情请阅读《ECMAScript 6 入门-阮一峰老师》(http://es6.ruanyifeng.com/)

本文参考

George MDN阮一峰的网络日志-学习闭包
转载请注明原文地址: https://www.6miu.com/read-16013.html

最新回复(0)