第8章 函数
除了实参之外,每次调用还会拥有一个值——本次调用的上下文——这就是 this 关键字的值。
如果函数挂载在一个对象上,作为对象的一个属性,就称它为对象的方法。当通过这个对象来调用函数时,该对象就是此次调用的上下文(context),也就是该函数的 this 的值。用于初始化一个新创建的对象的函数称为构造函数(constructor)。
8.2.2 函数调用
this 是一个关键字,不是变量,也不是属性名。JS的语法不允许给 this 赋值。this 没有作用域的限制。嵌套的函数不会从调用它的函数中继承 this。
如果嵌套函数作为方法调用,其 this 的值指向调用它的对象。如果嵌套函数作为函数调用,其 this 值是全局对象。如果你想访问这个外部函数的 this 值,需要将 this 的值保存在一个变量里,这个变量和内部函数都同在一个作用域里。通常使用变量 self 来保存 this。
8.3.2 可变长的实参列表:实参对象
标识符arguments是指向实参对象的引用。实参对象是一个类数组对象,arguments 也包含一个 length 属性。
var max = function (/* number... */) {上述函数称为“不定实参函数”。实参个数不能为零。
var m = Number.NEGATIVE_INFINITY;
for (var i = 0; i < arguments.length; i++) {
if (arguments[i] >= m) {
m = arguments[i];
}
}
return m;
};
8.4 作为值的函数
var operators = {
add: function (x, y) {
return x + y;
},
substract: function (x, y) {
return x - y;
},
multiply: function (x, y) {
return x * y;
},
divide: function (x, y) {
return x / y;
},
pow: Math.pow
};
var operate2 = function (operator, operand1, operand2) {
if (typeof operators[operator] === 'function') {
return operators[operator](operand1, operand2);
}
else {
throw Error('Unknown operator!');
}
}
var i = operate2('add', operate2('add', 2, 3), operate2('po', 3, 3));
console.log(i);
自定义函数属性
函数可以拥有属性,当函数需要一个“静态”变量来调用时保持某个值不变,最方便的方式就是给函数定义属性,而不是全局变量,定义全局变量会让命名空间变得更加杂乱无章。
var uniqueInteger = function () {
return uniqueInteger.counter++;
};
uniqueInteger.counter = 0;
console.log(uniqueInteger()); //0
console.log(uniqueInteger()); //1
console.log(uniqueInteger()); //2
console.log(uniqueInteger()); //3
console.log(uniqueInteger()); //4
console.log(uniqueInteger.counter); //5
阶乘函数,把函数自身当做数组
function factorial (n) {
if (isFinite(n) && n >= 0 && n === Math.round(n)) {
if (!(n in factorial)) {
factorial[n] = n * factorial(n - 1);
}
return factorial[n];
}
else {
throw Error('Invalid number');
}
};
factorial[0] = 1;
console.log(factorial(5));
8.5 作为命名空间的函数
假设你写了一段JS代码,这段代码要用在不同的网页中,和大多数代码一样,假定这段代码定义了一个用以存储中间计算结果的变量,但,当模块代码放到不同的程序中运行时,你无法得知这个变量是否已经创建了,如果已经存在这个变量,那么将会和代码发生冲突。解决办法是将代码放入一个函数内,然后调用这个函数。这样全局变量就变成了局部变量。
8.6 闭包
var uniqueInteger = (function () {
var counter = 0;
return function () {
return counter++;
};
}());
console.log(uniqueInteger()); // 0
console.log(uniqueInteger()); // 1
console.log(uniqueInteger()); // 2
console.log(uniqueInteger()); // 3
// 计数器
var counter = function () {
var n = 0;
return {
count: function () {
return n++;
},
reset: function () {
n = 0;
}
};
};
可以将计数器的装饰合并为属性存取器方法 getter 和 setter。
function counter(n) {
return {
get count() {
return n++;
},
set count(m) {
if (m >= n) {
n = m;
}
else {
throw Error('count can only be set to a larger value');
}
};
}
}
var c = counter(1000);
c.count // 1000,count后面不能加括号
c.count // 1001
c.count = 2000 // 无返回值
c.count // 2000
c.count = 2000 // Error
function addPredictProperty(o, name, predict) {
var value;
o['get' + name] = function () {
return value;
};
o['set' + name] = function (v) {
if (predict && !predict(v)) {
throw Error('set' + name + ': invalid value' + v);
}
else {
value = v;
}
};
}