View on GitHub

baidu-ife

百度前端学院的练习及笔记

day25~27 函数

1. 执行环境及其作用域

1.1 执行上下文

全局执行环境: web浏览器为window对象

最外层定义的函数变量、所有未定义直接赋值的变量、window对象的属性方法

局部(函数)执行环境: 每个函数都有自己的执行环境

img

img

执行上下文是栈式同步执行

变量对象(VO)

变量初始化阶段

VO按照如下顺序填充:

  1. 建立arguments对象
  2. 函数参数(若为传入, 初始化该参数值为undefined)
  3. 函数声明(属性值指向函数所在的内存地址, 若发生命名冲突, 会覆盖)
  4. 变量声明(初始化变量值为undefined, 若发生命名冲突, 会忽略)

代码执行阶段

VO内属性被赋值 并确定this的指向, 变为了活动对象(AO)

作用域链

作用域链: 当前执行代码所在环境的变量对象(函数: 活动对象), 经过一个又一个包含环境, 一直延续到全局执行环境

创建函数时,会创建一个包含全局变量对象的作用域链,保存在[[Scope]]属性中
调用函数时,函数会创建一个执行环境,复制函数的[[Scope]]属性,并有活动对象被创建并推入执行环境作用域链
函数执行完毕后,活动对象被销毁(除闭包),只剩下全局作用域

img

延长作用域链: try-catch的catch块、with

作用域与函数作用域的区别:
作用域在编译阶段就会确定; 执行上下文在执行阶段由引擎创建

1.3 块级作用域

JavaScript没有块级作用域!

使用var声明的变量会自动被添加到最接近的环境中, 若没用var声明则添加到全局环境

若局部环境有同名标识符则不会使用父环境的标识符

1.4 声明提前

函数内的变量声明将被提前至函数体顶部, 但变量初始化还在以前的位置

原因: 变量对象, 见上

2. 函数

2.1 函数定义

// 函数声明语法定义
function sum(num1, num2) {
	return num1 + num2;
}
// 函数表达式定义
var sum = function(num1, num2) {
	return num1 + num2;
};
//Function构造函数
var sum = new Function("num1", "num2", "return num1 + num2"); //不推荐

函数声明和函数表达式: 函数声明在执行任何代码之前可用; 函数表达式必须等到解析器执行到他所在代码行,才会被执行

注意:

  1. 函数表达式中function后可以没有函数名, 之后要加上;
  2. 函数是对象, 函数名是指针
  3. return用于函数停止执行并返回其表达式的值给调用者, 若没有return则返回undefined
  4. 语句声明定义的函数不能出现在循环、条件、try/cache/finally/with中

2.2 函数调用

函数调用、方法调用、构造函数调用、间接调用

this的指向

原则: this指的是调用函数的对象

情况 this指向
函数调用 全局对象window
方法调用 上级对象
构造函数调用 生成的新对象
apply调用 apply()的第一个参数

说明:

  1. 构造函数调用若没形参可省略括号
  2. 嵌套函数若为方法调用, this指向调用它的对象; 若为函数调用, 指向全局对象/undefined

详见: 点此进入

2.3 函数中的实参和形参

arguments属性:

属性 说明
length 长度
callee 指向拥有arguments的函数(非严格模式)

注意: 严格模式下不能使用callee和修改agruments

2.4 作为值的函数

函数名是变量, 函数可以作为值来使用。不仅可以把函数传递给另一个函数, 可以将一个函数作为另一个函数的结果返回

在Array.sort()方法使用较多

2.5 作为命名空间的函数

在命名空间内定义的变量都不会污染到全局命名空间

//第一个()将函数变成表达式, 第二个()执行了这个函数
(function() {
    // 私有变量
    var age = 20;
    var name = 'Tom';

    // 私有方法
    function getName() {
        return `your name is ` + name;
    }

    // 共有方法
    function getAge() {
        return age;
    }

    // 将引用保存在外部执行环境的变量中,形成闭包,防止该执行环境被垃圾回收
    window.getAge = getAge;
})();

(function() {

}());
//以上两种方式都可以

很容易创建私有变量和私有方法, 结合闭包可以创建共有方法。

2.6 函数属性、方法

属性:

属性 说明
length 期望传入函数的实参个数
prototype 指向”原型对象”的引用

方法:

属性 说明
apply() 在【参数1】作用域上,调用函数,参数为【参数2(数组)】
call() 在【参数1】作用域上,调用函数,参数为【参数2~n】
bind() 创建一个this值绑定到【参数1】的函数实例, 第二个以及以后的参数加上新创建的绑定函数运行时的参数按照顺序作为原函数的参数来调取原函数。
toString()/ toLocaleString() 函数代码
valueOf() 函数代码

bind()的示例:

function getConfig(Colors, size, other) {
	console.log(Colors, size, other);
}
var defaultConfig = getConfig.bind(null, "#CC0000", "1024 * 768");
defaultConfig("123");  //#CC0000 1024 * 768 123

2.7 闭包

能够读取其他函数内部变量的函数

作用:

① 访问函数内部的变量
② 让变量的值始终存在内存中(执行环境的作用域链被销毁了,但活动对象任然会存在内存中)

总结:

  1. 闭包是在函数被调用执行的时候才被确认创建的。
  2. 闭包的形成,与作用域链的访问顺序有直接关系。
  3. 只有内部函数访问了上层作用域链中的变量对象时,才会形成闭包,因此,我们可以利用闭包来访问函数内部的变量。