zoukankan      html  css  js  c++  java
  • 小红书第五章——引用类型之function类型

    有趣的函数——function类型

    函数实际上是对象,每个函数都是function类型的实例,具有属性和方法。

    1、函数的定义方法

    1)函数声明语法

    function sum(num1,num2){//和函数表达式相比,它还有一个很好玩的特性,下面会讲到哦
       return num1+num2;
    }

    2)函数表达式

    var sum=function(sum1,sum2){return sum1+sum2;};//注意,后面有分号哦

    3)函数构造函数 

    var sum=new Function("num1","num2","return num1+num2");//不推荐

    不建议使用构造函数方法定义函数,这种方法会导致解析两次代码:第一次是解析常规ECMAscript代码,第二次是解析传入构造函数中的字符串,从而影响性能。

    2、函数名

    由于函数是对象,函数名实际上是一个指向函数对象的指针,不会和某个函数绑定。也就是说一个函数对象可能有多个名字。

    function sum(num1,num2){
        return num1+num2;
    }
    var  sum1=sum;
    alert(sum1(10,10));//20
    sum=null;
    alert(sum1(10,10));//20

    如上面的代码所示,将sum置为null后,sum1依然可以调用该函数对象。

    3、没有重载

    声明两个同名函数时,后面的函数会覆盖前面的函数。

    function sum(num1,num2){
        return num1+num2;
    }
    function sum(num1,num2){
        return (num1+num2-1);
    }
    alert(sum(10,10))//9

    实际上,上面的代码和下面的代码没什么区别。

    function sum(num1,num2){
        return num1+num2;
    }
    sum=function(num1,num2){
        return (num1+num2-1);
    }
    alert(sum(10,10))//9

    4、函数声明与函数表达式

    首先观察以下这两种代码

    alert(sum(1));//2
    function sum(num1){
    return num1+1;
    }
    alert(sum(1));//unexpected identifier在这一行发生错误
    var sum=function(num1){
    return num1+1;
    };

    发现了吧,解析器在向执行环境加载数据时,对函数 声明和函数表达式的处理并不相同,解析器会率先读取函数声明,并使其在执行任何代码之前可用,而函数表达式则必须等到解释器执行到它所在的代码行,才会真正被解释执行。

    那,这又是为什么呢?

    解释:在代码开始执行前,解析器就已经通过一个名叫函数声明提升的过程,读取并将函数声明添加到执行环境中。对代码求值时,js引擎会在第一遍声明函数并将其放在源代码树的顶部,这样,即使声明函数的代码在调用它的代码之后,JS引擎也能将函数声明提升到顶部。

    5、作为值的函数

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

    比如下面

    function callsomeFunction(someFunction,someArgument){
       return someFunction(someFUnction,someArgument);
    }

    注意:上面的例子传给callcomeFunction的是someFunction和someArgument而不是执行后得结果

    应用:

    从一个函数中返回另一个函数,可以分情况在本函数中设置参数,有技巧的执行被嵌套的函数

    function createCompareFuc(propertyName){
        return function(obj1,obj2){
                      var value1=obj[propertyName];
                      var value2=obj[propertyName];
                       if(value1<value2){return -1;}
                       else if(value1>value2){return 1;}
                       else {return 0;}
                   };
    }

    如上面的代码,可以很方便的在一个函数中调用比较函数

    6、函数内部属性

    1)、callee

    函数内部有两个特殊的对象:arguments和this。其中arguments有一个有趣的属性:callee,该属性是一个指针,指向拥有这个argumenets对象的函数。

    callee属性主要用于消除函数名和函数的耦合。毕竟函数名只是函数对象的一个指针,如果在函数中直接用函数名的话,一旦函数名改变,这个函数对象的调用会产生错误

    如下面这个阶乘函数

    function fac(num){
        if(num<=1){return 1;}
       else {return num*fac(num-1);}
    }

    函数的执行与函数紧紧耦合,一旦函数名发生改变,则函数不可用。

    这样改比较好

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

    2)、this

    this,函数内部的另一个特殊对象,this引用的是函数据以执行的环境对象——也可以说是this值(全局环境下是window)

    看看下面的例子:

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

    如上所示,sayColor()是在全局环境中定义并引用了this,由于this指向函数据以执行的环境对象,故在这个函数执行前,无法确定this的值。

    当在全局环境下调用sayColor时,this引用的是全局对象window,换句话说,对this的求值会转变为对window.color的求值。而把这个函数赋给了对象o之后,该函数中this.color变成了o.color;

    关于this:阮一峰大神有一篇博客讲的很透彻,链接看这里:http://www.ruanyifeng.com/blog/2018/06/javascript-this.html

    3)、caller

    ECMAscript5规范化的另一个函数对象的属性,除了Opera的早期版本不支持,其他浏览器都支持这个函数属性。这个属性中保存着调用当前函数的函数的引用,如果是在全局作用域中调用当前函数,它的值为null。例如:

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

    7、函数属性和方法

    ECMAscript的prototype属性,就ECMAscript的引用类型来说,prototype是保存他们所有实例方法的真正所在,换句话说,诸如toString()、和valueOf()都保存在prototype名下,只不过是通过各自对象的实例访问罢了。在ECMAscript5中,prototype的属性不可枚举,因此使用for-in无法发现。

    每个函数都包含两个非继承而来的方法:apply()和call()

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

    1)、apply()

    apply()接收两个参数:一个是在其中运行函数的作用域,另一个是参数数组。其中,第二个参数可以是Array的实例,也可以是Arguments对象。

    function sum(num1,num2){
      return num1+num2;
    }
    function callSum1(num1,num2){
       return sum.apply(this,arguments);//传入arguments
    }
    funtion callSum2(num1,num2){
      return sum.apply(this,[num1,num2]);//传入数组
    }
    alert(callSum1(10,10));//10
    alert(callSUm2(10,10));//10

    2)、call()

    call()和apply()方法的作用相同,区别仅在于接收参数的方式不同,对于call()方法而言,接收的第一个参数是this值没有变化,有变化的是:其他参数必须直接传递给函数。

    如下所示I:

    funtion callSum2(num1,num2){
      return sum.call(this,num1,num2);//传入数组
    }

    注意:传递参数其实并非call()和apply()的真正用武之地,这两种方法的真正强大的地方在于能够扩充函数赖以生存的作用域。

    window.color="red";
    var o={color:"blue"};
    function sayColor(){
       alert(this.color);
    }
    sayColor();//red
    sayColor.call(this);//函数是在全局环境下调用的,故this指向window
    sayColor.call(window);
    sayColor.call(o);//用call改变了函数的执行环境,使其指向o。

    使用call()和apply()来扩充作用域的最大好处就是,对象不用与方法有任何耦合关系,讲到this时引用的这个例子的第一个版本中,我们是将sayColor()函数放到对象o中,然后再通过对象o来调用它,现在有了call()和apply()就不用这么麻烦啦。

    3)、bind()

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

    window.color="red";
    var o={color:"blue"};
    function sayColor(){
       alert(this.color);
    }
    var objectSayColor=sayColor.bind(o);
    objectSayColor();//blue
  • 相关阅读:
    管理心理学[9095]
    汽车文化[1196]
    小四轴——空心杯电机引起的电源干扰
    38 时序电路扩展2
    37 时序电路扩展1
    36 时序电路的动态特性分析2
    35 时序电路的动态特性分析1
    34 同步时序电路的设计方法2
    33 同步时序电路的设计方法1
    60. 第k个排列
  • 原文地址:https://www.cnblogs.com/MelodysBlog/p/10482237.html
Copyright © 2011-2022 走看看