JS JavaScript闭包和作用域
JavaScript高级程序设计中对闭包的定义:闭包是指有权访问另外一个函数作用域中变量的函数。
从概念上,闭包有两个特点:
1.函数
2.能访问另外一个函数的作用域中的变量
在ES6之前,JavaScript只有函数作用域的概念,没有块级作用域(但catch捕获的异常,只能在catch中访问)的概念。每个函数都是封闭的,外部访问不到函数作用域中的变量。
function getName() {
var name = "LHH";
console.log(name); //"LHH"
}
function displayName() {
console.log(name); //报错
}
把代码改成以下:
function getName() {
var name = "LHH";
function displayName() {
console.log(name);
}
return displayName;
}
var getLHH = getName();
getLHH() //"LHH"
函数是一个闭包,外部就可以访问函数中的变量
对于闭包有下面三个特性:
1.闭包可以访问当前函数以外的变量
function getOuter(){
var date = '815';
function getDate(str){
console.log(str + date); //访问外部的date
}
return getDate('今天是:'); //"今天是:815"
}
getOuter();
getData是一个闭包,该函数执行时,会形成一个作用域A,A中并没有定义变量data,但它能从父一级作用域中找到该变量的定义。
2.即使外部函数已经返回,闭包仍能访问外部函数定义的变量
function getOuter(){
var date = '815';
function getDate(str){
console.log(str + date); //访问外部的date
}
return getDate; //外部函数返回
}
var today = getOuter();
today('今天是:'); //"今天是:815"
today('明天不是:'); //"明天不是:815"
3.闭包可以更新外部变量的值
function updateCount(){
var count = 0;
function getCount(val){
count = val;
console.log(count);
}
return getCount; //外部函数返回
}
var count = updateCount();
count(815); //
count(816); //
作用域链
JavaScript中有一个执行环境(execution context)的概念,它定义了变量或函数有权访问的其他数据,决定了他们各自的行为。每个执行环境都有一个与之关联的变量对象,环境中定义的所有变量和函数都保存在这个对象中。可以修改它的属性,但不能引用它。
变量对象也是有父作用域的。当访问一个变量时,解释器会首先在当前作用域查找标识符,如果没有找到,就去父作用域找,直到找到该变量的标识符或者不再存在父作用域链了,这就是作用域链。
作用域链和原型继承有点类似:如果去查找一个普通对象的属性时,在当前对象和其原型中都找不到时,会返回undefined,但查找的属性在作用域中不存在的话就会抛出ReferenceError。
作用域顶端是全局对象。对于全局环境中的代码,作用域中只包含一个元素:全局对象。所以,在全局环境中定义变量的时候,它们会被定义到全局对象中。当函数被调用的时候,作用域链就会包含多个作用域对象。
全局环境
看一个例子:
// my_script.js
"use strict";
var foo = 1;
var bar = 2;
在全局环境中,创建了两个简单地变量。此时变量对象是全局对象:
执行上述代码,my_script.js本身会形成一个执行环境,以及它所引用的变量对象。
无嵌套函数(Non-nested functions)
"use strict";
var foo = 1;
var bar = 2;
function myFunc() {
//-- define local-to-function variables
var a = 1;
var b = 2;
var foo = 3;
console.log("inside myFunc");
}
console.log("outside");
//-- and then, call it:
myFunc();
当myFunc被定义的时候,myFunc的标识符(identifier)就被加到了当前的作用域对象中(在这里就是全局对象),并且这个标识符所引用的是一个函数对象(function object)。函数对象中所包含的是函数的源代码以及其他的属性。内部属性[[scope]]指向的就是当前的作用域对象。也就是指的就是函数的标识符被创建的时候,我们所能够直接访问的那个作用域对象(在这里就是全局对象)。

myFune所引用的函数对象,其本身不仅仅含有函数的代码,并且还含有指向其创建的时候的作用域对象。
当myFunc函数被调用的时候,一个新的作用域对象的被创建了。新的作用域对象中包含了myFunc函数所定义的的本地变量,以及其参数(arguments)。这个新的作用域对象的父作用域对象就是在运行myFunc时我们所能直接访问的那个作用域对象(即全局对象)。
所以,当myFunc被执行的时候,对象之间的关系如下图:

有嵌套的函数(Nested functions)
当函数返回没有被引用的时候,就会被垃圾回收器回收。但是对于闭包(函数嵌套是形成闭包的一种形式),即使外部函数返回了,函数对象仍会引用它被创建时的作用域对象。
"use strict";
function createCounter(initial) {
var counter = initial;
function increment(value) {
counter += value;
}
function get() {
return counter;
}
return {
increment: increment,
get: get
};
}
var myCounter = createCounter(100);
console.log(myCounter.get()); // 返回 100
myCounter.increment(5);
console.log(myCounter.get()); // 返回 105
当调用createCounter(100)时,对象之间的关系如下图所示:

内嵌函数increment和get都有指向createCounter(100) scope的应用。如果createCounter(100)没有任何返回值,那么createCounter(100) scope不再被引用,于是就可以被垃圾回收。但是因为createCounter(100)实际上是有返回值的,并且返回值被存储在了myCounter中,所以对象之间的引用关系变成了如下图所示:

即使createCounter(100)已经返回,但是其作用域仍在,并能且只能被内联函数访问。可以通过调用myCounter.increment() 或 myCounter.get()来直接访问createCounter(100)的作用域。
当myCounter.increment() 或 myCounter.get()被调用时,新的作用域对象会被创建,并且该作用域对象的父作用域对象会是当前可以直接访问的作用域对象。此时,引用关系如下:

当执行到return counter;时,在get()所在的作用域并没有找到对应的标示符,就会沿着作用域链往上找,直到找到变量counter,然后返回该变量。
当单独调用increment(5)时,参数value会存贮在当前的作用域对象。函数要访问value,能马上在当前作用域找到该变量。但是当函数要访问counter时,并没有找到,于是沿着作用域链向上查找,在createCounter(100)的作用域找到了对应的标示符,increment()就会修改counter的值。除此之外,没有其他方式来修改这个变量。闭包的强大也在于此,能够存贮私有数据。

相同的函数,不同的作用域
//myScript.js
"use strict";
function createCounter(initial) {
/* ... see the code from previous example ... */
}
//-- create counter objects
var myCounter1 = createCounter(100);
var myCounter2 = createCounter(200);
myCounter1 和 myCounter2创建之后,关系图如下:

在上面的例子中,myCounter1.increment和myCounter2.increment的函数对象拥有着一样的代码以及一样的属性值(name,length等等),但是它们的[[scope]]指向的是不一样的作用域对象。
这才有了下面的结果:
var a, b;
a = myCounter1.get(); // a 等于 100
b = myCounter2.get(); // b 等于 200
myCounter1.increment(1);
myCounter1.increment(2);
myCounter2.increment(5);
a = myCounter1.get(); // a 等于 103
b = myCounter2.get(); // b 等于 205
JS JavaScript闭包和作用域的更多相关文章
- javascript闭包和作用域链
最近在学习前端知识,看到javascript闭包这里总是云里雾里.于是翻阅了好多资料记录下来本人对闭包的理解. 首先,什么是闭包?看了各位大牛的定义和描述各式各样,我个人认为最容易一种说法: 外部函数 ...
- Javascript闭包与作用域
摘自开源中国 闭包和作用域是js中比较重要的知识,自己理解起来也有一定的难度 1.Javascript的作用域是函数作用域而非块级作用域 ? 1 2 3 4 5 6 7 8 9 10 11 12 // ...
- [ JS 进阶 ] 闭包,作用域链,垃圾回收,内存泄露
原网址:https://segmentfault.com/a/1190000002778015 1. 什么是闭包? 来看一些关于闭包的定义: 闭包是指有权访问另一个函数作用域中变量的函数 --< ...
- Javascript闭包与作用域this
闭包与this的一般用法 关于js函数与闭包的文章想必大家都是在熟悉不过的了,作为js核心亦即最强大的功能之一,每次回过头翻出来看一看,都会有不一样的收获与理解,经典的含义无非如此而已. 1.闭包 1 ...
- Javascript——闭包、作用域链
1.闭包:是指有权访问另一个函数作用域中的变量的函数.创建闭包的常见方式:在一个函数内部创建另一个函数. function f(name){ return function(object){ var ...
- 对js中闭包,作用域,原型的理解
前几天,和朋友聊天,聊到一些js的基础的时候,有一种‘好像知道,好像又不不知道怎么讲的感觉’...于是捡起书,自己理一理,欢迎拍砖. 闭包 理解闭包首先要理解,js垃圾回收机制,也就是当一个函数被执行 ...
- [Js]JavaScript闭包和范围的快速测试
1. if (!("a" in window)) { var a = 1; } alert(a); [分析]代码含义:如果window不包含属性a,就声明一个变量a并赋值为1 ①J ...
- js中闭包和作用域
将这方面很好的一系列文章:http://www.cnblogs.com/wangfupeng1988/p/3977987.html
- JavaScript闭包之“词法作用域”
大家应该写过下面类似的代码吧,其实这里我想要表达的是有时候一个方法定义的地方和使用的地方会相隔十万八千里,那方法执行时,它能访问哪些变量,不能访问哪些变量,这个怎么判断呢?这个就是我们这次需要分析的问 ...
随机推荐
- React.js 小书 Lesson10 - 组件的 state 和 setState
作者:胡子大哈 原文链接:http://huziketang.com/books/react/lesson10 转载请注明出处,保留原文链接和作者信息. state 我们前面提到过,一个组件的显示形态 ...
- nyoj 999——师傅又被妖怪抓走了——————【双广搜】
师傅又被妖怪抓走了 时间限制:1000 ms | 内存限制:65535 KB 难度:3 描述 话说唐僧复得了孙行者,师徒们一心同体,共诣西方.自宝象国救了公主,承君臣送出城西,沿路饥餐渴饮,悟 ...
- [转]ASP.NET Web API系列教程(目录)
本文转自:http://www.cnblogs.com/r01cn/archive/2012/11/11/2765432.html 注:微软随ASP.NET MVC 4一起还发布了一个框架,叫做ASP ...
- lua_nginx_module用例
content_by_lua server { listen ; server_name lua.luckybing.top; location / { default_type 'text/plai ...
- plsql 连接 虚拟机Linux下的Oracle数据库 失败
plsql 连接 虚拟机Linux下的Oracle数据库 失败2017年09月09日 09:10:23 守望dfdfdf 阅读数:187 标签: oraclelinux 更多个人分类: 工具 软件编辑 ...
- javascript小数相减出现一长串的小数位数
我们要修改网页某个数据的显示格式,需要两步操作: 1.在JS中通过$('.class1 .class2 li:eq(2) span.value').text().trim();类似的语句获取到数据内容 ...
- Redis的Lists数据类型
Lists 就是链表,相信略有数据结构知识的人都应该能理解其结构.使用Lists结构,我们可以轻松地实现最新消息排行等功能.Lists的另一个应用就是消息队列,可以利用Lists的PUSH操作,将任务 ...
- svn自动更新网站代码
运行环境说明:svn的执行目录: /usr/bin/svnsvn版本库目录: /usr/local/nginx/html/svnrepos/coinhello (其中svnrepos是我各个项目版本库 ...
- Web前端面试指导(十):元素定位有哪些?
本题点评 在web前端中,元素定位是必须掌握的,是网页制作的必备技能,也是衡量是否为一个合格的web前端开发的标准之一,在网页设计中,很多地方都需要使用定位,例如菜单弹出,提示信息层等都需要定位.所以 ...
- 纯css 简单网页
<div id="wrapper"> <header> <section> <h1>Web Design<h1> < ...