美文网首页
JS权威指南读书笔记(二)

JS权威指南读书笔记(二)

作者: 坤少卡卡 | 来源:发表于2017-09-13 22:43 被阅读0次

    第八章 函数

    函数的实参和形参

    当调用函数的时候传入的实参比函数声明时指定的形参个数要少,剩下的形参将设置为undefined值。所以比较好的做法是赋予一个默认值。在ES5中可以通过短路运算赋予一个默认值,比如a = a || [],当然这个a是一个形参。在ES6中的做法是直接给函数参数声明默认值:

    let a = function (page = 1,pageLimit = 10) {
      //
    }
    

    当调用函数的时候传入实参的个数超过定义时的形参个数时,没有办法直接获得未命名值得引用。可以使用arguments来获得所有传入的参数。因为实参对象是一个类数组对象,可以通过下标访问传入函数的实参。在ES6中可以使用剩余参数...来获取所有的参数。

    作为值的函数

    在JS中,函数不仅是一种语法,也是值。可以将函数赋值给变量,存储在对象的属性或数组的元素中,作为参数传入另一个函数。
    function square(x) {return x * x;}
    这个定义创建一个新的函数对象,并将其赋值给变量square。函数的名字实际上是看不见的,square仅仅是变量的名字,这个变量指代函数对象。函数还可以赋值给其他的变量,并正常工作:

    var s = square;   // s和square指代同一个函数
    square(4);       //  => 16
    s(4);            //  => 16
    

    函数同样可以赋值给对象的属性。当函数作为对象的属性调用时,函数就称为方法。
    函数甚至不需要名字,当把他们赋值给数组元素时:

    var a = [function(x) {return x * x},20];
    a[0](a[1]);   //  => 400
    

    上式虽然看起来怪怪的,但是是合法的函数调用表达式!

    自定义函数属性

    如果需要写一个依赖函数自身返回值的函数,最好的办法就是给函数对象添加一个属性,而不是把这个信息放到全局变量中去污染环境。举个例子:

    function sum () {
        return sum.counter++;//  先返回计数器的值,然后计数器自增1
    }
    sum.counter = 0;
    

    闭包

    JS采用词法作用域,也就是说,函数的执行依赖于变量作用域,这个作用域是在函数定义时决定的,而不是函数调用时决定的。为了实现这种词法作用域,JS函数对象的内部状态不仅包含函数的代码逻辑,还必须引用当前的作用域链。函数对象可以通过作用域链相互关联起来,函数体内部的变量都可以保存在函数作用域内,这种特性在计算机科学中称为“闭包”
    来看一个官方示例:

    var scope = "global scope";  //全局变量
    function checkscope() {
        var scope = "local scope";  //局部变量
        function f() {return scope;}  //返回scope
        return f;
    }
    checkscope()()  //  返回???
    

    回想一下词法作用域的基本规则:JS函数的执行用到了作用域链,这个作用域链是函数定义的时候创建的。嵌套的函数f()定义在这个作用域里,其中的变量scope一定是局部变量,不管在何时何地执行函数f()。所以返回local scope,而不是global scope

    函数属性、方法和构造函数

    bind()方法

    这个方法的作用就是将函数绑定至某个对象。例如:

    function f(y) {return this.x + y}
    var o = {x : 1};
    var g = f.bind(o); //通过调用g(x)来调用o.f(x)
    g(2)  // => 3
    

    不仅如此,还附带一些其他应用:除了第一个实参之外,传入bind()的实参也会绑定至this.举个例子:

    var sum = function(x,y) {return x + y};
    var succ = sum.bind(null,1);
    // this的值绑定到null,并且第一个参数绑定到1
    succ(2) // 3;x绑定到1,并传入2作为实参y
    
    function f(y,z) {return this.x + y + z};
    var g = f.bind({x:1},2);
    g(3) // 6;this.x绑定到1,y绑定到2,z绑定到3
    

    Function()构造函数

    Function()构造函数可以传入任意数量的字符串实参,最后一个实参表示的文本就是函数体;它可以包含任意的JS语句,每两条语句之间用分号分隔。如果不包含任何参数,只需传入函数体字符串即可。

    注意:Function()构造函数不需要通过传入实参指定函数名,也就是创建的是一个匿名函数。

    关于Function()构造函数有几点需要注意:

    • Function()构造函数允许JS在运行时动态地创建并编译函数。
    • 每次调用Function()构造函数都会解析函数体,并创建新的函数对象。如果是在一个循环或者多次调用的函数中执行这个构造函数,效率会受影响。而循环中的嵌套函数和函数定义表达式不会每次执行都重新编译。
    • Function()构造函数所创建的函数并不是使用词法作用域,函数体代码的编译总是会在顶层函数执行。 如下所示:
    var scope = "global";
    function cons() {
        var scope = 'local';
        return new Function("return scope");
        //Function()构造函数只传入一个字符串表示该匿名函数没有参数,只有返回体。
        //上面说到并不是使用词法作用域,所以此时会去顶层函数所处作用域的变量scope,所以结果返回“global”
    }
    

    可以将Function()构造函数认为是在全局作用域执行的eval()。eval()可以在自己的私有作用域内定义新变量和函数。在很多JS编码规范里是不建议使用eval()和Function构造函数。具体可参考Airbnb的编码规范

    函数式编程

    高阶函数

    所谓高阶函数就是操作函数的函数,它接收一个或多个函数作为参数,并返回一个新函数。举个🌰:

    function mapper(f) {
        return function(a) {return map(a,f);};
    }
    var increment = function(x) {return x + 1};
    var incrementer = mapper(increment);
    incrementer([1,2,3]) //  [2,3,4]
    
    // 调用mapper会传入一个map()方法里的回调函数,然后会返回一个函数,这个函数的参数就是需要处理的数组。
    //可以理解为mapper(increment)([1,2,3])
    

    相关文章

      网友评论

          本文标题:JS权威指南读书笔记(二)

          本文链接:https://www.haomeiwen.com/subject/iyglsxtx.html