js闭包是怎么产生的

来源:互联网 发布:阿里云服务器防御 编辑:程序博客网 时间:2024/06/10 14:46

首先来看一个实例

这是 HTML代码

<body><button>添加事件</button><input type="button" value="1"><input type="button" value="2"><input type="button" value="3"><input type="button" value="4"><input type="button" value="5"><input type="button" value="6"><input type="button" value="7"></body>

写法一:

<script type="text/javascript">for (var i = document.getElementsByTagName('input').length-1 ; i >= 0; i--) { document.getElementsByTagName('input')[i].onclick = function(){ alert(i); }}</script>

写法二:

<script type="text/javascript">document.getElementsByTagName('button')[0].onclick = function (){for (var i = document.getElementsByTagName('input').length-1 ; i >= 0; i--) {console.log(i); document.getElementsByTagName('input')[i].onclick = function(){ alert(i); }}}</script>

需求 循环给每一个input,绑定一个点击事件,点击不同的input,会产生不同的值。
最后结果是 点击每个input 都是 弹出一样的值。 我们就来解剖一下这2种写法 为什么会产生这样的结果

写法一:


 我们知道 在JS中 没有java中 {} 的作用域块,只有函数是独立的作用于块。for循环var 的变量产生的变量没有被函数的作用域块包裹。var 出来的i 暴露在全局环境中,自然而然的挂在了 window对象上。那么打印的 i 等同于 widow.i 。
JS知识点 值传递 和 引用传递 大家先来理解这2句话
1.通过值传递参数在函数体内对变量做修改不会影响变量本身
2.通过对象传递参数在函数体内对变量做更改会影响变量本身
方法一没有被函数包裹吧,那就出于在全局环境中,把全局环境看做一个函数体,window就是函数体对象吧。那么就是第二句话通过对象传递参数会更改变量本身。 只有点击的时候才会触发,所以每次取的i就是取的window上面的i 也就是无论点击那个都是弹出一样的值。

写法二:

我们先谈谈闭包是怎么产生的?
官方是这样解释什么是闭包的:
  闭包是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分。
看到这句话就知道闭包与环境有关,与环境有关就离不开作用域。 然而js 作用域中特殊的就是词法作用域
这个词法作用域又称之为静态作用域或者闭包。 词法作用域和闭包或许字面意思难以解释,我们解释静态作用域,静态
作用域,静态的吧。静态作用域就是函数声明时,就已经订好的作用域,以后也不会改变的作用域就是静态作用域。那么为什么
函数声明时就已经订好了作用域呢?  js预处理的时候就已经订好了一切。为什么js会预处理呢?因为js这个语言特性就是这样,在
js代码运行时会首先全盘扫描一遍,把该做的都做了。
所以闭包就是这样产生的:
js语言特性在js代码运行的时候就已经把一切都定死了,作用域什么都已经定好了,所以就产生了闭包。
闭包是js语言的一种特性,闭包通常是一个函数,函数是一个独立的作用域,独立的作用域外部环境无法访问,就是闭。封闭自己的词法作用域,
函数有许多特殊形式的函数,这就成就了闭包 包的东西不同,但是它能够引用到的外部函数的成员变量,一定是它包的东西。可以理解为它能够引用外部函数的成员变量,那它就一定是闭包。
写法二的案例就是一个典型的闭包问题,它闭了自己的词法作用域,包了自己的创建环境。js是没有块级作用域,var i 就是外部函数
的成员变量。 然而它包进来的成员变量是一个引用关系,所以最后打印的i也是for循环处理完成的产物。
解决闭包问题的其中的一种方法,用闭包来毁闭包:

<script type="text/javascript">document.getElementsByTagName('button')[0].onclick = function (){for (var i = document.getElementsByTagName('input').length-1 ; i >= 0; i--) {console.log(i); document.getElementsByTagName('input')[i].onclick =  function(i){ returnfunction(){ alert(i); };}(i);}}</script> 
把外部函数的成员变量当做参数传进去,自执行返回一个独立的词法作用域。取的值的是当时返回的词法作用域已经处理完成的i;

与闭包有关的预处理和作用域我博客都有相关的内容,欢迎各位爱好者参观!

原创粉丝点击