《你不知道的JavaScript(上)》笔记——作用域闭包
当函数可以记住并访问所在的词法作用域时, 就产生了闭包, 即使函数是在当前词法作用域之外执行。
function wait(message) {
setTimeout( function timer() {
console.log( message );
}, 1000 );
}
wait( "Hello, closure!" );
将一个内部函数( 名为 timer) 传递给 setTimeout(..)。 timer 具有涵盖 wait(..) 作用域的闭包, 因此还保有对变量 message 的引用。wait(..) 执行 1000 毫秒后, 它的内部作用域并不会消失, timer 函数依然保有 wait(..)作用域的闭包。
深入到引擎的内部原理中, 内置的工具函数 setTimeout(..) 持有对一个参数的引用, 这个参数也许叫作 fn 或者 func, 或者其他类似的名字。 引擎会调用这个函数, 在例子中就是内部的 timer 函数, 而词法作用域在这个过程中保持完整。这就是闭包。
在定时器、 事件监听器、Ajax 请求、 跨窗口通信、 Web Workers 或者任何其他的异步(或者同步) 任务中, 只要使用了回调函数, 实际上就是在使用闭包!
模块模式
还有一种代码模式利用了闭包——模块
function CoolModule() {
var something = "cool";
var another = [1, 2, 3];
function doSomething() {
console.log( something );
}
function doAnother() {
console.log( another.join( " ! " ) );
}
return {
doSomething: doSomething,
doAnother: doAnother
};
}
var foo = CoolModule();
foo.doSomething(); // cool
foo.doAnother(); // 1 ! 2 ! 3
首先, CoolModule() 只是一个函数, 必须要通过调用它来创建一个模块实例。 如果不执行外部函数, 内部作用域和闭包都无法被创建。
其次, CoolModule() 返回一个用对象字面量语法 { key: value, ... } 来表示的对象。 这个返回的对象中含有对内部函数而不是内部数据变量的引用。 我们保持内部数据变量是隐藏且私有的状态。 可以将这个对象类型的返回值看作本质上是模块的公共 API。
这个对象类型的返回值最终被赋值给外部的变量 foo, 然后就可以通过它来访问 API 中的属性方法, 比如 foo.doSomething()。
因此模块模式需要具备两个必要条件:
- 必须有外部的封闭函数, 该函数必须至少被调用一次(每次调用都会创建一个新的模块
实例)。 - 封闭函数必须返回至少一个内部函数, 这样内部函数才能在私有作用域中形成闭包, 并
且可以访问或者修改私有的状态。
模块模式另一个简单但强大的变化用法是, 命名将要作为公共 API 返回的对象:
var foo = (function CoolModule(id) {
function change() {
// 修改公共 API
publicAPI.identify = identify2;
}
function identify1() {
console.log( id );
}
function identify2() {
console.log( id.toUpperCase() );
}
var publicAPI = {
change: change,
identify: identify1
};
return publicAPI;
})( "foo module" );
foo.identify(); // foo module
foo.change();
foo.identify(); // FOO MODULE
通过在模块实例的内部保留对公共 API 对象的内部引用, 可以从内部对模块实例进行修改, 包括添加或删除方法和属性, 以及修改它们的值。
现代的模块机制
大多数模块依赖加载器 / 管理器本质上都是将这种模块定义封装进一个友好的 API。
var MyModules = (function Manager() {
var modules = {};
function define(name, deps, impl) {
for (var i = 0; i < deps.length; i++) {
deps[i] = modules[deps[i]];
}
modules[name] = impl.apply(impl, deps);
}
function get(name) {
return modules[name];
}
return {define: define, get: get};
})();
这段代码的核心是 modules[name] = impl.apply(impl, deps)。 为了模块的定义引入了包装函数(可以传入任何依赖), 并且将返回值, 也就是模块的 API, 储存在一个根据名字来管理的模块列表中。
下面展示了如何用它来定义模块:
MyModules.define("bar", [], function () {
function hello(who) {
return "Let me introduce: " + who;
}
return {hello: hello};
});
MyModules.define("foo", ["bar"], function (bar) {
var hungry = "hippo";
function awesome() {
console.log(bar.hello(hungry).toUpperCase());
}
return {awesome: awesome};
});
var bar = MyModules.get("bar");
var foo = MyModules.get("foo");
console.log(bar.hello("hippo")); // Let me introduce: hippo
foo.awesome(); // LET ME INTRODUCE: HIPPO
"foo" 和 "bar" 模块都是通过一个返回公共 API 的函数来定义的。 "foo" 甚至接受 "bar" 的示例作为依赖参数, 并能相应地使用它。
《你不知道的JavaScript(上)》笔记——作用域闭包的更多相关文章
- 你不知道的JavaScript上卷笔记
你不知道的JavaScript上卷笔记 前言 You don't know JavaScript是github上一个系列文章 初看到这一标题的时候,感觉怎么老外也搞标题党,用这种冲突性比较强的题目 ...
- 读书笔记-你不知道的JavaScript(上)
本文首发在我的个人博客:http://muyunyun.cn/ <你不知道的JavaScript>系列丛书给出了很多颠覆以往对JavaScript认知的点, 读完上卷,受益匪浅,于是对其精 ...
- 《你不知道的javascript(上)》笔记
作用域是什么 编译原理 分词/词法分析 这个过程会将由字符组成的字符串分解成(对编程语言来说)有意义的代码块,这些代码块被称为词法单元 解析/语法分析 词法单元流(数组)转换成一个由元素逐级嵌套所组成 ...
- 【你不知道的javaScript 上卷 笔记2】 javaScript 的作用域规则
一.什么是词法作用域? 词法作用域是在定义词法阶段的作用域,就是由代码变量和作用域块写在哪里决定的,基本上词法分析器在处理代码时会保持作用域不变. 二.词法作用域特点 完全由写代码期间函数所声明的位置 ...
- <你不知道的JavaScript>读书笔记
近几天看了一本不错的 JavaScript 的书,是 Kyle Simpson 写的 <You Don't know JS>.这本书是 Kyle Simpson 在 Github 上的开源 ...
- 你不知道的javascript读书笔记3
概述 这是我看<你不知道的JavaScript(中卷)>中关于类型检查的笔记,供以后开发时参考,相信对其他人也有用. typeof 我们知道js中有七种内置类型:undefined, nu ...
- 《你不知道的JavaScript》笔记(一)
用了一个星期把<你不知道的JavaScript>看完了,但是留下了很多疑惑,于是又带着这些疑惑回头看JavaScript的内容,略有所获. 第二遍阅读这本书,希望自己能够有更为深刻的理解. ...
- 【你不知道的javaScript 上卷 笔记3】javaScript中的声明提升表现
console.log( a ); var a = 2; 执行输出undefined a = 2; var a; console.log( a ); 执行输出2 说明:javaScript 运行时在编 ...
- 【你不知道的javaScript 上卷 笔记5】javaScript中的this词法
function foo() { console.log( a ); } function bar() { var a = 3; foo(); } var a = 2; bar(); 上面这段代码为什 ...
随机推荐
- zlog日志函数库
在C的世界里面没有特别好的日志函数库(就像JAVA里面的的log4j,或者C++的log4cxx).C程序员都喜欢用自己的轮子.printf就是个挺好的轮子,但没办法通过配置改变日志的格式或者输出文件 ...
- vue语法概要二
函数 用途 类别 v-html 用于输出html格式的数据 输出 v-bing 用于输出值 输出 v-model 双向绑定 输入和输出 v-if 逻辑判断 分支语句 v-else 同上 分支语句 v- ...
- spark not serializable异常分析及解决方案
转载自: http://bigdataer.net/?p=569 1.背景 在使用spark开发分布式数据计算作业过程中或多或少会遇到如下的错误: Serialization stack: objec ...
- ndk学习之C语言基础复习----结构体、共用体与C++开端
自己实现sprintf功能: 关于C中的系统函数sprintf在上次[https://www.cnblogs.com/webor2006/p/7545627.html]学习中已经用到过了,这里再来回顾 ...
- Pytest编写测试函数
Pytest编写测试函数 一个规范的测试方法一定带有断言,在使用pytest时,可以直接使用Python自带的assert关键字 Pytest允许在assert关键字添加任意表达式,表达式的值通过bo ...
- [唐胡璐]Selenium技巧- Highlight页面元素
大家都知道QTP的对象高亮显示功能特别强大, Selenium Webderiver也可以实现此功能。 高亮显示有时候对Debug还是相当有用的。 解决脚本: 调用脚本: 结果显示:
- mysql:用户自定义变量关联失效
自定义变量的属性和限制 使用自定义变量的查询,无法使用查询缓存. 不能在使用常量或者标识列的地方使用自定义变量,例如表名.列明和LIMIT子句中. 用户自定义变量的生命周期是在一个连接中有效,所以不能 ...
- jquery 下载
jquery下载所有版本(实时更新) 摘自:http://www.jq22.com/jquery-info122 插件描述:jquery下载,实时更新jquery1.2到最新3.2.1所有版本下载 j ...
- 使用谷歌chrome浏览器查看任何标签的固有属性
查看任何标签的固有属性property: 使用谷歌浏览器:Ctrl+Shift+I 开发者工具----点击Elements----点击a标签----点击Properties属性及其属性值. 自定义属性 ...
- csv和xlsx区别
CSV是文本文件,用记事本就能打开.XLS 是二进制的文件只有用 EXCEL 才能打开: CSV 文件格式只能保存活动工作表中的单元格所显示的文本和数值.数据列以逗号分隔,每一行数据都以回车符结束.如 ...