1. Function
1.1. 创建: 3 种
-
声明:
function 函数名(参数列表) { 函数体; return 返回值; }
-
直接量:
var 函数名 = function (参数列表) { 函数体; return 返回值; }
-
用
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
语法中, 默认不支持多个同名函数同时存在- 如何变通:
- 仅定义一个函数
arguments
: 函数内自己创建- 用于接收传入函数的所有参数值的
- 类数组对象
- 类数组对象 VS 数组:
- 相同: 都有下标, 都有
length
, 可以通过for
遍历 - 不同: 类型不同 ->
API
不通用
- 相同: 都有下标, 都有
- 类数组对象 VS 数组:
- 函数内, 根据
arguments
中参数的个数 / 类型, 动态决定执行不同操作
- 问题: 参数是否还需要
- 需要
- 参数用于提示调用者如何正确使用函数
- 参数名一般都比
arguments
简洁且见名知意
- 需要
- 如何变通:
1.3. 匿名函数
1.3.1 什么是
创建函数时, 不指定函数名的函数
1.3.2. 何时: 2 种
- 当一个函数仅使用一次时
- 划分临时作用域, 避免全局污染时
1.3.3. 如何: 2 种
- 回调:
- Ex: 1.
arr.sort(function (a, b) { return a - b })
2.str.replace(reg, function (kw) { return 替换值 })
3.btn.addEventListener('click', function () { ...... })
- Ex: 1.
- 自调: 定义匿名函数后, 立刻调用自己, 调用后立刻释放
- 如何: 2 种
+function() { ...... }(......)
(function() { ...... })(......)
- 何时: 只要不希望照成全局污染时, 就用匿名函数包裹所有自定义代码
- 如何: 2 种
1.4. 作用域和作用域链
1.4.1 什么是作用域
一个变量的可用范围, 也是一个变量的实际存储位置
1.4.2. 包括: 2 种
- 全局作用域:
window
-> 保存全局变量- 特点: 随处可用, 可重复使用
- 函数作用域:
AO
-> 保存局部变量- 特点: 仅函数内可使用, 不可重用
1.4.3. 函数的生命周期
- 程序开始执行前:
- 创建执行环境栈(
ECS
): 依次保存每个调用函数的执行环境 - 在执行环境栈(
ECS
)中压入第一个全局执行环境( 全局EC
): 浏览器主程序main()
的执行环境 - 主程序创建全局作用域对象
window
, 全局EC
引用window
对象
- 创建执行环境栈(
- 开始执行程序:
- 所有全局变量都保存在全局作用域对象
window
中
- 所有全局变量都保存在全局作用域对象
- 定义函数:
- 用函数名创建全局函数
- 创建函数对象封装函数体
- 函数名变量引用函数体
- 函数对象中的
scope
属性, 引用回创建函数时的作用域对象, 通常指向window
- 调用函数
- 在执行环境栈(
ECS
)中压入一个新的执行环境EC
- 为本次函数调用创建专门的函数作用域对象
AO( 活动对象 )
- 在
AO
中添加函数中定义的所有局部变量(var
的和参数变量 ) - 新的执行环境(
EC
)引用AO
AO
的parent
指向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.5.5. 特点
- 函数嵌套
- 外层函数包含一个受保护的局部变量
- 外层函数将操作受保护变量的内层函数返回
试题
- 闭包是如何形成: 外层函数作用域
AO
无法释放
1.5.6. 闭包的问题
- 问题( 缺点 ): 比普通函数占用更多的内存 -> 外层函数作用域
AO
无法释放- 解决: 将引用闭包函数的变量( 接收内层函数的变量 )赋值为
null
- 导致内层函数释放 -> 导致外层函数的
AO
一并释放
- 导致内层函数释放 -> 导致外层函数的
- 解决: 将引用闭包函数的变量( 接收内层函数的变量 )赋值为
1.5.7. 画闭包简图: 2 步
- 找受保护的变量:
- 外层函数的局部变量(
var
的 / 参数变量 ) - 确定在外层函数调用后, 受保护变量的最终值
- 外层函数的局部变量(
- 找抛出的内层函数:
return
- 直接给全局变量赋值
- 隐藏抛出: 将内层函数放入一个数组 / 对象中隐藏返回
- 一次外层函数调用, 返回的多个内层函数, 共用一个受保护的变量
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
函数没有被正确执行