资讯专栏INFORMATION COLUMN

《JavaScript 语言精粹》 读书笔记 - 函数(二)

lufficc / 1784人阅读

摘要:对象被传递到从句中被捕获。一些语言提供了尾递归优化。这意味着如果一个函数返回自身递归调用的结果,那么调用的过程会被替换为一个循环,可以显著提高速度。构建一个带尾递归的函数。语言精粹读书笔记函数

第四章 函数 Functions (二) 参数 arguments

arguments数组: 函数可以通过此参数访问所有它被调用时传递给它的参数列表,包括哪些没有被分配给函数声明时定义的形式参数的多余参数。

类似数组"(array-like)"的对象。arguments拥有一个length属性,没有任何数组方法。

返回 return

return被执行,函数立刻返回而不再执行余下的语句

一个函数总是会返回一个值,没有指定返回值,则返回 undefined

使用 new 调用函数,且返回值不是一个对象,则返回this (该新对象)

异常 Exceptions

异常是干扰程序的正常流程的不寻常(但并非完全是出乎意料的)事故

 try {
   if(false) {
     throw {
       name:"TypeError",
       message:"number is required"
     }
   }
 }catch(e) {
   document.write(e.name + ": "+e.message)
 }

throw 语句中断函数的运行。对象被传递到catch 从句中被捕获。

扩充类型的功能

通过给Function.prototype 增加方法,来使得该方法对所有函数可用:

   Function.prototype.method = function(name,func) {
     this.prototype[name] = func;
     return this;
   }

这样在函数,数组,字符串,数字,正则表达式和布尔值等基本类型的构造函数上添加方法时,就可以省去prototype 这几个字符。使得对应的所有变量都适用改方法

为Number类型加上一个取整方法

   Number.method("integer",function(){
       return Math[this<0?"ceil":"floor"](this);
     });

为String加上一个去除首尾空白的方法

  String.method("trim",function(){
      return this.replace(/^s+|s+$/g,"");
   })

与类库一起混用是,在确定没有该方法时才添加它

 // 符合条件时才增加方法
 Function.prototype.method = function(name,func) {
   if(!this.prototype[name]) {
     this.prototype[name] = func;
   }
   return this;
 }
递归
直接或者间接地调用自身的一种函数,它把一个问题分解为一组相似的子问题

经典的递归问题汉诺塔。塔上有3根柱子(1,2,3)和一套子直径各不相同的空心圆盘。
开始盘子从小到大的顺序堆叠在1号柱子上,目标是要经过2号柱子全部移动到3号柱子上,中间不允许较大的盘放在较小的盘上;

分解成子问题:

将1号柱子上的从上到下的n-1个盘利用3号柱子移动到2号柱子上。

将1号柱子上最下面的一个盘,直接移动到3号柱子上.

最后把2号柱子上n-1个盘利用递归调用方法,全部移动到3号柱子上。

  var hanoi = function(disc,src,aux,dst) {
    if(disc > 0) {
      hanoi(disc -1,src,dst,aux);
      document.writeln("Move disc "+disc + " from "+src+"to "+dst);
      hanoi(disc-1,aux,src,dst)
    }
  };
  hanoi(3,"Src","Aux","Dst");

  //
  Move disc 1 from Srcto Dst
  Move disc 2 from Srcto Aux
  Move disc 1 from Dstto Aux
  Move disc 3 from Srcto Dst
  Move disc 1 from Auxto Src
  Move disc 2 from Auxto Dst
  Move disc 1 from Srcto Dst

利用递归高效地操作树形结构,比如浏览器端的文档对象模型(DOM),

 var walk_the_DOM = function walk(node,func) {
   func(node);
   node = node.firstChild;
   while(node) {
     walk(node,func);
     node = node.nextSibling;
   }
 }

 // 定义 getElementsByAttribute 函数,
 //它以一个属性名称字符串和一个可选的匹配值作为参数。
 var getElementsByAttribute = function(att,value) {
   var results = [];
   walk_the_DOM(document.body,function(node) {
     var actual = node.nodeType === 1 $$ node.getAttribute(att);
     if(typeof actual === "string" && (actual === value || typeof value != "string")) {
       results.push(node)
     }
  });
  return results;
 }

一些语言提供了尾递归优化。这意味着如果一个函数返回自身递归调用的结果,那么调用的过程会被替换为一个循环,可以显著提高速度。

  //构建一个带尾递归的函数。因为它会返回自身调用的结果
  // 实现 factorial = n*(n-1)(n-2)... 1;
  var factorial = function factorial(i,a) {
    a = a || 1;
    if(i<2) {
      return a;
    }
    return factorial(i-1,a*i);
  };
  document.writeln(factorial(4))   // 4*3*2*1 = 24
作用域
作用域控制着变量与参数的可见性及生命周期

函数作用域: 定义在函数中的参数和变量在函数外部是不可见的,而在一个函数内部任何位置定义的变量,在该函数内部任何地方都可见。

在JavaScript中缺少会计作用域,最好的做法是在函数体的顶部声明函数中可能用到的所有变量。

闭包 Closure
闭包:函数可以访问它被创建时所处的上下文环境。

内部函数可以访问外部函数的实际变量而无需复制

作用域的好处是内部函数可以访问定义它们的外部函数的参数和变量(除了this 和arguments)

内部函数拥有比它的外部函数更长的生命周期。

  var myObject = (function(){
    var value = 0;
    return {
      increment:function(inc) {
        value += typeof inc === "number" ?inc :1;
      },
      getValue:function() {
        return value;       
      }
    }

  })()

上面定义的value变量对increment和getValue方法总是可用的,但函数的作用域使得它对其他的程序来说是不可见的

回调 Callbacks
异步请求,提供一个服务器的响应到达是随即触发的回调函数,
异步函数立即返回。
模块 Module
使用 函数 和 闭包创造模块

模板的一般形式:

一个定义了私有变量和函数的函数

利用闭包创建可以访问私有变量和函数的特权函数

返回这个特权函数,或者把它们保存到一个可访问到的地方。

利用前面使用的method方法,为String 增加一个deentityify 方法,寻找字符串中的HTML字符实体并把它们替换为对应的字符。

  String.method("deentityify",function(){
     // 字符实体表,它映射字符实体的名字到对应的字符
     var entity = {
       quot: """,
       lt:"<",
       gt:">"
     };
    // 返回 deetityify 方法
    return function() {
      // 下面就是deetityify方法,
      return this.replace(/&([^&;]+);/g,
          function(a,b){
            var r = entity[b];
            return typeof r === "string"?r:a;
          }
      )
    }   
  }());

最后,使用()运算符立刻调用我们刚刚构造出来的函数,这个调用所创建并返回的函数才是deentityify方法

  document.writeln("<">".deentityify())    // <">
级联
让执行后的函数返回this 而不是 undefined,就可以启用级联

一个级联中就,我们可以在多带带一条语句中依次调用同一个对象的很多方法。一个启用了Ajax类库可能允许我们以这样的形式去编码

  getElement("myBoxDiv")
     .move(300,150)
     .width(100)
     .height(200)
     ...
     .tip("This box is resizeable")

上面的例子中,每次调用返回的结果都会被下一次调用所用。

柯里化
柯里化允许我们把函数与传递给它的参数相结合,产生一个新的函数
  Function.method("curry",function(){
    var slice = Array.prototype.slice,
        args = slice.apply(arguments),
        that = this;
    return function() {
      return that.apply(null,args.concat(slice.apply(arguments)))
    }
    })

 function add (a,b) {return a+b}
 add.curry(1)(6) //7
记忆
函数可以将先前操作的结果记录在某个对象里,从而避免无谓的重复运算,这种优化被称为记忆

以Fibonacci数列为例,一个Fibonacci数字是之前两个Fibonacci数字之和。最前面的两个数字时0和1

 var fibonacci = function (n) {
   return n<2? n:fibonacci(n-1) + fibonacci(n-2)
 }
 for(var i =0 ;i< = 10;i+=1) {
   document.writeln("//"+i+": "+fibonacci(i))
 }

 //0: 0
 //1: 1
 //2: 1
 //3: 2
 //4: 3
 //5: 5
 //6: 8
 //7: 13
 //8: 21
 //9: 34
 //10: 55

改进,利用memo数组保存我们的存储结果,存储结果可以隐藏在闭包中。

  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;
  }();
  fibonacci(10)  //55

推而广之,编写一个函数来帮助我们构造带记忆功能的函数。

  var memoizer = function(memo,formula) {
    var recur = function(n) {
      var result = memo[n];
      if(typeof result !== "number") {
        result = formula(recur,n);
        memo[n] = result;
      }
      return result;
    };
    return recur;
  }
  var fibonacci = memoizer([0,1],function(recur,n) {
      return recur(n-1) + recur(n-2);
    });
  fibonacci(10)  // 55

《JavaScript 语言精粹》读书笔记 - 函数

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/93238.html

相关文章

  • JavaScript 语言精粹读书笔记 - 函数

    摘要:语言精粹读书笔记第四章函数函数字面量函数字面量包含个部分第一部分,保留字第二部分,函数名,它可以被忽略。这个超级延迟绑定使得函数对高度复用。构造器调用模式一个函数,如果创建的目的就是希望结合的前缀来调用,那它就被称为构造器构造。 《JavaScript 语言精粹》 读书笔记 第四章 函数 Functions 函数字面量 函数字面量包含4个部分: 第一部分, 保留字 function...

    wdzgege 评论0 收藏0
  • 读书笔记-1【javascript语言精粹】继承

    摘要:使用构造器有个严重的危害,如果在调用构造器函数的时候忘记使用前缀,不仅不会绑定到新对象,还会污染全局变量原型模式原型模式中,我们采用对象来继承。 构造器调用模式 当一个函数对象被创建时,Function构造器会运行类似这样的代码: this.prototype = {constructor: this} new一个函数事会发生: Function.method(new, functio...

    malakashi 评论0 收藏0
  • JavaScript语言精粹 修订版》 读书笔记

    摘要:于是我就先把这本薄的经典书语言精粹修订版豆瓣读书本书简介总共章,除去附录,才页,读完并记录了一些笔记。读书笔记还可以分享给别人看。编程语言第版定义了的标准。程序检查时丢弃值为函数的属性。 之前看到这篇文章,前端网老姚浅谈:怎么学JavaScript?,说到怎么学习JavaScript,那就是看书、分析源码。10本书读2遍的好处,应该大于一本书读20遍。看书主动学习,看视频是被动学习。看...

    EscapedDog 评论0 收藏0
  • 读书笔记:编写高质量javascript的68个方法

    摘要:第条尽量少使用全局对象避免声明全局变量尽量声明局部变量避免对全局变量增加属性第条始终声明局部变量第条避免使用语句第条熟练使用闭包的函数值包含了比调用他们时执行所需要的代码还要更多的信息。那些在其所涵盖的作用域内跟踪变量的函数称为闭包。 书还没看完。一遍看,一遍写读书笔记。 这本书的序是JavaScript之父Brendan Eich写的,作者是JavaScript标准化委员会专家。可想...

    Vicky 评论0 收藏0
  • 【阅读笔记javascript 语言精粹

    摘要:前言由于最近的项目用到了一些的代码,所以我带着好奇心,认真阅读了这本书,粗略地了解语言的基本结构和特性,对于一些不熟悉的新概念,以记录的形式加强印象,也是对学习的反思总结。 前言 由于最近的项目用到了一些js的代码,所以我带着好奇心,认真阅读了这本书,粗略地了解js语言的基本结构和特性,对于一些不熟悉的新概念,以记录的形式加强印象,也是对学习的反思总结。 一、字面量(literals...

    tangr206 评论0 收藏0

发表评论

0条评论

lufficc

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<