js学习笔记:引用类型——Function

    xiaoxiao2022-06-29  46

    ECMAScript中的函数实际上是对象。每个函数都是Function类型的实例,而且都与其他引用类型一样具有属性和方法。 由于函数是对象,因此函数名实际上也是一个指向函数对象的指针,不会与某个函数绑定。

    定义函数

    函数声明

    function sum(num1,num2){ return num1+num2; }

    函数表达式

    var sum = function(num1,num2){ return num1+num2; };

    函数声明与函数表达式的区别

    解析器会率先读取函数声明,并使其在执行任何代码之前可用(函数声明提升)而对于函数表达式,则必须等到解析器执行到他所在的代码行才会真正被执行解释。 sum(10,10); //完全可以正常运行 function sum(num1,num2){ return num1+num2; }

    上段代码之所以可以运行,是因为在代码开始执行之前,解析器就已经通过一个名为函数声明提升的过程,读取并将函数声明添加到执行环境中。所以即使声明函数的代码在调用它的代码后面,js引擎也能把函数声明提升到顶部。

    sum(10,10); //产生错误 var sum = function (num1,num2){ return num1+num2; }

    上段代码之所以会产生错误,原因在于函数位于一个初始化语句中,而不是一个函数声明,在执行到函数所在语句之前,变量sum中不会保存有对函数的引用。

    使用Function构造函数

    构造函数可以接收任意数量的参数,但最后一个参数始终被看成是函数体,而前面的参数则枚举出了新函数的参数。

    var sum = new Function("num1","num2","return num1+num2");

    不推荐这种方法定义函数,因为这种方法回到直接洗两次代码(第一次解析常规ECMAScript代码,第二次是解析传入构造函数中的字符串),从而影响性能。

    由于函数名仅仅是指向函数的指针,因此函数名与包含对象指针的其他变量没什么不同。

    function sum(num1,num2){ return num1+num2; } sum(10,10); //20 //anotherSum和sum指向同一函数 var anotherSum = sum; anotherSum(10,10); //20 sum = null; anotherSum(10,10); //20

    使用不带圆括号的函数名是访问指针,而非调用函数。

    返回

    当一个函数被调用时,它从第一个语句开始执行,并在遇到关闭函数体的}时结束,然后函数把控制权交还给调用该函数的程序。

    return语句可用来使函数提前返回。当return被执行时,函数立即返回而不再执行余下的语句。

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

    如果函数调用时在前面加上了new前缀,且返回值不是一个对象,则返回该新对象(this)。

    没有重载

    之前曾经有个例子:

    function addSomeNumber(num){ return num+=100; } function addSomeNumber(num){ return num+=200; } var result = addSomeNumber(100); //300

    结果是后面的函数覆盖了前面的函数。以上代码其实和以下代码没什么区别:

    var addSomeNumber = function(num){ return num+=100; } addSomeNumber= function(num){ return num+=200; } var result = addSomeNumber(100); //300

    观察重写之后的代码,很容易看清楚在创建第二个函数时,实际上覆盖了引用第一个函数的变量。


    作为值的函数

    因为ECMAScript中的函数名本身就是变量,所以函数也可以作为值来使用。

    可以像传递参数一样把一个函数传递给另一个函数可以将一个函数作为另一个函数的结果返回。 function callSomeFunction(someFunction,someArgument){ return someFunction(someArgument); } fucntion add10(num){ return num+10; } var result = callSomeFunction(add10,10); //20

    要访问函数的指针而不执行函数的话,必须去掉函数名后面的圆括号。因此上面例子中传递给callSomeFunction的是add10而不是执行它们之后的结果。

    函数内部属性

    arguments

    是一个类数组对象,包含着传入函数中的所有参数。

    callee属性:是一个指针,指向拥有这个arguments对象的函数。 //经典的阶乘函数 function factorial(num){ if(num<=1){ return 1; }else{ return num*factorial(num-1); } }

    这个函数的执行与函数名紧紧耦合在了一起,为了消除这种耦合现象,可以使用arguments.callee:

    function factorial(num){ if(num<=1){ return 1; }else{ return num*arguments.callee(num-1); } }

    this

    是函数据以执行的环境对象。

    window.color = "red"; var o = {color"blue"}; fucntion sayColor(){ alert(this.color); } sayColor(); //red o.sayColor = sayColor; o.sayColor(); //blue

    函数的名字仅仅是一个包含指针的变量而已,因此,即使在不同的环境中执行,全局的sayColor和o.sayColor 指向的仍然是同一个函数。

    caller

    保存着调用当前函数的函数的引用,如果是在全局作用域中调用当前函数,它的值为null。

    function outer(){ inner(); } function inner(){ alert(inner.caller); } outer();

    alert警告框中最后会显示outer函数的源代码。因为outer调用了inner,所以inner.caller就指向outer()。 为了实现更松散的耦合,也可以通过arguments.callee.caller来访问相同的信息。

    function outer(){ inner(); } function inner(){ alert(arguments.callee.caller); } outer();

    当在严格模式下运行时,访问arguments.callee会导致错误。 并且严格模式下,不能为函数的caller属性赋值。


    函数的属性和方法

    length属性

    表示函数希望接受的命名参数的个数。

    fucntion sayName(name){ alert(name); } function sum(num1,num2){ return num1+num2; } function sayHi(){ alert("hi"); } sayName.length; //1 sum.length; //2 sayHi.length; //0

    apply()和call()

    这两个方法的用途都是在特定的作用域中调用函数,实际上等于设置函数体内this对象的值。

    apply接收两个参数: 在其中运行函数的作用域参数数组(可以是Array的实例,也可以是arguments对象) function sum(num1,num2){ return num1+num2; } function callSum1(num1,num2){ return sum.apply(this,arguments); //传入arguments对象 } function callSum2(num1,num2){ return sum.apply(this,[num1,num2]); //传入数组 } alert(callSum1(10,10)); //20 alert(callSum2(10,10)); //20

    上面的代码中,callSum1在执行sum函数时传入了this(因为是在全局作用域中调用的,所以传入的就是window对象)。

    call方法和apply方法的作用相同,区别仅在于接收参数的方式不同。 call方法将参数直接传递给函数而不是以数组的形式,换句话说,call方法传递给函数的参数必须逐个列举出来。 function sum(num1,num2){ return num1+num2; } function callSum(num1,num2){ return sum.call(this,num1,num2); }

    call方法和apply方法的结果并没有什么不同,决定使用哪个完全取决于采取哪种给函数传递参数的方式最方便。如果打算直接传入arguments对象或者包含函数中先接收到的也是一个数组,那么使用apply方法肯定更方便,否则可能就会选择call方法。

    call和apply最大的用武之地是能够扩充函数赖以运行的作用域:

    window.color = "red"; var o = {color"blue"}; fucntion sayColor(){ alert(this.color); } sayColor(); //red sayColor.call(this); //red sayColor.call(window); //red sayColor.call(o); //blue

    在调用sayColor.call(o)时函数的执行环境改变了,函数内部的this对象指向了o。

    使用apply和call来扩充作用域的最大好处是,对象不需要与方法有任何耦合关系。

    bind( )

    这个方法会创建一个函数的实例,其this值会被绑定到传给bind函数的值:

    window.color = "red"; var o = {color"blue"}; fucntion sayColor(){ alert(this.color); } var objectSayColor = sayColor.bind(o); objectSayColor(); //blue

    这里,sayColor调用bind并传入o,创建了objectSayColor 函数,objectSayColor 函数的this值等于o,因此即使在全局作用域中调用这个函数,也会看到blue

    兼容到ie9,polyfill:

    Function.prototype.bind = function(context){ var args = [].slice.apply(arguments,1); var func = this; return function(){ return func.apply(context,args.concat([].slice.apply(arguments))); } }

    继承的方法

    toLocaleString、toString和valueOf都返回函数的代码。

    转载请注明原文地址: https://ju.6miu.com/read-1125319.html

    最新回复(0)