• 忘掉天地
  • 仿佛也想不起自己
bingliaolongBingliaolong  2025-04-05 23:47 Aet |  隐藏边栏 |   抢沙发 |  8 
文章评分 2 次,平均分 5.0 :

递归

执行上下文和堆栈

  1. 有关正在运行的函数的执行过程的相关信息被存储在其 执行上下文 中
  2. 执行上下文是一个内部数据结构,它包含有关函数执行时的详细细节:当前控制流所在的位置,当前的变量,this 的值(此处我们不使用它),以及其它的一些内部细节
  3. 一个函数调用仅具有一个与其相关联的执行上下文

嵌套调用

  1. 当一个函数进行嵌套调用时,将发生以下的事儿:
    1. 当前函数被暂停;
    2. 与它关联的执行上下文被一个叫做 执行上下文堆栈 的特殊数据结构保存;
    3. 执行嵌套调用;
    4. 嵌套调用结束后,从堆栈中恢复之前的执行上下文,并从停止的位置恢复外部函数

链表

  1. 如果我们确实需要快速插入/删除,则可以选择另一种叫做 链表的数据结构
  2. 链表元素 是一个使用以下元素通过递归定义的对象:
    1. value。
    2. next 属性引用下一个 链表元素 或者代表末尾的 null

  1. 添加一个新值,我们需要更新链表的头:

  1. 要从中间删除一个值,可以修改前一个元素的 next:

  1. 链表主要的缺点
    1. 无法很容易地通过元素的编号获取元素

Rest 参数与 Spread 语法

概述

  1. 在 JavaScript 中,很多内建函数都支持传入任意数量的参数
    1. Math.max(arg1, arg2, ..., argN) —— 返回参数中的最大值。
    2. Object.assign(dest, src1, ..., srcN) —— 依次将属性从 src1..N 复制到 dest。

Rest 参数 ...

  1. 在 JavaScript 中,无论函数是如何定义的,你都可以在调用它时传入任意数量的参数
    1. 例如
    2. 虽然这里这个函数不会因为传入过多的参数而报错。但是,当然,只有前两个参数被求和了

  1. 可以在函数定义中声明一个数组来收集参数
    1. 语法是这样的:...变量名,这将会声明一个数组并指定其名称,其中存有剩余的参数
    2. 这三个点的语义就是“收集剩余的参数并存进指定数组中”

  1. 也可以选择将第一个参数获取为变量,并将剩余的参数收集起来

注意:Rest参数

  1. Rest 参数必须放到参数列表的末尾

“arguments” 变量

  1. 有一个名为 arguments 的特殊类数组对象可以在函数中被访问,该对象以参数在参数列表中的索引作为键,存储所有参数

注意:箭头函数

  1. 在箭头函数中访问 arguments,访问到的 arguments 并不属于箭头函数,而是属于箭头函数外部的“普通”函数
    1. 所以,箭头函数没有 "arguments"
    2. 好比,箭头函数没有自身的 this

Spread 语法

  1. 从数组中获取参数列表
  2. 例如,内建函数Math.max会返回参数中最大的值:
    1. 如果我们有一个数组 [3, 5, 1],我们该如何用它调用 Math.max 呢?
    2. 直接“原样”传入这个数组是不会奏效的,因为 Math.max 期望的是列表形式的数值型参数,而不是一个数组:

  1. 我们不能手动地去一一设置参数 Math.max(arg[0], arg[1], arg[2]),因为我们不确定这儿有多少个
    1. 在代码执行时,参数数组中可能有很多个元素,也可能一个都没有
    2. Spread 语法 可以解决这个问题
    3. 它看起来和 rest 参数很像,也使用 ...,但是二者的用途完全相反
    4. 当在函数调用中使用 ...arr 时,它会把可迭代对象 arr “展开”到参数列表中
    5. 以 Math.max 为例:

  1. 还可以使用 spread 语法来合并数组:

  1. 上面的示例中,我们使用数组展示了 spread 语法,其实我们可以用 spread 语法这样操作任何可迭代对象
    1. Spread 语法内部使用了迭代器来收集元素,与 for..of 的方式相同
    2. 对于这个特定任务,我们还可以使用 Array.from 来实现,因为该方法会将一个可迭代对象(如字符串)转换为数组:

  1. 不过 Array.from(obj) 和 [...obj] 存在一个细微的差别:
    1. Array.from 适用于类数组对象也适用于可迭代对象
    2. Spread 语法只适用于可迭代对象

复制 array/object

  1. 使用 spread 语法也可以做同样的事情(译注:也就是进行浅拷贝)

变量作用域,闭包

概述

  1. JavaScript 是一种非常面向函数的语言。它给了我们很大的自由度
    1. 在 JavaScript 中,我们可以随时创建函数,可以将函数作为参数传递给另一个函数,并在完全不同的代码位置进行调用

let/const

  1. 在 JavaScript 中,有三种声明变量的方式:let,const(现代方式),var(过去留下来的方式)
    1. 用 const 声明的变量的行为也相同(译注:与 let 在作用域等特性上是相同的)
    2. 旧的 var 与上面两个有着明显的区别

代码块

  1. 如果在代码块 {...} 内声明了一个变量,那么这个变量只在该代码块内可见

  1. 这里如果没有代码块则会报错

嵌套函数

  1. 如果一个函数是在另一个函数中创建的,该函数就被称为“嵌套”函数
    1. 这里创建的 嵌套 函数 getFullName() 是为了更加方便
    2. 它可以访问外部变量,因此可以返回全名。嵌套函数在 JavaScript 中很常见

  1. 更有意思的是,可以返回一个嵌套函数:作为一个新对象的属性或作为结果返回
    1. 之后可以在其他地方使用。不论在哪里调用,它仍然可以访问相同的外部变量

词法环境

  1. 在 JavaScript 中,每个运行的函数,代码块 {...} 以及整个脚本,都有一个被称为 词法环境(Lexical Environment) 的内部(隐藏)的关联对象
  2. 词法环境对象由两部分组成:
    1. 环境记录(Environment Record) —— 一个存储所有局部变量作为其属性(包括一些其他信息,例如 this 的值)的对象
    2. 对 外部词法环境 的引用,与外部代码相关联

词法环境:变量

  1. 一个“变量”只是 环境记录 这个特殊的内部对象的一个属性
  2. “获取或修改变量”意味着“获取或修改词法环境的一个属性”
  3. 举个例子,这段没有函数的简单的代码中只有一个词法环境:
    1. 这就是所谓的与整个脚本相关联的 全局 词法环境

注意:词法环境

  1. “词法环境”是一个规范对象(specification object):它只存在于 语言规范的“理论”层面,用于描述事物是如何工作的
  2. 我们无法在代码中获取该对象并直接对其进行操作

词法环境:函数声明

  1. 一个函数其实也是一个值,就像变量一样
    1. 不同之处在于函数声明的初始化会被立即完成
  2. 当创建了一个词法环境(Lexical Environment)时,函数声明会立即变为即用型函数(不像 let 那样直到声明处才可用)
    1. 这就是为什么我们甚至可以在声明自身之前调用一个以函数声明(Function Declaration)的方式声明的函数

内部和外部的词法环境

  1. 在一个函数运行时,在调用刚开始时,会自动创建一个新的词法环境以存储这个调用的局部变量和参数
    1. 在这个函数调用期间,我们有两个词法环境:内部一个(用于函数调用)和外部一个(全局):
    2. 内部词法环境与 say 的当前执行相对应。它具有一个单独的属性:name,函数的参数。我们调用的是 say("John"),所以 name 的值为 "John"
    3. 外部词法环境是全局词法环境。它具有 phr 变量和函数本身

  1. 当代码要访问一个变量时 —— 首先会搜索内部词法环境,然后搜索外部环境,然后搜索更外部的环境,以此类推,直到全局词法环境

词法环境:返回函数

  1. 回到 makeCounter 这个例子
    1. 在每次 makeCounter() 调用的开始,都会创建一个新的词法环境对象,以存储该 makeCounter 运行时的变量
    2. 因此,我们有两层嵌套的词法环境
    3. 不同的是,在执行 makeCounter() 的过程中创建了一个仅占一行的嵌套函数:return count++。我们尚未运行它,仅创建了它
    4. 所有的函数在“诞生”时都会记住创建它们的词法环境
      从技术上讲,这里没有什么魔法:所有函数都有名为 [[Environment]] 的隐藏属性,该属性保存了对创建该函数的词法环境的引用
    5. 因此,counter.[[Environment]] 有对 {count: 0} 词法环境的引用
      这就是函数记住它创建于何处的方式,与函数被在哪儿调用无关
      [[Environment]] 引用在函数创建时被设置并永久保存
    6. 稍后,当调用 counter() 时,会为该调用创建一个新的词法环境,并且其外部词法环境引用获取于 counter.[[Environment]]:
    7. 当 counter() 中的代码查找 count 变量时,它首先搜索自己的词法环境(为空,因为那里没有局部变量),然后是外部 makeCounter() 的词法环境,并且在哪里找到就在哪里修改

闭包

  1. 指一个函数可以记住其外部变量并可以访问这些变量
  2. 在某些编程语言中,这是不可能的,或者应该以一种特殊的方式编写函数来实现
  3. 但如上所述,在 JavaScript 中,所有函数都是天生闭包的(只有一个例外,"new Function" )

垃圾回收

  1. 通常,函数调用完成后,会将词法环境和其中的所有变量从内存中删除
    1. 因为现在没有任何对它们的引用了
    2. 与 JavaScript 中的任何其他对象一样,词法环境仅在可达时才会被保留在内存中
  2. 但是,如果有一个嵌套的函数在函数结束后仍可达,则它将具有引用词法环境的 [[Environment]] 属性
    1. 下面这个例子中,即使在(外部)函数执行完成后,它的词法环境仍然可达
    2. 因此,此词法环境仍然有效

变量

  1. var 声明与 let 相似。大部分情况下,我们可以用 let 代替 var 或者 var 代替 let,都能达到预期的效果:

var

  1. 用 var 声明的变量,不是函数作用域就是全局作用域
    1. 它们在代码块外也是可见的(译注:也就是说,var 声明的变量只有函数作用域和全局作用域,没有块级作用域)
    2. 由于 var 会忽略代码块,因此我们有了一个全局变量 test

  1. var允许重新声明

  1. “var” 声明的变量,可以在其声明语句前被使用
    1. 这种行为称为“提升”
    2. 因为所有的 var 都被“提升”到了函数的顶部

IIEF

  1. 在之前,JavaScript 中只有 var 这一种声明变量的方式,并且这种方式声明的变量没有块级作用域
  2. 程序员们就发明了一种模仿块级作用域的方法。这种方法被称为“立即调用函数表达式”(immediately-invoked function expressions,IIFE)
    1. 如今,我们不应该再使用 IIFE 了,但是你可以在旧脚本中找到它们

  1. 创建方法

声明:本文为原创文章,版权归所有,欢迎分享本文,转载请保留出处!

bingliaolong
Bingliaolong 关注:0    粉丝:0
Everything will be better.

发表评论

表情 格式 链接 私密 签到
扫一扫二维码分享
繁