首页 > 代码库 > 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语言精粹_第四章