• 忘掉天地
  • 仿佛也想不起自己
bingliaolongBingliaolong  2025-04-05 23:47 Aet 隐藏边栏 |   抢沙发  3 
文章评分 1 次,平均分 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 中,有三种声明变量的方式:letconst(现代方式),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 expressionsIIFE
    1. 如今,我们不应该再使用 IIFE 了,但是你可以在旧脚本中找到它们

  1. 创建方法

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

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

发表评论

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