《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)事件冒泡,即事件开始时由最具体的元 ...
随机推荐
- 测试用例又双叒叕失败了,NLP帮你
摘要:本文将介绍如何使用AI技术实现失败测试用例的智能分析. 本文分享自华为云社区<测试用例又双叒叕失败了,啥原因?NLP帮你来分析>,作者: 敏捷的小智 . 随着软件行业的快速发展,为了 ...
- 如何给网页和代码做HTML加密?
如何给网页和代码做HTML加密? 本篇文章给大家谈谈html混淆加密在线,以及HTML在线加密对应的知识点,希望对各位有所帮助,不要忘了收藏本站喔. 如何给代码加密? 1.源代码加密软件推荐使 ...
- 火山引擎数智平台最新直播活动:ByteHouse技术架构与最佳实践分享
数据的时效性,正深刻影响着企业的发展. 以大型半导体制造厂商为例,不同于常规工厂生产流水线,半导体制造通用的无人实验室生产模式高度依赖机械臂作业,且对整个生产调度链路中的精密度要求非常高,这背后主 ...
- 使用 Kubeadm 部署 Kubernetes(K8S) 安装 -- 持久化存储(PV&PVC)
使用 Kubeadm 部署 Kubernetes(K8S) 安装 -- Ingress-Ngnix 使用 Kubeadm 部署 Kubernetes(K8S) 安装 -- 持久化存储(NFS网络存储) ...
- Bug生命周期
新建,确认,解决,重新验证,关闭,重新打开 一个Bug由测试人员发现并提交,我们将状态标注为新建:开发人员接收了该Bug,将Bug的状态修改为已分配,表示已经认可:开发人员解决了该bug后,就将bug ...
- lab3 page tables
1.Speed up system calls (easy) 要求:有些操作系统(例如 Linux)通过在用户空间和内核之间的只读区域共享数据来加速某些系统调用.这样可以消除在执行这些系统调用时进行内 ...
- L3-020 至多删三个字符 (30分) (DP)
问题描述: 给定一个全部由小写英文字母组成的字符串,允许你至多删掉其中 3 个字符,结果可能有多少种不同的字符串? 输入格式: 输入在一行中给出全部由小写英文字母组成的.长度在区间 [4, 1e6] ...
- 阿里云张建锋:核心云产品全面 Serverless 化
11月3日,2022 杭州 · 云栖大会上,阿里云智能总裁张建锋表示,以云为核心的新型计算体系正在形成,软件研发范式正在发生新的变革,Serverless 是其中最重要的趋势之一,阿里云将坚定推进核心 ...
- Serverless实战——2分钟,教你用Serverless每天给女朋友自动发土味情话
原文链接:https://developer.aliyun.com/article/981268 演示视频:https://developer.aliyun.com/live/249772 一.Ser ...
- 黑池舞蹈节banner