《你不知道的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(); 上面这段代码为什 ...
随机推荐
- Sqlmap对dvwa进行sql注入测试
前提准备条件: 1.下载安装dvwa,下载链接地址:http://www.dvwa.co.uk/.2.需要安装python运行环境.3.下载sqlmap包并将其解压. 一.查看所有的数据库;(其中db ...
- Netty——基本使用介绍
https://blog.csdn.net/haoyuyang/article/details/53243785 1.为什么选择Netty 上一篇文章我们已经了解了Socket通信(IO/NIO/AI ...
- mongodb备份每一天的数据
需求:把mongodb里面存储6个月的数据备份到本地,一天天的来备份,方便对备份管理.然后mongo保留一周的数据(优化查询速度,可以用mongo的ttl来实现,但是我的业务场景不太适合用ttl索引) ...
- WPF多值绑定及多值转换(MultiBinding和IMultiValueConverter)
WPF可以使用MultiBinding进行多值绑定,使用IMultiValueConverter进行多值转换 例: (1)转换器 public class ContentConverter : IMu ...
- Mysql批量更新的一个坑-&allowMultiQueries=true允许批量更新(转)
实际上,我们经常会遇到这样的需求,那就是利用Mybatis批量更新或者批量插入,但是,实际上即使Mybatis完美支持你的sql,你也得看看你说操作的数据库是否支持,而阿福,最近就遇到这样的一个坑. ...
- 利用 Label 小小的提升一下用户体验
label ,Html 标签里面很普通的一个,可是她却有一个很独特的作用,我不知道我是忘了她还是不曾记得她,下面简单介绍一下她. 一.定义和用法 <label> 标签为 input 元素定 ...
- docker的网络服务
docker提供 网络服务,主要通过两种方式: 1.外部访问 2.容器互联 通过外部访问Docker容器,主要通过端口映射的方式. [root@docker ~]# docker run -t -P ...
- 【leetcode】1292. Maximum Side Length of a Square with Sum Less than or Equal to Threshold
题目如下: Given a m x n matrix mat and an integer threshold. Return the maximum side-length of a square ...
- Shiro (包含权限满足其中一个就通过的用法)
方法/步骤 1 web.xml添加配置 <!-- shiro过滤器 --> <filter> <filter-name>shiroFilter</filter ...
- 老男孩Python==基础数据类型考试题
转载 # Python基础数据类型考试题 # 考试时间:两个半小时 满分100分(80分以上包含80分及格) # 一,基础题. # 1, 简述变量命名规范(3分) # 1.变量由字母.数字.下划线任意 ...