继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

一起来啃《JavaScript语言精粹》----函数

holdtom
关注TA
已关注
手记 1845
粉丝 240
获赞 991

一、调用

  • <strong>方法调用模式</strong>
    当一个函数被保存为对象的一个属性时,我们称它为一个方法。如果调用表达式包含一个提取属性的动作,那么它就是被当做一个方法来调用。

496

Paste_Image.png


方法可以使用this访问自己所属的对象,通过this可取得它们所属对象的上下文的方法称为公共方法。

  • <strong>函数调用模式</strong>
    当一个函数并非一个对象的属性时,那么它就是被当做一个函数来调用:
    var sum = add(3, 4);//7
    以此模式调用函数时,this被绑定到全局对象。


    497

    Paste_Image.png


    这事语言设计的一个错误,错误后果就是方法不能利用内部函数来帮助它工作,因为内部函数的this被绑定了错误的值,所以不能共享该方法对对象的访问权。
    <b>解决方案:</b>
    为该方法定义一个变量并给它赋值为this,那么内部函数就可以通过那个变量访问到this:


    416

    Paste_Image.png

  • <strong>构造器调用模式</strong>
    如果在一个函数前面带上 new 来调用,那么背地里将会创建一个连接到该函数的prototype成员的新对象,同时 this 会被绑定到那个新对象上。
    //创建一个名为Quo的构造器函数。它构造一个带有status属性的对象。
    var Quo = function(string){
    this.status = string
    }
    //给Quo的所有实例提供一个名为get_status的公共方法。
    Quo.prototype.get_status = function(){
    return this.status;
    };
    //构造一个Quo实例。
    var myQuo = new Quo('confused');
    myQuo.get_status()//'confused'

  • <strong>Apply调用模式</strong>
    apply方法让我们构建一个参数数组传递给调用函数。它也允许我们选择this的值。apply方法接收两个参数,第一个是要绑定给this的值,第二个就是一个参数数组。

631

Paste_Image.png

二、扩充类型的功能

  • 我们可以通过给Function.prototype 增加方法来使得该方法对所有函数可用


    388

    Paste_Image.png


    通过给Function.prototype 增加一个method方法,我们下次给对象增加方法的时候就不必键入prototype这几个字符,省掉了一点麻烦。

  • JavaScript没有专门的整数类型,但有时候确实只需要提取数字中的整数部分。JavaScript本身提供的取整方法有些丑陋。我们可以通过Number.prototype增加一个integer方法来改善它。它会根据数字的正负来判断是使用Math.ceiling还是Math.floor。


    409

    Paste_Image.png

  • PS:这里需要指出的是Javascript的原型继承机制,可以形象的理解为Object是祖师爷爷,Function既当爹又当妈,剩下的Number,String,Array等都是儿女:
    Number,String,Array => Function => Object

523

Paste_Image.png

  • 另外,基本类型的原型是公用结构,所以在类库混用时务必小心。一个保险的做法是只在确定没有该方法时才添加它。
    //符合条件时才增加方法。
    Function.prototype.method = function(name,func){
    if(!this.prototype[name]){
    this.prototype[name] = func;
    }
    return this;
    }

三、闭包

  • 我们通过调用一个函数的形式去初始化myObject,该函数会返回一个对象字面量。函数里定义一个value变量。该变量对increment和getValue方法总是可用的,但是函数的作用域使得它对其他的程序来说是不可见的。


    545

    Paste_Image.png


    这个例子我们并没有把一个函数赋值给myObject。我们是把调用该函数后返回的结果赋值给它。

611

Paste_Image.png


当我们调用quo时,它返回包含get_status方法的一个新对象。该对象的一个引用保存在myQuo中。即使quo已经返回了,但get_status方法仍然享有访问quo对象的status属性的特权。get_status方法并不是访问该参数的一个副本,它访问的就是该参数本身。这是可能的,因为该函数可以访问它被创建时所处的上下文环境,这被称为<b>闭包</b>。

  • 来看一个更有用的例子


    700

    Paste_Image.png


    1.我们调用fade,把document.body作为参数传递给它,fade函数设置level为1.它定义一个step函数,接着调用setTimeout,并传递step函数和一个时间(100毫秒)给它。然后它返回,fade函数结束。
    2.在大约100毫秒后,step函数被调用。它把fade函数的level变量转换为16位字符。接着,它修改fade函数得到的节点的背景颜色。然后查看fade函数的level变量。如果背景色尚未变成白色,那么它增大fade函数的level变量,接着用setTimeout预定让它自己再次运行。
    3.step函数很快再次被调用。但这次,fade函数的level变量值变成2.fade函数在之前已经返回了,但只要fade的内部函数需要,它的变量就会持续保留。

  • <b>从一个经典的错误说起</b>
    构造六个div,当点击一个div时,按照预期,应该打出相应div的值,但是它总是会显示div的数量。因为事件处理器函数绑定了变量i本身,而不是函数在构造时的变量i的值,i始终无法被释放。

700

Paste_Image.png


避免在循环中创建函数,它可能只会带来无谓的计算,还会引起混淆,正如上面那个经典的错误。我们可以先在循环外创建一个辅助函数,让这个辅助函数再返回一个绑定了当前i值的函数,这样就不会导致混淆了。改良后的例子,用正确的方式给一个数组中的节点设置事件处理程序


700

Paste_Image.png


另一种解决办法,这里我们用一个立即执行函数给它包住,我们不再依赖i,而是用另外一个变量n把它保留下来。

400

Paste_Image.png

四、记忆

  • 函数可以将先前操作的结果记录在某个对象里,从而避免无谓的重复运算。这种优化被称为<b>记忆</b>。JavaScript的对象和数组要实现这种优化是非常方便的。
    比如说,我们想要一个递归函数来计算Fibonacci数列。一个Fibonacci数列是之前两个Fibonacci数字之和。最前面的两个数字是0和1。


    603

    Paste_Image.png


    但是这样做了很多无谓的工作。fibonacci函数被调用了453次。如果我们让该函数具备<b>记忆</b>功能,就能显著地减少运算量。
    我们在一个名为memo的数组里保存我们的存储结果,存储结果可以隐藏在闭包中。当函数被调用时,这个函数首先检查结果是不是已经存在,如果存在,就立即返回这个结果。


    444

    Paste_Image.png


    这个函数返回的结果相同,但是它只被调用了29次。

  • 我们可以编写一个函数来帮助我们构造带<b>记忆</b>功能的函数。memoizer函数取得一个初始的memo数组和formula函数。它返回一个管理meno存储和在需要时调用formula函数的recur函数。我们把这个recur函数和它的参数传递给formula函数:


    485

    Paste_Image.png


    现在,我们可以使用memoizer函数来定义fibonacci函数,提供其初始的memo数组和formula函数:


    565

    Paste_Image.png


    通过设计这种产生另一个函数的函数,极大地减少了我们的工作量。例如,要产生一个可以记忆的阶乘函数,我们只需提供基本的阶乘公式即可:

    534

    Paste_Image.png



作者:韩宝亿
链接:https://www.jianshu.com/p/38fed5d60cb7


打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP