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 最常用的10个自定义函数[推荐]
Dec 26 Javascript
浏览器脚本兼容 文本框中,回车键触发事件的兼容
Jun 21 Javascript
javascript 获取iframe里页面中元素值的方法
Feb 17 Javascript
jQuery使用模式窗口实现在主页面和子页面中互相传值的方法
Mar 01 Javascript
HTML5 实现的一个俄罗斯方块实例代码
Sep 19 Javascript
Angularjs2不同组件间的通信实例代码
May 06 Javascript
微信小程序实现下载进度条的方法
Dec 08 Javascript
用npm安装vue和vue-cli,并使用webpack创建项目的方法
Sep 28 Javascript
JS对象和字符串之间互换操作实例分析
Feb 02 Javascript
JS实现的定时器展示简单秒表、页面弹框及跳转操作完整示例
Jan 26 Javascript
vue 验证两次输入的密码是否一致的方法示例
Sep 29 Javascript
详解vue中v-for的key唯一性
May 15 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
php学习笔记之面向对象编程
2012/12/29 PHP
php rmdir使用递归函数删除非空目录实例详解
2016/10/20 PHP
php输出含有“#”字符串的方法
2017/01/18 PHP
微信公众平台开发教程③ PHP实现微信公众号支付功能图文详解
2019/04/10 PHP
在Ajax中使用Flash实现跨域数据读取的实现方法
2010/12/02 Javascript
JS控件的生命周期介绍
2012/10/22 Javascript
初始Nodejs
2014/11/08 NodeJs
JavaScript中的包装对象介绍
2015/01/27 Javascript
JavaScript中setFullYear()方法的使用详解
2015/06/11 Javascript
Javascript实现字数统计
2015/07/03 Javascript
图文详解Javascript中的上下文和作用域
2017/02/15 Javascript
vue 虚拟dom的patch源码分析
2018/03/01 Javascript
详解element-ui设置下拉选择切换必填和非必填
2019/06/17 Javascript
JavaScript实现随机五位数验证码
2019/09/27 Javascript
vue使用高德地图点击下钻上浮效果的实现思路
2019/10/12 Javascript
nodejs nedb 封装库与使用方法示例
2020/02/06 NodeJs
Python的Django框架中自定义模版标签的示例
2015/07/20 Python
Python检测网站链接是否已存在
2016/04/07 Python
使用pandas 将DataFrame转化成dict
2019/12/10 Python
Django正则URL匹配实现流程解析
2020/11/13 Python
html5实现多文件的上传示例代码
2014/02/13 HTML / CSS
Melissa鞋马来西亚官方网站:MDreams马来西亚
2018/04/05 全球购物
加拿大在线眼镜零售商:SmartBuyGlasses加拿大
2019/05/25 全球购物
Set里的元素是不能重复的,那么用什么方法来区分重复与否呢? 是用==还是equals()? 它们有何区别?
2014/07/27 面试题
英文自我鉴定
2013/12/10 职场文书
学生干部学习的自我评价
2014/02/18 职场文书
大班幼儿评语大全
2014/04/30 职场文书
电子信息工程专业自荐书
2014/06/24 职场文书
网吧七夕活动策划方案
2014/08/31 职场文书
党支部组织生活会整改方案
2014/09/30 职场文书
技术员岗位职责
2015/02/04 职场文书
集团财务总监岗位职责
2015/04/03 职场文书
员工辞退通知书
2015/04/17 职场文书
学校食堂食品安全承诺书
2015/04/29 职场文书
详解CSS不受控制的position fixed
2021/05/25 HTML / CSS
Windows Server 修改远程桌面端口的实现
2022/06/25 Servers