函数概述
- 定义: 封装一项专门任务的步骤清单的代码段, 并起一个名字
- 何时使用函数
- 当一项任务需要反复执行, 但又不希望重复编写时
- 函数: 封装函数定义的引用类型对象
- 函数名: 指向函数定义的一个变量
- 声明时不执行, 也不读取内部的代码, 调用时才读取内部代码并执行
- 声明提前: 正式开始执行程序前, 先将var声明的变量和function声明的函数提前到当前作用域的顶部, 集中声明, 赋值留在原地。
- 按值传递: 两变量间赋值, 或将变量作为函数的参数传递时, 都仅将变量中的值copy一份给对方。
- 全局函数: 在ES标准中规定的, 由浏览器厂商实现的。不需要任何对象前缀就可以直接访问的函数
- 编码解码
- 问题1: url中不允许出现多字节字符
- 解决: 使用encodeURI(), 将多字节字符编码为utf-8格式的单字节字符
- 使用decodeURI(), 将收到的编码后的单字节字符, 解码为多字节字符原文
- 问题2: url中不允许出现url的保留字符
- 解决: 使用encodeURIComponent(), 编码多字节字符和保留字
- 使用decodeURLComponent()解码
- 问题1: url中不允许出现多字节字符
- eval: 执行字符串格式的语句或表达式
- 如eval(“alert(‘Hello’)”)
- JS中分母是0, 不报错, 返回infinity。分子与分母同时为0, 返回NaN。
- 编码解码
函数对象
- 执行环境栈(ECS, Execution Context Stack): 保存全局以及每个函数的执行环境的栈结构
- 执行环境(EC): 调用函数时, 创建的引用函数资源的对象
- 变量对象(VO, Variable Object): 专门存储变量的对象
- 浏览器打开时的内存逻辑: 窗口一打开, 默认ESC中压入一个全局EC, 全局EC引用了window对象的VO
- 注意: window中的变量都是全局变量
- 活动对象(AO, Activation Object):专门保存本次函数调用时的局部变量, AO中有一个属性始终指向window对象
函数的生命周期
- 定义时: 仅创建一个函数对象, 封装了函数定义, 不会读取函数的内容
- 调用时: 创建函数的EC对象压入ECS中, 函数的EC对象引用了当前函数的AO
- 变量的使用规则: 优先在AO中找, 找不到, 再去window对象中找
- 调用后: 函数的EC对象出栈, AO对象失去引用, 被回收, AO对象的局部变量一起被释放
函数重载
- 重载: 程序中同时定义多个相同函数名, 不同函数列表的函数。调用时, 根据传入参数的不同, 动态选择匹配的函数执行。
- 注意: JS语法不支持重载, 可使用arguments实现重载效果。
- 何时使用重载: 不同参数, 不同操作, 但操作名称相同
- 使用重载的目的: 减轻调用者调用时选择的负担
- arguments: 所有函数对象中默认都有的专门接收调用时传入参数的值的类数组对象。
- 类数组对象: 类似于数组的对象
- 类数组对象与数组的相同点
- arguments.length: 参数值的个数
- arguments[i]: 获得下标为i位置的参数值
- 类数组对象与数组的相同点
- 参数变量: 提醒调用者, 如何正确使用函数
如何创建函数对象
- 声明方式: function 函数名(参数列表){函数体; return 值;}
- 只有声明方式才能被提前
- 函数直接量:
- var 函数名 = function(参数列表){函数体; return 值;}
- new function:
- var 函数名 = new function(“参数1”, “参数2”,…”函数体”);
匿名函数
- 定义: 定义时没有变量引用
- 何时使用:
- 匿名函数自调: 定义完立刻执行, 执行完立刻释放
- 何时使用自调: 一个函数只用一次
- 使用方法:
- (function(参数列表){})(参数值列表)
- (functiuon(参数列表){}(参数值列表))
- 回调: 将函数作为对象传递给其他函数, 由其他函数调用
- 何时使用回调: 如果一个函数只能被另一个函数调用
- 匿名函数自调: 定义完立刻执行, 执行完立刻释放
- 优点: 节省内存空间
- 缺点: 无法反复使用
作用域与作用域链
- 作用域: 一个变量的可用范围, 其实就是变量的实际存储位置
- 本质: EC对象的一个scope属性, 引用了window或AO
- window: 全局作用域 —— 全局变量
- AO: 局部作用域 —— 局部变量
- 本质: EC对象的一个scope属性, 引用了window或AO
- 作用域链: 以当前EC的scope属性为起点依次向上引用每个AO, 直到window结束, 形成的多级引用关系, 只要是在作用域链上存在的变量, 当前函数都可使用
闭包
- Problem: 全局变量, 在程序任何位置都可访问(修改), 这样会造成全局污染。但是局部变量又不可重用。
- 解决方案: 既要重用变量, 又要保护变量不被污染 —— 闭包
- 如何创建闭包:
- 先用外层函数封装一个受保护的局部变量
- 再用内层函数操作外层函数的变量
- 外层函数将内层函数返回到外部, 在外部反复调用
- 如何判断一个闭包:
- 函数嵌套
- 内层函数使用外层函数的变量
- 内层函数被返回到外部, 在外部调用
- 何时使用闭包: 既要重用变量, 又要保护变量不被污染
- 缺点: 占用更多的内存空间, 因为outer的活动对象无法释放
- 判断闭包输出
- 同一次外层函数调用返回的内层函数, 操作同一个变量
- 外层函数调用了几次就有几个受保护的变量副本