《JavaScript权威笔记(第六版)》笔记0x5 函数

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/funkstill/article/details/88420548

    在JavaScript里,函数即对象,程序可以随意操控它们。比如, JavaScript可以把函数赋值给变量,或者作为参数传递给其他函数。因为函数就是对象,所以可以给它们设置属性,甚至调用它们的方法。

    JavaScript的函数可以嵌套在其他函数中定义,这样它们就可以访问它们被定义时所处的作用域中的任何变量。这意味着JavaScript函数构成了一个闭包(closure),它给JavaScript带来了非常强劲的编程能力。

函数调用

    函数调用

printprops({x:1});
var total=distance(0,0,2,1)+distance(2,1,3,5);
var probability=factorial(5)/factorial(13);

    对于普通的函数调用,函数的返回值成为调用表达式的值。如果该函数返回是因为解释器到达结尾,返回值就是undefined。如果函数返回是因为解释器执行到一条return语句,返回值就是return之后的表达式的值,如果return语句没有值,则返回undefined。

    方法调用

    对方法调用的参数和返回值的处理,和上面所描述的普通函数调用完全一致。但是,方法调用和函数调用有一个重要的区别,即:调用上下文。属性访问表达式由两部分组成:一个对象和属性名称。在像这样的方法调用表达式里,对象o成为调用上下文,函数体可以使用关键字this引用该对象。

var calculator={
    operand1:1,
    operand2:1,
    add:function(){
        this.result = this.operand1+this.operand2;
    }
};
calculator.add();
calculator.result//=>2

    构造函数调用

    如果函数或者方法调用之前带有关键字new,它就构成构造函数调用。

    如果构造函数调用在圆括号内包含一组实参列表,先计算这些实参表达式,然后传入函数内,这和函数调用和方法调用是一致的。但如果构造函数没有形参, JavaScript构造函数调用的语法是允许省略实参列表和圆括号的。凡是没有形参的构造函数调用都可以省略圆括号:

var o=new Object();
var o=new Object;

    构造函数调用创建一个新的空对象,这个对象继承自构造函数的prototype属性。构造函数试图初始化这个新创建的对象,并将这个对象用做其调用上下文,因此构造函数可以使用this关键字来引用这个新创建的对象。注意,尽管构造函数看起来像一个方法调用,它依然会使用这个新对象作为调用上下文。也就是说,在表达式new o.m()中,调用上下文并不是o。
    构造函数通常不使用return关键字,它们通常初始化新对象,当构造函数的函数体执行完毕时,它会显式返回。在这种情况下,构造函数调用表达式的计算结果就是这个新对象的值。然而如果构造函数显式地使用return语句返回一个对象,那么调用表达式的值就是这个对象。如果构造函数使用return语句但没有指定返回值,或者返回一个原始值,那么这时将忽略返回值,同时使用这个新对象作为调用结果。

    间接调用

    JavaScript中的函数也是对象,和其他JavaScript对象没什么两样,函数对象也可以包含方法。其中的两个方法call()和apply()可以用来间接地调用函数。两个方法都允许显式指定调用所需的this值,也就是说,任何函数可以作为任何对象的方法来调用,哪怕这个函数不是那个对象的方法。两个方法都可以指定调用的实参。 call()方法使用它自有的实参列表作为函数的实参, apply()方法则要求以数组的形式传入参数。

函数参数

    将对象属性做实参

    当一个函数包含超过三个形参时,对于程序员来说,要记住调用函数中实参的正确顺序实在让人头疼。每次调用这个函数时都要不厌其烦地查阅文档,为了不让程序员每次都翻阅手册这么麻烦,最好通过名/值对的形式来传入参数,这样参数的顺序就无关紧要了。为了实现这种风格的方法调用,定义函数的时候,传入的实参都写入一个单独的对象之中,在调用的时候传入一个对象,对象中的名/值对是真正需要的实参数据。

作为命名空间的函数

    在函数中声明的变量在整个函数体内都是可见的(包括在嵌套的函数中),在函数的外部是不可见的。不在任何函数内声明的变量是全局变量,在整个JavaScript程序中都是可见的。在JavaScript中是无法声明只在一个代码块内可见的变量的,基于这个原因,我们常常简单地定义一个函数用做临时的命名空间,在这个命名空间内定义的变量都不会污染到全局命名空间。

function mymodule(){//模块代码
    //这个模块所使用的所有变量都是局部变量
    //而不是污染全局命名空间
}
mymodule();//不要忘了还要调用这个函数

     这段代码仅仅定义了一个单独的全局变量:名叫"mymodule"的函数。这样还是太麻烦,可以直接定义一个匿名函数,并在单个表达式中调用它:

(function(){//mymodule()函数重写为匿名的函数表达式
//模块代码
}());//结束函数定义并立即调用它

闭包

    函数对象可以通过作用域链相互关联起来,函数体内部的变量都可以保存在函数作用域内,这种特性在计算机科学文献中称为“闭包”。

    词法作用域规则

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

    实现闭包

var uniqueInteger=(function(){
    var counter=0;//函数的私有状态
    return function(){return counter++;};
}());

函数式编程

    使用函数处理数组

var sum=function(x,y){return x+y;};
var square=function(x){return x*x;};
var data=[1,1,3,5,5];
var mean=data.reduce(sum)/data.length;
var deviations=data.map(function(x){return x-mean;});
var stddev=Math.sqrt(deviations.map(square).reduce(sum)/(data.length-1));

    高阶函数

    高阶函数(higher-order function)就是操作函数的函数,它接收一个或多个函数作为参数,并返回一个新函数。

//这个高阶函数返回一个新函数,这个函数将它的实参传入f()
//并返回f的返回值的逻辑非
function not(f){
    return function(){//返回一个新函数
        var result=f.apply(this,arguments);//调用f
        return !result;//对结果求反
    };
}
var even=function(x){//判断奇偶的函数
    return x%2===0;
};
var odd=not(even);
[1,1,3,5,5].every(odd);//=>每个元素都是奇数

     记忆

//返回f()的带有记忆功能的版本
//只有当f()的实参的字符串表示都不相同时它才会工作
function memorize(f){
    var cache={};//将值保存在闭包内
    return function(){//将实参转换为字符串形式,并将其用作缓存的键
        var key=arguments.length+Array.prototype.join.call(arguments,",");
        if(key in cache)return cache[key];
        else return cache[key]=f.apply(this,arguments);
    };
}
/*
memorize()函数创建一个新的对象,这个对象被当做缓存(的宿主)并赋值给一个局部变量,
因此对于返回的函数来说它是私有的(在闭包中)。所返回的函数将它的实参数组转换成字
符串,并将字符串用做缓存对象的属性名。如果在缓存中存在这个值,则直接返回它。
否则,就调用既定的函数对实参进行计算,将计算结果缓存起来并返回
*/
//返回两个整数的最大公约数
function gcd(a,b){
    var t;
    if(a<b)t=b,b=a,a=t;//确保a>=back
    while(b!=0)t=b,b=a%b,a=t;//欧几里得算法求公约数
    return a;
}
var gcdmeno=memorize(gcd);
gcdmeno(85,187);//=>17//当写一个递归函数时,往往需要实现记忆功能
var factorial=memorize(function(n){
    return (n<=1)?1:n*factorial(n-1);
});
factorial(5)//=>120

猜你喜欢

转载自blog.csdn.net/funkstill/article/details/88420548