javascript中的作用域和闭包详解


Posted in Javascript onJanuary 13, 2016

一、JavaScript作用域

JavaScript变量实际上只有两种作用域,全局变量和函数的内部变量。在函数内部任何一个地方定义的变量(var scope)其作用域都是整个函数体。
全局变量:指的是window对象下的对象属性。
作用域划分:基于上下文,以函数进行划分的,而不是由块划分的。
强调两点:
1. 在同一作用域中,JavaScript是允许变量的重复定义,并且后一个定义将覆盖前一个定义。
2. 函数内部如果不加关键字var而定义的变量,默认为全局变量。

var scope="global"; 
function t(){ 
  console.log(scope); //"global" 
  scope="local" 
  console.log(scope); //"local" 
} 
t(); 
console.log(scope); //"local" 




var scope="global"; 
function t(){ 
  console.log(scope); //"undefined" 
  var scope="local" 
  console.log(scope); //"local" 
} 
t(); 
console.log(scope); //"global"

在变量解析过程中首先查找局部的作用域,然后查找上层作用域。在第一段代码的函数当中没有定义变量scope,于是查找上层作用域(全局作用域),进而进行输出其值。但是在第二段代码的函数内定义了变量scope(无论是在console之后还是之前定义变量,都认为在此作用域拥有变量scope),于是不再向上层的作用域进行查找,直接输出scope。但是不幸的是此时的局部变量i并没有赋值,所以输出的是undefined。

//所以根据函数作用域的意思,可以将上述第二段代码重写如下: 
var scope="global"; 
function t(){ 
  var scope; 
  console.log(scope); 
  scope="local" 
  console.log(scope); 
} 
t();

由于函数作用域的特性,局部变量在整个函数体始终是由定义的,我们可以将变量声明”提前“到函数体顶部。

var b; //第1步 
function fun(){  
  b = "change";  
}  
alert(b);//输出undefined,由于第1步只定义未赋值 
 
 
var b; //第1步 
function fun(){  
  b = "change";  
} 
fun(); //调用上述函数 
alert(b); //输出change

当使用var声明一个变量时,创建的这个属性是不可配置的,也就是说无法通过delete运算符删除。
二、作用域实例

<html> 
<head> 
  <script type="text/javascript"> 
    function buttonInit(){ 
      for(var i=1;i<4;i++){ 
        var b=document.getElementById("button"+i); 
        b.addEventListener("click",function(){ alert("Button"+i);},false); 
      } 
    } 
    window.onload=buttonInit; 
  </script> 
</head> 
<body> 
  <button id="button1">Button1</button> 
  <button id="button2">Button2</button> 
  <button id="button3">Button3</button> 
</body> 
</html>

当注册事件结束后,i的值为4,当点击按钮时,事件函数即function(){ alert("Button"+i);}这个匿名函数中没有i,根据作用域链,所以到buttonInit函数中找,此时i的值为4,所以弹出”button4“。
三、javaScript闭包
在js中,闭包主要涉及到js的几个其他的特性:作用域链,垃圾(内存)回收机制,函数嵌套,等等。
1. 作用域链:简单来说,作用域链就是函数在定义的时候创建的,用于寻找使用到的变量的值的一个索引,而他内部的规则是,把函数自身的本地变量放在最前面,把自身的父级函数中的变量放在其次,把再高一级函数中的变量放在更后面,以此类推直至全局对象为止。当函数中需要查询一个变量的值的时候,js解释器会去作用域链去查找,从最前面的本地变量中先找,如果没有找到对应的变量,则到下一级的链上找,一旦找到了变量,则不再继续。如果找到最后也没找到需要的变量,则解释器返回undefined。
2. Javascript的垃圾回收机制:在Javascript中,如果一个对象不再被引用,那么这个对象就会被GC回收。如果两个对象互相引用,而不再被第3者所引用,那么这两个互相引用的对象也会被回收。因为函数a被b引用,b又被a外的c引用,这就是为什么函数a执行后不会被回收的原因。构建一个闭包,这些变量将不会被内存回收器所回收,只有当内部的函数不被调用以后,才会销毁这个闭包,而没有任何一个闭包引用的变量才会被下一次内存回收启动时所回收。
3. 有了闭包,嵌套的函数结构才可以运作
四、利用js闭包实现循环绑定事件

<html> 
<head> 
  <title>闭包</title> 
</head> 
<body> 
  <ul id="list"> 
    <li>第1条记录</li> 
    <li>第2条记录</li> 
    <li>第3条记录</li> 
    <li>第4条记录</li> 
    <li>第5条记录</li> 
    <li>第6条记录</li> 
  </ul> 
  <script type="text/javascript"> 
    function tt(nob) { 
      this.clickFunc = function() { 
        alert("这是第" + (nob + 1) + "记录"); 
      } 
    } 
    var list_obj = document.getElementById("list").getElementsByTagName("li"); //获取list下面的所有li的对象数组 
    for (var i = 0; i<= list_obj.length; i++){ 
      console.log(list_obj[i]) 
      list_obj[i].onmousemove = function(){ 
        this.style.backgroundColor = "#cdcdcd"; 
      } 
      list_obj[i].onmouseout = function() { 
        this.style.backgroundColor = "#FFFFFF"; 
      } 
      //list_obj[i].onclick = function() { 
      // alert("这是第" + i + "记录"); //不能正常获取 alert出来的都是:“这是第6记录” 
      //} 
      var col = new tt(i); //调用tt函数 
      list_obj[i].onclick = col.clickFunc; //执行clickFunc函数 
    } 
  </script> 
</body> 
</html>

以上就是本文的全部内容,希望对大家学习javascript程序设计有所帮助。

Javascript 相关文章推荐
Javascript 圆角div的实现代码
Oct 15 Javascript
ExtJS PropertyGrid中使用Combobox选择值问题
Jun 13 Javascript
js动态添加删除,后台取数据(示例代码)
Nov 25 Javascript
js中直接声明一个对象的方法
Aug 10 Javascript
详解Angular中$cacheFactory缓存的使用
Aug 19 Javascript
微信小程序中页面FOR循环和嵌套循环
Jun 21 Javascript
react-native-tab-navigator组件的基本使用示例代码
Sep 07 Javascript
JS获取当前地理位置的方法
Oct 25 Javascript
angular4自定义组件非input元素实现ngModel双向数据绑定的方法
Dec 28 Javascript
微信小程序实现展示评分结果功能
Feb 15 Javascript
JavaScript中this函数使用实例解析
Feb 21 Javascript
实用的 vue tags 创建缓存导航的过程实现
Dec 03 Vue.js
JSON+Jquery省市区三级联动
Jan 13 #Javascript
Easyui form combobox省市区三级联动
Jan 13 #Javascript
轻松实现javascript图片轮播特效
Jan 13 #Javascript
简单的JS时钟实例讲解
Jan 13 #Javascript
基于jquery实现的仿优酷图片轮播特效代码
Jan 13 #Javascript
详解iframe与frame的区别
Jan 13 #Javascript
浅析JavaScript中的变量复制、参数传递和作用域链
Jan 13 #Javascript
You might like
实例(Smarty+FCKeditor新闻系统)
2007/01/02 PHP
解析PHP提交后跳转
2013/06/23 PHP
php curl获取网页内容(IPV6下超时)的解决办法
2013/07/16 PHP
Yii框架实现邮箱激活的方法【数字签名】
2016/10/18 PHP
laravel手动创建数组分页的实现代码
2018/06/07 PHP
PHP观察者模式定义与用法实例分析
2019/03/22 PHP
php curl发送请求实例方法
2019/08/01 PHP
在 Laravel 6 中缓存数据库查询结果的方法
2019/12/11 PHP
JQuery.ajax传递中文参数的解决方法 推荐
2011/03/28 Javascript
jQuery对表单的操作代码集合
2011/04/06 Javascript
关于跨站脚本攻击问题
2011/12/22 Javascript
从JQuery源码分析JavaScript函数的apply方法与call方法
2014/09/25 Javascript
javascript实现状态栏中文字动态显示的方法
2015/10/20 Javascript
Ajax分页插件Pagination从前台jQuery到后端java总结
2016/07/22 Javascript
jQuery动态添加与删除tr行实例代码
2016/10/18 Javascript
微信分享调用jssdk实例
2017/06/08 Javascript
jQuery动态添加li标签并添加属性和绑定事件方法
2018/02/24 jQuery
vue组件表单数据回显验证及提交的实例代码
2018/08/30 Javascript
vue cli3 调用百度翻译API翻译页面的实现示例
2019/09/13 Javascript
TypeScript之调用栈的实现
2019/12/31 Javascript
python将txt等文件中的数据读为numpy数组的方法
2018/12/22 Python
Django框架实现的普通登录案例【使用POST方法】
2019/05/15 Python
Django 路由控制的实现
2019/07/17 Python
python使用 request 发送表单数据操作示例
2019/09/25 Python
关于Python中定制类的比较运算实例
2019/12/19 Python
python 爬虫爬取京东ps4售卖情况
2020/12/18 Python
HTML5操作WebSQL数据库的实例代码
2017/08/26 HTML / CSS
世界最大域名注册商:GoDaddy
2016/07/24 全球购物
美国最大的香水出口:FragranceX.com
2017/11/04 全球购物
static全局变量与普通的全局变量有什么区别?static局部变量和普通局部变量有什么区别?static函数与普通函数有什么区别?
2015/02/22 面试题
介绍下java.util.Arrays类
2012/10/16 面试题
销售总监工作职责
2013/11/21 职场文书
《小石潭记》教学反思
2014/02/13 职场文书
铲车司机岗位职责
2014/03/15 职场文书
构建和谐校园倡议书
2015/01/19 职场文书
社区干部培训心得体会
2016/01/06 职场文书