【读书笔记】细读《JavaScript权威指南》(第八章: 函数)
8.1 函数定义
- 函数表达式
var f = function fact (x) {
if (x<=1) return 1;
else return x * fact(x-1);
}
如果一个函数定义表达式包含名称,函数的局部作用域将会包含一个绑定到函数对象的名称,或说函数名称将成为函数内部的一个局部变量(通常应用于递归函数)。
- 函数语句
函数声明语句必须被提前到外部脚本或外部函数作用域的顶部。这也就意味这它们不能出现在循环、条件判断、with等语句块中。这也是es规范没有将函数声明语句归类为语句的原因。
8.2 函数调用
8.2.1 普通的函数调用
var a = f(x);
单独调用函数,计算返回值。普通的函数调用一般不使用this值。在非严格模式下,this指向全局对象;在严格模式下this值为undefined
8.2.2 方法调用
方法是指作为对象属性存在的函数。 与普通的函数调用的唯一区别就是调用上下文(this)指向调用该方法的对象。
方法链(“链式调用”风格的编程)
当方法的返回值是一个对象,这个对象还可以再调用它的方法。这种方法调用序列中(通常称为“链”或“级联”)每次的调用结果都是另外一个表达式的组成部分。
var o = {
m: function () {
var self = this;
console.log(this === o); // true
f();
function f () {
console.log(this === o); // false
console.log(self === o); // true
}
}
}
一个常见的误区就是误认为嵌套函数时this会指向外层函数的上下文。实际上只要记住普通的函数调用里的上下文只有两种可能指向。详见8.2.1小节。
8.2.3 构造函数调用
构造函数调用创建一个新的空对象,这个对象继承来自构造函数的prototype属性,构造函数试图初始化这个新创建的对象,并将其用做调用上下文。
var o = new Object();
var o = new Object;
如果构造函数没有形参,圆括号是可以省略的。
8.2.4 间接调用
任何函数都可以作为任何对象的方法来调用,哪怕这个函数不是那个对象的方法。call与apply都可以用来间接调用函数,两个方法都允许显式指定this。
8.3 函数的实参与形参
JavaScript的函数调用既不检查参数类型,也不检查传入参数的个数。
8.3.1 可选形参
当调用函数时传入的实参比函数声明时指定的形参个数要少,剩下的形参都将设置为undefined值。
function getPropertyNames (o, /* optional */ a) {
a = a || [];
for (var property in o) a.push(property);
return a;
}
可选实参要放在参数表后面,注释也是有必要的。
8.3.2 可变长的实参列表: 实参对象
函数体内可用标识符arguments访问实参对象。实参对象是一个类数组对象。
function max (x, y, z) {
let max = -Infinity;
for (let i = 0; i < argument.length; i++) {
if (max < argument[i]) max = argument[i];
}
return max;
}
callee和caller属性
实参对象还定义了两个属性。在非严格模式下,es规定callee指代当前正在执行的函数。caller是非标准的,但被大多数浏览器所实现,它指代调用当前正在执行的函数的函数。
var factorial = function (x) {
if (x <= 1) return 1;
return x * argument.callee(x-1); // 在匿名函数中递归调用自身
}
8.3.3 将对象属性用做实参
当函数包含超过三个形参时,记住实参的正确顺序就有点困难了。一个解决方案是通过键值对的形式来传入参数,这样参数的顺序就无关紧要了。
8.4 作为值的函数
自定义函数属性
当函数需要一个“静态”变量来在调用时保持某个值不变,最方便的方式就是给函数定义属性,而不是定义全局变量。
function uniqueInteger () {
return uniqueInteger.counter++;
}
8.5 作为命名空间的函数
定义一个函数用做临时的命名空间,在这个命名空间里定义的变量都不会污染到全局命名空间。
(function() {}());
该函数被称为立即执行函数,最外层的圆括号是必须的。如果不加,解释器会识图将function解析为函数声明语句,加了以后再会正确解析为函数定义表达式。
8.6 闭包
闭包的定义:
函数对象可以通过作用域链相互关联起来,函数体内部的变量可以保存在函数作用域中(看起来是函数将变量“包裹”了起来)。
闭包的主要特性:
捕捉到局部变量和参数,并一直保存下来。
var scope = 'global scope';
function checkscope () {
var scope = 'local scope';
function f () { return scope; }
return f;
}
checkscope()(); // 'local scope'
上面的代码就是使用闭包的一个基本形式,即在函数体外调用其嵌套子函数(前提是该子函数被函数返回)。
从作用域链上来看,调用 f 时闭包所指向的作用域链和定义 f 时的作用域链不同,简单地表示一下,前者是【f 的局部变量保存对象 - 全局对象】,而后者是【f 的局部变量保存对象 - checkscope 的局部变量保存对象 - 全局对象】
上一篇: Kotlin 基本数据类型
下一篇: Kotlin基本数据类型