学习笔记 2020-10-27
JavaScript 高级程序设计(第4版) 阅读记录
函数
函数表达式
函数表达式创建的函数叫做匿名函数,也叫做兰姆达函数。因为 function
关键字没有标识符。未赋值给其他变量的匿名函数的 name
属性是空字符串。
递归
递归函数通常的形式是一个函数通过名称调用自己。
在非严格模式下,可以使用 arguments.callee
来访问自身函数。在严格模式下,可以使用命名函数表达式。
const factorial = function f(num) {
if (num <= 1) {
return 1;
} else {
return num * f(num - 1);
}
};
尾调用优化
ECMAScript6
新增了一项内存管理优化机制,让 JS
引擎在满足条件时可以重用栈帧。例如尾调用,即外部函数的返回是内部函数的返回值。
function outerFunction() {
return innerFunction(); // 尾调用
}
在 ES6
优化之前,这个例子会在内存中发生如下操作。
- 执行到
outerFunction
函数体,第一个栈帧被推到栈上。 - 执行
outerFunction
函数体,到return
语句。计算返回值必须先计算innerFunction
。 - 执行到
innerFunction
函数体,第二个栈帧被推到栈上。 - 执行
innerFunction
函数体,计算其返回值。 - 将返回值传回
outerFunction
,然后outerFunction
再返回值。 - 将栈帧弹到栈外。
在 ES6
优化之后,执行这个例子会在内存中发生如下操作。
- 执行到
outerFunction
函数体,第一个栈帧被推到栈上。 - 执行
outerFunction
函数体,到达return
语句。为求值返回语句,必须先求值innerFunction
。 - 引擎发现把第一个栈帧弹出栈外也没问题,因为
innerFunction
的返回值也是outerFunction
的返回值。 - 弹出
outerFunction
的栈帧。 - 执行到
innerFunction
函数体,栈帧被推到栈上。 - 执行
innerFunction
函数体,计算其返回值。 - 将
innerFunction
的栈帧弹出栈外。
尾调用优化的条件
条件是外部栈帧没有必要存在。
代码在严格模式下执行。
外部函数的返回值是对为调用函数的调用。
尾调用函数返回后不需要执行额外的逻辑。
尾调用函数不是引用外部函数作用域中自由变量的闭包。
要求严格模式的原因是非严格模式下函数调用中允许使用 f.arguments
和 f.caller
,而它们都会引用外部函数的栈帧。
闭包
函数执行时,每个执行上下文中都会有一个包含其中变量的对象。全局上下文中的叫变量对象,它会在代码执行期间始终存在。而函数内部上下文中的叫活动对象,只在函数执行期间存在。在定义函数时,就会为它创建作用域链,预装载全局变量对象,并保存在内部的 [[Scope]]
中。在调用这个函数时,会创建相应的执行上下文,然后通过复制函数的 [[Scope]]
来创建其作用域链。接着创建函数的活动对象并将其推入作用域链的前端。
函数内部的代码访问变量时,就会使用给定的名称从作用域链中查找变量。函数执行完毕后,局部活动对象会被销毁。
而在闭包中,一个函数若是引用了外部作用域的变量,当这个函数存在的时候,外部作用域就不会被销毁。即使外部作用域对应的函数已经执行完毕。
this
对象
可以利用闭包来使一个函数内部访问外部作用域的 this
对象。
即定义一个变量来保存外部函数的 this
引用,这样内部函数可以访问到该变量。
window.identity = 'The Window';
let object = {
identity: 'My Object',
getIdentity() {
return this.identity;
}
};
object.getIdentity(); // 'My Object'
(object.getIdentity)(); // 'My Object'
(object.getIdentity = object.getIdentity)(); // 'The Window'
内存泄漏
IE
在 IE9
以前对 JScript
对象和 COM
对象使用了不同的垃圾回收机制,所以闭包在旧版本中可能会导致问题。即存在内存泄漏的问题。
现代 JavaScript 教程
可选链 “?.”
一个常见的情景:
let user = {}; // user 没有 address
console.log(user && user.address && user.address.street); // undefined(不报错)
可选链会在遇到 undefined
或 null
时停止运算并返回 undefined
。
let user = {}; // user 没有 address
console.log(user?.address?.street); // undefined (不报错)
可选链只允许前面的值成为可选值,不影响后面。可选链前的变量必须已声明,否则会触发一个错误。
可使用 ?.()
可选的调用一个可能不存在的函数。
可使用 ?.[]
来使用方括号访问属性。