首页 > 代码库 > JavaScript语言精粹_第四章
JavaScript语言精粹_第四章
4.1 函数对象
在JavaScript中,函数就是对象。对象是“名/值”对的集合并拥有一个连到原型对象的隐藏链接。对象字面量产生的对象连接到Object.prototype。函数对象连接到Function.prototype(该原型对象本身连接到object.prototype)
每个函数对象在创建是也随带一个prototype属性,它的值是一个拥有constructor属性且至即为该函数的对象
函数可以存放变量,可以被当作参数传递给其他函数,也可以在返回函数。
因为函数是对象,所以它可以像其他的值一样被使用,也可以拥有方法
4.3 调用
调用一个函数将暂停当前函数的执行,传递控制权和参数给新函数。每个函数接受两个附加的参数:this和arguments。
在JavaScript中有四种调用模式:方法调用模式,函数调用模式,构造器调用模式和apply调用模式。这些模式在如何初始化关键参数this上存在差异。
方法调用模式
函数保存为对象的一个属性时,它被称为方法。方法可以通过this去访问,this到对象的绑定发生在调用的时候,这个“超级”迟绑定使得函数可以对this高度复用。
函数调用模式
当一个函数并非一个对象的属性时,它被当作一个函数来调用(在对象的方法中再次定义的方法)。
当函数以此模式调用时,this被绑定到全局对象(这是一个bug),可以该方法定义一个变量并给它赋值为this,那么内部函数就可以通过那个变量访问到this
构造器调用模式
如果在一个函数前面带上new来调用,那么将创建一个隐藏链接到该函数的prototype成员的新对象,同时this将对绑定到那个新对象上。
结合new前缀调用的函数被称为构造器函数,它们保存在以大写格式命名的变量里,如果调用构造器函数时没有在前面加上new,可能会发生非常糟糕的事情(在其他地方报错,可能你要改一天。。。),既没有编译时警告,也没有运行时警告,所以大写约定非常重要。
Apply调用模式
apply让我们构建一个参数数组并用其去调用函数,它允许我们选择this的值。apply方法接收两个参数,第一个是将绑定给this的值,第二个就是参数数组
4.4 参数
当函数被调用时,会得到arguments数组,这使得编写一个无须指定参数个数的函数成为可能。
可以构造一个将很多个值相加的函数。
因为这个设计错误,使得arguments并非一个真正的数组,它只是一个“类似数组”的对象,它拥有length属性,但缺少所有的数组方法。
4.5 返回
return语句可以用来是函数提前返回。一个函数总是会返回一个值,如果没有制定返回值,则返回undefined。
如果函数以在前面加上new前缀的方式来调用,且返回值不是一个对象,则返回this(该新对象)
4.7 给类型增加方法
JavaScript允许给语言的基本类型增加方法。
JavaScript没有单独的整数类型,我们可以通过Number.prototype添加一个integer来改善它。
Number.prototype.integer = function (){
return Math(this < 0 ? ‘ceiling‘ : ‘floor‘](this);
}
基本类型的原型是公共的结构,所以只在确定没有改方法时才添加它。
4.8 递归
递归函数可以非常高效地操作树形结构,比如浏览器端的文档对象模型(DOM),每次递归调用时处理给定树的一小段。
var walk_the_DOM = function walk(node,func){
func(node);
node = node.firstChild;
while(node){
walk(node,func);
node = node.nextSibling;
}
};
var getElementsByAttrbute = function(attr,value){
var rusults = [];
walk_the_DOM(document.body,function(node){
var actual = node.nodeType === 1 && node.getAttrbute(att);
if(typeof actual === ‘string‘ && (actual === value || typeof value !== ‘string‘)){
results.push(node);
}
});
return results;
};
一些语言提供了尾递归优化,这意味着如果一个函数返回自身递归调用的结果,那么调用的过程会被替换为一个循环,它可以显著提高速度。但这里JavaScript没有提供尾递归优化,深度递归的函数可能会因为返回堆栈溢出而运行失败。
4.9 作用域
JavaScript有函数作用域,但是没有块级作用域。最好的做法是在函数整体的顶部声明函数中可能用到的所有变量
4.10 闭包
内部函数拥有比它的外部更长的生命周期
一个常见的例子
var add_the_handlers = function (nodes) {
var i;
for(i=0; i < nodes.length; i += 1){
nodes[i].onclick = function (e){
alert(i);
}
}
};
它总是会显示节点的数目,是因为事件处理器函数绑定了变量i,而不是函数在构造是的变量i的值
var add_the_handlers = function (nodes) {
var i;
for(i=0; i < nodes.length; i += 1){
nodes[i].onclick = function (i){
return function(e){
alert(i);
};
}(i);
}
};
4.11 回调
函数可以让不连续事件的处理变得更容易,可以发起异步的请求,提供一个当服务器的响应到达时将被调用的回调函数。
4.12 模块
通过使用闭包和函数来构造模块,模块是一个提供接口却隐藏状态与实现的函数或对象,通过使用函数去产生模块,我们可以摈弃全局变量的使用,从而缓解这个糟糕的特性带来的影响。
模块的一般形式:一个定义了私有变量和函数的函数;利用闭包创建可以访问私有变量和函数的特权函数;最后返回这个特权函数,或者把他们保存到一个可访问到的地方。
模块模式通常通过单例模式使用。
4.13 级联
让一些方法返回this而不是undefined,就可以启用级联。
在一个级联中,我们可以在单独一条的语句中依次调用同一个对象的很多方法。
4.14 套用
可以将函数与传递给它的参数相结合去产生出一个新的函数
4.15 记忆
在计算机领域中,记忆是主要用于加速程序计算的一种优化技术,是的函数避免重复演算之前已被处理的输入,而返回以缓存的结果。
JavaScript的对象和数组要实现这种优化是非常方便的
var fibonacci = function (n) {
return n < 2 ? n : fibonacci (n - 1) + fibonacci (n - 2);
}
for (var i = 0; i <= 10; i += i){
document.writeIn( i + ‘=‘ + fibonacci(i));
}
这里调用了11次,而它自身调用了442次去计算可能已被刚计算过的值。
var fibonacci = function(){
var memo = [0, 1];
var fib = function (n){
var result = memo[n];
if(typeof result !== ‘number‘) {
result = fib(n - 1) + fib(n -2);
memo[n] = result;
}
return result;
};
return fib;
}();
这个函数返回同样的结果,调用了它11次,它自身调用了18次去取得之前存储的结果。
JavaScript语言精粹_第四章