Function(创建、重载、匿名函数、作用域和作用域链、闭包)、错误处理

506 阅读7分钟

1. Function

1.1. 创建: 3 种
  1. 声明: function 函数名(参数列表) { 函数体; return 返回值; }

  2. 直接量: var 函数名 = function (参数列表) { 函数体; return 返回值; }

  3. new: var 函数名 = new Function('参数1', '参数2', '函数体; return 返回值;')

    • 强调: 所有参数变量和函数体, 必须用 "" / '' 包裹

  • 试题

    // 以下函数创建正确的是:
    function fun(a, b) { return a - b }
    var fun = function (a, b) { return a - b }
    var fun = new Function('a', b, 'return a - b') // 这个是错误的
    var fun = new Function('a', 'b', 'return a - b')
    
1.2. 重载: overload
1.2.1. 什么是

相同函数名, 不同参数列表的多个函数, 在调用时可以根据参数的不同, 自动选择对应的函数执行

1.2.2. 何时
  • 如果一件事, 根据不同的参数, 执行不同的逻辑时
1.2.3. 为什么
  • 减少函数的数量, 减轻调用者的负担
1.2.4. 如何
  • 问题: JS 语法中, 默认不支持多个同名函数同时存在
    • 如何变通:
      1. 仅定义一个函数
      2. arguments: 函数内自己创建
        • 用于接收传入函数的所有参数值的
        • 类数组对象
          • 类数组对象 VS 数组:
            • 相同: 都有下标, 都有 length, 可以通过 for 遍历
            • 不同: 类型不同 -> API 不通用
      3. 函数内, 根据 arguments 中参数的个数 / 类型, 动态决定执行不同操作
    • 问题: 参数是否还需要
      • 需要
        • 参数用于提示调用者如何正确使用函数
        • 参数名一般都比 arguments 简洁且见名知意
1.3. 匿名函数
1.3.1 什么是

创建函数时, 不指定函数名的函数

1.3.2. 何时: 2 种
  1. 当一个函数仅使用一次时
  2. 划分临时作用域, 避免全局污染时
1.3.3. 如何: 2 种
  1. 回调:
    • Ex: 1. arr.sort(function (a, b) { return a - b }) 2. str.replace(reg, function (kw) { return 替换值 }) 3. btn.addEventListener('click', function () { ...... })
  2. 自调: 定义匿名函数后, 立刻调用自己, 调用后立刻释放
    • 如何: 2 种
      1. +function() { ...... }(......)
      2. (function() { ...... })(......)
    • 何时: 只要不希望照成全局污染时, 就用匿名函数包裹所有自定义代码
1.4. 作用域和作用域链
1.4.1 什么是作用域

一个变量的可用范围, 也是一个变量的实际存储位置

1.4.2. 包括: 2 种
  1. 全局作用域: window -> 保存全局变量
    • 特点: 随处可用, 可重复使用
  2. 函数作用域: AO -> 保存局部变量
    • 特点: 仅函数内可使用, 不可重用
1.4.3. 函数的生命周期
  • 程序开始执行前:
    • 创建执行环境栈( ECS ): 依次保存每个调用函数的执行环境
    • 在执行环境栈( ECS )中压入第一个全局执行环境( 全局 EC ): 浏览器主程序 main() 的执行环境
    • 主程序创建全局作用域对象 window, 全局 EC 引用 window 对象
  • 开始执行程序:
    • 所有全局变量都保存在全局作用域对象 window
  • 定义函数:
    • 用函数名创建全局函数
    • 创建函数对象封装函数体
    • 函数名变量引用函数体
    • 函数对象中的 scope 属性, 引用回创建函数时的作用域对象, 通常指向 window
  • 调用函数
    • 在执行环境栈( ECS )中压入一个新的执行环境 EC
    • 为本次函数调用创建专门的函数作用域对象 AO( 活动对象 )
    • AO 中添加函数中定义的所有局部变量( var 的和参数变量 )
    • 新的执行环境( EC )引用 AO
    • AOparent 指向 window
    • 变量使用顺序: 优先使用 AO 中的局部变量, AO 中没有, 才去全局找
  • 调用后:
    • 执行环境栈( ECS )中本次函数调用的 EC 出栈
    • 导致函数作用域对象 AO 被释放
    • 导致局部变量一同释放
1.4.4. 作用域链

由各级作用域对象连续形成的链式结构

  • 顺序: 先函数作用域对象 AO -> 全局作用域对象 window
  • 所有的变量都保存在作用域链上的对象中
    • 局部变量都保存在函数作用域对象 AO
    • 全局变量都保存在全局作用域对象 window
  • 控制了变量的使用顺序
    • 先用 AO( 函数作用域 ) 中的局部变量, 如果 AO 中没有, 才去 window( 全局作用域 ) 中找
1.5. 闭包
1.5.1. 什么是

既重用变量, 又保护变量不被污染的一种机制

1.5.2. 为什么
  • 全局变量和局部变量都有优缺点:
    • 全局:
      • 优点: 可反复使用
      • 缺点:随处可用-> 易被污染
    • 局部:
      • 优点: 仅在函数内部可用, 不会被污染
      • 缺点: 不可重用
1.5.3. 何时
  • 既要重用, 又要保护变量不被污染
1.5.4. 如何: 3 步
  1. 用外层函数, 包裹住受保护的变量和操作受保护变量的内层函数
  2. 外层函数将内层函数返回到外部, 使用者调用外层函数, 获得返回的内层函数对象
  3. 通过调用内层函数, 访问被保护的变量
1.5.5. 特点
  1. 函数嵌套
  2. 外层函数包含一个受保护的局部变量
  3. 外层函数将操作受保护变量的内层函数返回

试题

  • 闭包是如何形成: 外层函数作用域 AO 无法释放
1.5.6. 闭包的问题
  • 问题( 缺点 ): 比普通函数占用更多的内存 -> 外层函数作用域 AO 无法释放
    • 解决: 将引用闭包函数的变量( 接收内层函数的变量 )赋值为 null
      • 导致内层函数释放 -> 导致外层函数的 AO 一并释放
1.5.7. 画闭包简图: 2 步
  1. 找受保护的变量:
    • 外层函数的局部变量( var 的 / 参数变量 )
    • 确定在外层函数调用后, 受保护变量的最终值
  2. 找抛出的内层函数:
    1. return
    2. 直接给全局变量赋值
    3. 隐藏抛出: 将内层函数放入一个数组 / 对象中隐藏返回
      • 一次外层函数调用, 返回的多个内层函数, 共用一个受保护的变量

2. 错误处理

2.1. 什么是错误

程序运行过程中, 导致程序无法继续执行的异常情况

  • 发生错误后, 程序会立刻中断退出
2.2. 什么是错误处理
  • 即使程序发生错误, 也保证不强行退出的一种机制
2.3. 为什么
  • 避免程序强行退出, 导致极差的用户体验
2.4. 何时
  • 只要希望即使发生错误, 也保证不强行退出
2.5 如何
try {
  // 可能发生错误的语句
} catch(err) { // 只有发生错误, 才执行
  // 错误代码处理: 1. 保存进度  2. 提示用户  3. 记录日志
}
  • 其中: err 是错误处理对象
2.6 错误处理对象
2.6.1. 什么是

错误发生时, 自动创建的, 保存错误信息的对象

2.6.2. 问题
  • 性能问题: 放入 try catch 中的代码, 可能执行效率会变低, 且一旦发生错误, 还要额外创建 Error 对象, 占用更多的内存空间
    • 解决: 如果可提前与之错误原因, 可用 if ...... else ...... 来代替 try catch -> 对于开发人员的的经验要求非常高
2.6.3. 主动抛出错误
  • 什么是: 当前程序运行出错时, 主动新建一个错误, 抛出
  • 何时: 在协作开发中, 函数的作者, 可用主动抛出错误的方式提醒调用者如何正确使用函数
  • 如何: throw new Error('错误提示')
2.6.4. JS 中错误的错误类型
  • SyntaxError: 语法错误
  • ReferenceError: 引用错误, 要用的变量未找到
  • TypeError: 类型错误, 使用错误的类型调用函数或访问元素
  • RangeError: 范围错误, 参数超范围
  • URLError: URL 错误, 与 URL 相关函数参数不正确
  • EvalError: eval 函数没有被正确执行