0%

学习笔记 2020 10 27

学习笔记 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 优化之前,这个例子会在内存中发生如下操作。

  1. 执行到 outerFunction 函数体,第一个栈帧被推到栈上。
  2. 执行 outerFunction 函数体,到 return 语句。计算返回值必须先计算 innerFunction
  3. 执行到 innerFunction 函数体,第二个栈帧被推到栈上。
  4. 执行 innerFunction 函数体,计算其返回值。
  5. 将返回值传回 outerFunction ,然后 outerFunction 再返回值。
  6. 将栈帧弹到栈外。

ES6 优化之后,执行这个例子会在内存中发生如下操作。

  1. 执行到 outerFunction 函数体,第一个栈帧被推到栈上。
  2. 执行 outerFunction 函数体,到达 return 语句。为求值返回语句,必须先求值 innerFunction
  3. 引擎发现把第一个栈帧弹出栈外也没问题,因为 innerFunction 的返回值也是 outerFunction 的返回值。
  4. 弹出 outerFunction 的栈帧。
  5. 执行到 innerFunction 函数体,栈帧被推到栈上。
  6. 执行 innerFunction 函数体,计算其返回值。
  7. innerFunction 的栈帧弹出栈外。
尾调用优化的条件

条件是外部栈帧没有必要存在。

  • 代码在严格模式下执行。

  • 外部函数的返回值是对为调用函数的调用。

  • 尾调用函数返回后不需要执行额外的逻辑。

  • 尾调用函数不是引用外部函数作用域中自由变量的闭包。

要求严格模式的原因是非严格模式下函数调用中允许使用 f.argumentsf.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'
内存泄漏

IEIE9 以前对 JScript 对象和 COM 对象使用了不同的垃圾回收机制,所以闭包在旧版本中可能会导致问题。即存在内存泄漏的问题。

现代 JavaScript 教程

可选链 “?.”

一个常见的情景:

let user = {}; // user 没有 address
console.log(user && user.address && user.address.street); // undefined(不报错)

可选链会在遇到 undefinednull 时停止运算并返回 undefined

let user = {}; // user 没有 address
console.log(user?.address?.street); // undefined (不报错)

可选链只允许前面的值成为可选值,不影响后面。可选链前的变量必须已声明,否则会触发一个错误。

可使用 ?.() 可选的调用一个可能不存在的函数。

可使用 ?.[] 来使用方括号访问属性。