Javascript的作用域,作用域链,闭包
1,作用域和作用域链概念
作用域就是变量与函数的可访问范围,即作用域控制着变量与函数的可见性和生命周期。在JavaScript中,变量的作用域有全局作用域和局部作用域两种。
1.1 全局作用域,在代码中任何一个地方都能访问的对象拥有全局作用域,一般来说以下几种情形拥有全局作用域:
最外层函数和在最外层函数外面定义的变量拥有全局作用域;
所有未定义直接赋值的变量自动声明为拥有的全局作用域;
浏览器环境中,所有windows对象的属性拥有全局作用域;
1.2 局部作用域,局部作用域一般只是固定的代码片段内可访问到,在JS中一般是函数作用域;
1.3 作用域链,当代码在一个环境中执行是,会常见变量对象的一个作用域链,作用域链的作用是保证队执行环境有权访问的所有变量和函数的有序访问。全局执行环境的变量对象始终都是作用域链中最后一个对象。
2,作用域规则
作用域有一套根据名称查找变量的规则。
实际情况中,通常需要同事顾及几个作用域,当一个块或函数嵌套在另一块或函数中时,就发生了作用域的嵌套。因此,在当前作用域中无法找到某个变量时,引擎就会在外层嵌套的作用域中举行查找,直到找到该变量,或抵达最外层的作用域(也就是全局作用域)为止,但不能通过向下搜索作用域链而进入另一个执行环境。
var a = 1;
function f() {
var a =2;
var b =3;
console.log(a);
}
f(); //2
a; //1
b; // 报错
3,作用域工作模型
JS作用域的工作模型是词法作用域,词法作用域就是定义在词法阶段的作用域。换句话说,词法作用域是由你在写代码时将变量和块作用域写在哪里来决定的,因此当词法分析器处理代码时会保持作用域不变(大部分情况下是这样的)。
function f1() { alert(a)}f1();
function f2() { var a = 2; f1();}var a =1;f1(); //1f2(); //1,因为f1定义时的作用域的a为全局的a=1,说明函数不管在哪里调用都是基于它定义时的作用域;上面代码中,当函数执行时,会先查找所需的变量,作用域查找会在找到第一个匹配的标识符时停止,在多层的嵌套作用域中可以定义同名的标识符,这叫做"遮蔽效应"(内部的标识符"遮蔽"了外部的标识符)。抛开遮蔽效应,作用域查找始终从运行是所处的最内部作用域开始,逐级向外或者说向上进行,直到遇见第一个匹配的标识符为止。词法作用域规则会使函数在查找变量时从函数内部到函数使用时的作用域。所以无论函数在哪里被调用,也无论它如何被调用,它的词法作用域都只由函数被声明时所处的位置决定。
4,函数作用域
JS中没有块级作用域,而是基于函数的作用域。函数作用域的含义是指,属于这个函数的全部变量都可以在整个函数的范围内使用及复用(事实上在嵌套的作用域中也可以使用)
function d() { if (true){ var a = 1; } console.log(a);}d(); //if不会生成新的作用域,所以a会泄露到当前函数作用域利用函数作用域的特点,我们可以在任意代码片段外部添加包装函数,可以将内部的变量和函数定义"隐藏"起来,外部作用域无法访问包装函数内部的任何内容。
var a = 1;(function f() { var a = 2; console.log(a); //2,利用函数作用域包装代码,使函数内部变量不会被外部访问})();console.log(a)
var a =1;function f() { console.log(a); //underfined,不是1 var a = 2; console.log(a); //2}f();//上面代码解析为function f() { var a; console.log(a); a = 2; console.log(a);}var a;a = 1;f();这个过程就是声明提升。声明提升是因为JS在执行之前,会有一个预编译过程,预编译阶段生成变量声明和函数声明,没有初始化行为(赋值),匿名函数不参与预编译只有在解释执行阶段才会进行变量初始化。声明提升原理函数和变量的预解析特点: 1.函数声明会置顶 2.变量声明也会置顶 3.函数声明比变量声明更置顶:(函数在变量上面) 4.变量和赋值语句一起书写,在js引擎解析时,会将其拆成声明和赋值2部分,声明置顶,赋值保留在原来位置 5.声明过的变量不会重复声明
(function () { a =1; alert(window.a); var a = 2; alert(a);})();//上面代码解析(function () { var a ; //a被提升到顶端后,a=1就不是隐式声明全局变量了,而是给a赋值操作,所以window.a未声明为undefined; a =1; alert(window.a); //undefined; a = 2; alert(a); //2})();
5,欺骗函数作用域
执行环境类型中有两种,全局和局部函数,但可以用其他方法欺骗函数作用域。
eval(),Javascript中的eval(...)函数可以接受一个字符串为参数,并将其中的内容视为好像在书写是就存在于程序中这个位置的代码并在运行到此位置执行参数代码。因为eval(...)可以在运行期修改书写期的词法作用域,存在安全问题,不建议使用。
var a = 1;function f() { eval('var a =2;');//eval中的a遮蔽了全局中的a,让作用域弹出的a改变了 console.log(a);}f();
with,with语句接收一个对象参数,表示with语句的对象作用域,with语句中的变量都会在这个指定的对象中查找。with会影响性能,不建议使用
window.onload = function () { var a = 0; var obj = { a:1, b:2 }; with(obj){ a = 2; //会在obj查找a属性 } console.log(obj.a); //2 console.log(a); //0 with(obj){ c = 3; //没有在指定对象中找到c,非严格模式下,with会在全局隐式创建一个全局变量 } console.log(obj.c); //undefined console.log(c); //3};
6,闭包
闭包(closure
)是Javascript
语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现。
闭包的特性:
1,函数嵌套函数
2,函数内部可以引用外部的参数和变量
3,参数和变量不会被垃圾回收机制回收
闭包的定义及其优缺点
闭包
是指有权访问另一个函数作用域中的变量的函数,创建闭包的最常见的方式就是在一个函数内创建另一个函数,通过另一个函数访问这个函数的局部变量,闭包的缺点就是常驻内存,会增大内存使用量,使用不当很容易造成内存泄露。闭包是javascript
语言的一大特点,主要应用闭包场合主要是为了:设计私有的方法和变量。一般函数执行完毕后,局部活动对象就被销毁,内存中仅仅保存全局作用域。但闭包的情况不同!
嵌套函数的闭包
window.onload = function () { function aaa() { var a = 1; return function(){ alert(a++) }; } var fun = aaa(); fun();// 1 执行后 a++,,然后a还在~ fun();// 2 fun = null;//a被回收!! }; //闭包会使变量始终保存在内存中,如果不当使用会增大内存消耗。 javascript的垃圾回收原理 (1)在JavaScript中,如果一个对象不再引用,那么这个对象就会被GC回收; (2)如果两个对象互相引用,而不再被第3者所引用,那么这两个互相引用的对象也会被回收; 使用闭包的好处: 1,希望一个变量长期驻扎在内存中 2,避免全局变量的污染 3,私有成员的存在 一,全局变量的累加 window.onload = function () { var a = 1;
function f() { a++; alert(a); } f(); //2 f(); //3}
二,局部变量
window.onload = function () { function f() { var a = 1; a++; alert(a); } f(); f();//那么怎么才能做到变量a既是局部变量又可以累加呢?
三,局部变量的累加
window.onload = function () { function outer(){ var x=10; return function(){ //函数嵌套函数 x++; alert(x); } } var y = outer(); //外部函数赋给变量y; y(); //y函数调用一次,结果为11,相当于outer()(); y(); }
函数声明于函数表达式
在js中我们可以通过关键字function来声明一个表达式:
<script> window.onload = function () { function abc(){ alert(123); } abc(); } </script> //我们也可以通过一个"()"来将这个声明变成一个表达式:
window.onload = function () { (function abc(){ alert(123); })() }
四,模块化代码,减少全局变量的污染
window.onload = function () { var abc = (function () { //abc为外部匿名函数的返回值 var a = 1; return function () { a++; alert(a); } })(); abc(); //2 ;调用一次abc函数,其实是调用里面内部函数的返回值 abc(); //3 }
5,私有成员的存在
window.onload = function () { var aaa = (function(){ var a = 1; function bbb(){ a++; alert(a); } function ccc(){ a++; alert(a); } return { b:bbb, //json结构 c:ccc } })(); aaa.b(); //2 aaa.c() //3 }
六,使用匿名函数实现累加
window.onload = function () { function box(){ var age = 100; return function(){ //匿名函数 age++; return age; }; }var b = box();alert(b());alert(b()); //即alert(box()());alert(b());alert(b); // function () { // age++; // return age; // } b = null; //解除引用,等待垃圾回收}
七,在循环中直接找到对应元素的索引
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"><head> <meta http-equiv="Content-Type" content="text/html;charset=UTF-8" /> <title></title> <script> window.onload = function(){ var aLi = document.getElementsByTagName('li'); for (var i=0;i<aLi.length;i++){ aLi[i].onclick = function(){ //当点击时for循环已经结束 alert(i); }; } } </script> </head> <body> <ul> <li>123</li> <li>456</li> <li>789</li> <li>010</li> </ul> </body> </html>
八,使用闭包改写上面的代码
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"><head> <meta http-equiv="Content-Type" content="text/html;charset=UTF-8" /> <title></title> <script> window.onload = function(){ var aLi = document.getElementsByTagName('li'); for (var i=0;i<aLi.length;i++){ (function(i){ aLi[i].onclick = function(){ alert(i); }; })(i); } }; </script> </head> <body> <ul> <li>123</li> <li>456</li> <li>789</li> </ul> </body> </html>
转载于:https://www.cnblogs.com/sjie0224/articles/8195570.html
Javascript的作用域,作用域链,闭包相关推荐
- JavaScript中的作用域,闭包和上下文
深入理解JavaScript中的作用域和上下文 很多语言当中都会有作用域的概念,它会给我们带来便利,偶尔也会有烦恼,只有清楚地理解和掌握了它,才能更好地为我所用,今天就带来这么一篇文章供大家参考. 介 ...
- javascript中关于作用域和闭包
列表项目 前言 学习了javascript已经很久了,关于这个语言中的这两个特性也是早已耳熟能详,但是在实际的使用的过程中或者是遇到相关的问题的时候,还是不能很好的解决. 因此我觉得很有必要深入的学习 ...
- JavaScript 函数(作用域以及闭包)
JavaScript 函数(作用域以及闭包) ・执行环境及作用域 执行环境定义了变量或函数有权访问的其他数据. 每个执行环境都有一个与之关联的变量对象,环境中定义的所有变量或函数都保存在这个对象中, ...
- javascript(面向对象,作用域,闭包,设计模式等)
javascript(面向对象,作用域,闭包,设计模式等) 1. 常用js类定义的方法有哪些? 参考答案:主要有构造函数原型和对象创建两种方法.原型法是通用老方法,对象创建是ES5推荐使用的方法.目前 ...
- 【你不知道的JavaScript上卷】——作用域与闭包
原文: [你不知道的JavaScript上卷]--作用域与闭包 JS语言万变不离其宗,其中最常用.最重要的也就是常用的几个大概念.数据类型.作用域.原型链.闭包.this指针.异步,不同的人理解不一样 ...
- 【金三银四】 一文弄懂 js 数据类型、堆栈内存、作用域(链)、闭包知识拓展 (一)
引言 对答如流系列篇,关于基本数据类型.堆栈内存.作用域作用域链.闭包 大家好,这里是lionLoveVue,基础知识决定了编程思维,学如逆水行舟,不进则退.金三银四,为了面试也还在慢慢积累知识,Gi ...
- JavaScript函数,作用域以及闭包
JavaScript函数,作用域以及闭包 1. 函数 (1). 函数定义:函数使用function关键字定义,它可以用在函数定义表达式或者函数声明定义. a. 函数的两种定义方式: * functio ...
- PHP (20140510)深入浅出 JavaScript 变量、作用域和内存 v 0.5
深入浅出 JavaScript 变量.作用域和内存 v 0.5 本文主要从原理入手分享变量和作用域的相关知识,最后结合本文所分享知识,再次深入了解下闭包的运行原理. 主要参考<JS高级程序设计& ...
- 深入理解JavaScript的变量作用域
在学习JavaScript的变量作用域之前,我们应当明确几点: a.JavaScript的变量作用域是基于其特有的作用域链的. b.JavaScript没有块级作用域. c.函数中声明的变量在整个函数 ...
- JavaScript之词法作用域和动态作用域
作用域 作用域是指程序源代码中定义变量的区域. 作用域规定了如何查找变量,也就是确定当前执行代码对变量的访问权限. JavaScript 采用词法作用域(lexical scoping),也就是静态作 ...
最新文章
- VisualStudio代码样式-我最喜欢的一种风格
- 与优秀的人在一起进步:我发起的“乐学”分享活动
- 机器学习实战(用Scikit-learn和TensorFlow进行机器学习)(九)
- java客户端作为kafka生产者测试
- Python CheckiO 题解系列 丨 博客目录索引
- 字符串转命令行字符图片
- linux fsck命令,Linux中fsck命令起什么作用呢?
- BZOJ5329:[SDOI2018]战略游戏(圆方树,虚树)
- ubuntu linux 搭建 webssh 网页ssh远程登录其他服务器
- java 符_java运算符
- 分形理论在图像处理中的应用研究(综述)
- 数值分析实验(四)之方程求根的数值方法
- 怎样更改计算机应用图标,win7如何更改软件图标_win7修改应用程序图标的教程
- 关于 kubernetes网络(CNI规范)中Calico,NetworkPolicy(网络策略)方面的一些笔记
- 怎么用计算机隐藏应用程序,win7 隐藏应用程序 电脑如何隐藏程序_win7教程_uc电脑园...
- 【每天听见吴晓波】为什么要听见吴晓波?
- 自定义函数代替inet_addr函数写入ip
- Tkmybatis快速入手
- matlab中的xcorr函数的c语言转写,与matlab运算结果完全一致
- 2023年杀手级的 5 款免费FTP客户端,真的好用到爆,推荐给需要的工程师!