《Javascript高级程序设计》读书笔记——函数与闭包
函数与闭包
函数创建
创建函数有两种方式,第一种是函数声明。函数声明有一个很重要的特征就是函数声明提升(function declaration hoisting),意思是在执行代脚本前会先读取所有的函数声明。这意味着可以把函数声明放在调用它的语句后面:
sayHi();
function sayHi() {console.log('Hi');}
第二种方式是函数表达式:
var functionName = function(arg0, arg1, arg2) { /*function body*/ }
既然是叫表达式,那么自然也是要先赋值才能使用,否则会抛出错误:
sayHi(); //throw error!
var sayHi = function {console.log('Hi');}
关于递归
书上讲的关于使用递归时会犯的一个错就是将递归函数的引用重新赋值:
function factorial(num) {
if(num <= 1) return 1;
return num * factorial(num - 1);
}
var anotherFactorial = factorial; //创建新的变量也指向递归函数
factorial = null;
console.log(anotherFactorial(4)); //throw error
要解决这种窘境,书上给的一种解决方法是使用arguments.callee(arguments.callee是一个指向正在执行的函数的指针),但这个内置参数在严格模式下是不允许的,不建议使用。 比较推荐的是使用命名函数表达式:
var factorial = (function f(num){
if(num == 1) return 1;
return num * f(num - 1);
});
这样就不必担心函数的引用被重写而导致上述问题了。但这种方式就使得递归函数失去了之前函数声明提升的特征。
还有另一种思路是 在创建函数表达式时使用const关键字:const factorial = function(num){...};。 如果有IDE或其他工具做检测的话,可以将问题在代码阶段就暴露出来。
闭包
闭包是指有权访问另一个函数作用域中的变量的函数。创建闭包的常见方式,就是在一个函数内部创建另一个函数,仍以前面的createComparisionFunction()为例子:
书上给的这个闭包的概念算是最表层的描述了,但闭包的底层原理要复杂的多。
MDN上关于闭包的一段解释:
A closure is a special kind of object that combines two things: a function, and the environment in which that function was created. The environment consists of any local variables that were in-scope at the time that the closure was created.
现在放上书里的例子:
function createComparisionFunction(propertyName) {
return function(object1, object2) {
var value1 = object1[propertyName];
var value2 = object2[propertyName];
if(value1 < value2) {
return -1;
} else if(value1 > value2) {
return 1;
} else {
return 0;
}
};
}
红宝书在讲述闭包原理的前面几大段里抛出了好多个概念:执行环境、作用域链、活动对象、变量对象。如果还不是很能明白的话可以对照书上图7-1这样理解:函数的执行环境就是个包含着作用域链的东西;变量对象差不多等同于活动对象,就是个用来保存 执行函数时的 局部变量的。
另外,还需记住书上说的一点:作用域链本质上是一个指向活动对象(变量对象 )的指针链表,它只引用但不包含变量对象。
在执行createComparisonFunction函数的时候,会创建一个活动对象来保存用到的局部变量,然后让createComparisionFunction的执行环境 的 作用域链指向这个活动对象;执行过程中,又创建了匿名函数;学作用域链时我们便知道,函数搜索变量是在作用域链中一个接一个地向上搜索直至找到对应变量名或到达全局作用域中,其本质就是沿着作用域链中指向的活动对象进行遍历。 创建了匿名函数后,会预先初始化它的作用域链并在上面添加外部的createComparisionFunction函数的活动对象; 如下:
(此时是没有匿名函数的执行环境和活动对象的,只有在执行函数时才会创建)
一般来说,当函数执行完毕后,其执行环境和作用链都会被销毁,而活动对象 也会被回收。但是,在上述创建了匿名函数的情况下,因为匿名函数的作用域链仍在引用着createComparisonFunction的活动对象,所以这个活动对象不会被垃圾收集器回收。(关于js垃圾回收)
在匿名函数执行时的情况就是下面这样的:

匿名函数还引用着创建它的外部函数的活动对象,因而可以访问到里面的变量。以上这种内部函数附带着外部函数“环境”的机制,就叫做闭包。
如果仍然感觉不明白,可以看下这个知乎话题下的回答:知乎:什么是闭包?
《Javascript高级程序设计》读书笔记——函数与闭包的更多相关文章
- javascript高级程序设计读书笔记----函数表达式
定义函数两种方式: 1.函数声明 function sayHi(){ alert("Hi"); } sayHi();//调用函数 2.函数表达式 var sayHi = funct ...
- JavaScript高级程序设计-读书笔记(2)
第6章 面向对象的程序设计 创建对象 1.最简单方式创建Object的实例,如 var person = new Object(); person.name = “Greg”; person.age ...
- javascript高级程序设计读书笔记-事件(一)
读书笔记,写的很乱 事件处理程序 事件处理程序分为三种: 1.html事件2. DOM0级,3,DOM2级别 没有DOM1 同样的事件 DOM0会顶掉html事件 因为他们都是属性 而 ...
- javascript高级程序设计读书笔记
第2章 在html中使用javascript 一般都会把js引用文件放在</body>前面,而不是放在<head>里, 目的是最后读取js文件以提高网页载入速度. 引用js文 ...
- JavaScript高级程序设计-读书笔记(7)
第22章 高级技巧 1.高级函数 (1)安全的类型检测 在任何值上调用Object原生的toString()方法,都会返回一个[object NativeConstructorName]格式的字符串. ...
- JavaScript高级程序设计学习笔记--函数表达式
关于函数声明,它的一个重要特征就是函数声明提升,意思是在执行代码之间会读取函数声明,意思是在执行代码之前会先读取函数声明.这就意味着可以把函数声明放在调用它的语句 后面. sayHi(); funct ...
- Javascript高级程序设计读书笔记(第六章)
第6章 面向对象的程序设计 6.2 创建对象 创建某个类的实例,必须使用new操作符调用构造函数会经历以下四个步骤: 创建一个新对象: 将构造函数的作用域赋给新对象: 执行构造函数中的代码: 返回新 ...
- JavaScript高级程序设计 读书笔记
第一章 JavaScript 简介 第二章 Html中使用JavaScript 第三章 基本概念 第四章 变量,作用域,内存 第五章 引用类型 第六章 面向对象 第七章 函数表达式 第八章 BOM 第 ...
- JavaScript高级程序设计-读书笔记(6)
第20章 JSON JSON是一个轻量级的数据格式,可以简化表示复杂数据结构的工作量 JSON的语法可以表示一下三种类型的值 l 简单值:使用与JavaScript相同的语法,可以在JS ...
- JavaScript高级程序设计-读书笔记(5)
第13章 事件 1.事件流 事件流描述的是从页面中接收事件的顺序.IE的事件流是事件冒泡流,而Netscape Communicator的事件流是事件捕获流. (1)事件冒泡,即事件开始时由最具体的元 ...
随机推荐
- 想提高运维效率,那就把MySQL数据库部署到Kubernetes 集群中
摘要:Kubernetes 很多看起来比较"繁琐"的设计的主要目的,都是希望为开发者提供更多的"可扩展性",给使用者带来更多的"稳定性"和& ...
- module 'numpy' has no attribute 'int'.
原因:np.int 在 NumPy 1.20 中已弃用,在 NumPy 1.24 中已删除 AttributeError: module 'numpy' has no attribute 'int'. ...
- Java 剑指offer(16) 打印1到最大的n位数
题目 输入数字n,按顺序打印出从1最大的n位十进制数.比如输入3,则打印出1.2.3一直到最大的3位数即999. 思路 陷阱: n过大时是大数问题,不能简单用int或者long数据输出,需要采用字符串 ...
- Leaflet 使用图片作为地图
Leaflet 使用图片作为地图 关键代码: L.CRS.Simple.transformation = new L.Transformation(1, 0, 1, 0); // 坐标原点切换为左上角 ...
- CH0304 IncDec Sequence (差分)
题目链接: https://ac.nowcoder.com/acm/contest/999/B 思路:(见图中解释) AC代码: #include<bits/stdc++.h> using ...
- L2-016 愿天下有情人都是失散多年的兄妹 (25分) (简单递归判断)
L2-016 愿天下有情人都是失散多年的兄妹 (25分) 呵呵.大家都知道五服以内不得通婚,即两个人最近的共同祖先如果在五代以内(即本人.父母.祖父母.曾祖父母.高祖父母)则不可通婚.本题就请你帮助一 ...
- Goolge Kick Start Round A 2020 (A ~ D题题解)
比赛链接:kick start Round A 2020 A. Allocation 题目链接 题意 给出 \(N\) 栋房子的价格,第 \(i\) 栋房子的价格为 \(A_i\),你有 \(B\) ...
- Serverless 时代开启,云计算进入业务创新主战场
作者 | 于洪涛 "我们希望让用户做得更少而收获更多,通过 Serverless 化,让企业使用云服务像用电一样简单." Serverless 化正在成为全新的软件研发范式,阿里云 ...
- Contest3376 - 2024寒假集训-排位赛竞赛(一)
A: 幂位和 高精度. 用高精度加法或乘法算出\(2^{1000}\),再将各位累加即为答案. #include <bits/stdc++.h> using namespace std; ...
- java基础-构建工具mvn-day20
目录 1. 初识mvn 2. 用maven创建工程 3. maven工程 之间的关系 4. 父子 mvn工程 5. mvn常见的插件 6. tomcat插件 1. 初识mvn mvn是一个项目构建工具 ...