学习笔记 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 (不报错)
可选链只允许前面的值成为可选值,不影响后面。可选链前的变量必须已声明,否则会触发一个错误。
可使用 ?.() 可选的调用一个可能不存在的函数。
可使用 ?.[] 来使用方括号访问属性。