zoukankan      html  css  js  c++  java
  • 引用类型(三):Function类型

    一、 Function类型
    函数实际上是对象。每个函数都是Function类型都实例,而且都与其他引用类型一样具有属性和方法。由于函数是对象,因此函数名实际上也是一个指向函数对象都指针。
    1、函数通常是使用函数声明语法定义的

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

    2、这与下面使用函数表达式定义的方式几乎相差无几

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

    3、最后一种定义函数的方式是使用Function构造函数,它可以接收任意数量的参数,但最后一个参数始终都被看成函数体

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

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

    function sum(num1,num2){
        return num1+num2;
    }
    console.log(sum(10,10)); //20
    
    var anotherSum = sum;
    console.log(anotherSum(10,10)); //20
    
    sum = null;
    console.log(anotherSum(10,10));//20

    二、没有重载
    1、将函数名想象为指针,有助于理解为什么es(ECMAScript)中没有函数重载的概念

    function addSomeNum(num){
        return num + 100;
    }
    function addSomeNum(num){
        return num + 200;
    }
    var result = addSomeNum(100); 
    console.log(result); //300

    转化一下

    var addSomeNum = function(num){
        return num + 100;
    }
    addSomeNum = function(num){
        return num + 200;
    }
    var result = addSomeNum(100);

    通过观察重写之后的代码,很容易看清楚到底是怎么回事儿,在创建第二个函数时,实际上覆盖了引用第一个函数的变量addSomeNum


    三、函数声明和函数表达式的区别

    console.log(sum(10,10));
    function sum(num1,num2){
        return num1 + num2;
    }

    可以正常运行

    console.log(sum1(10,10));
    var sum1 = function(num1,num2){
        return num1+num2;
    }

    不能正常运行


    因为解析器通过一个名为函数声明提升的过程,读取并将函数声明添加到执行环境中。函数表达式只提升变量sum,变量sum中还没有函数的引用,除了这点区别外,
    函数声明与函数表达式的语法其实是等价的


    四、函数里面的参数是一个函数时
    传入一个函数时

    function callSomeFunction(someFunction,somArgument){
        return someFunction(somArgument);
    }
    function add10(num){
        return num + 10;
    }
    var result1 = callSomeFunction(add10,10);
    console.log(result1); //20
    
    function getGreeting(name){
        return 'hello,' + name;
    }
    var result2 = callSomeFunction(getGreeting,'Lucy');
    console.log(result2); //'hello,Lucy'

    这里的callSomeFunction()是通用的

    返回一个函数时
    这里是一个对象数组,我们想要根据某个属性对数组进行排序,而传递给数组sort()方法对比较函数要接收两个参数,ap即要比较的值。可是,我们需要一种中方式来指明按照哪个属性来排序,要解决这个问题

    function creatComparisonFuntion(propertyName){
        return function(object1,object2){
            var value1 = object1[propertyName];
            var value2 = object2[propertyName];
            if(value1 < value2){
                return -1;
            }else if( value1 > value2){
                return 1;
            }else{
                return 0
            }
        }
    }
    var data=[{name:'Lily',age:29},{name:'Lucy',age:39},{name:'Jone',age:19},{name:'Han',age:49},{name:'Ming',age:9}];
    data.sort(creatComparisonFuntion('name'));
    console.log(data);
    data.sort(creatComparisonFuntion('age'));
    console.log(data)    

    从一个函数中返回另一个函数,这也是极为有用的一种技术,上面这样返回函数形式可以解决


    五、arguments和this (函数内部属性)
    1、arguments是一个类数组对象,包含着传入函数中到所有参数。虽然arguments到主要用途是保存函数参数。
    但这个对象还有一个名叫callee的属性,该属性是一个指针,指向拥有这个arguments对象的函数。
    经典的阶乘函数

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

    定义阶乘函数一般都要用到递归函数:在函数有名字,而且名字以后也不会变的情况下,这样定义没有问题。但问题是这个函数与函数名factorial紧紧耦合在了一起。
    为了消除这种紧密耦合的想象,可以像下面这样使用arguments.callee.

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

    重写后的函数体内,没有再引用函数名factorial,这样,无论引用函数时使用的是什么名字,都可以保证正常名字完成递归调用

    var trueFactorial = factorial;
    factorial = function(){
        return 0;
    }
    alert(trueFactorial(5)); //120
    alert(factorial(5)); //0


    六、函数都属性和方法
    属性:length和prototype
    方法:call,apply,bind
    1、length:表示函数希望接收都命名参数都个数

    function sum(num1,num2){
        return num1+num2
    }
    console.log(sum.length) //2

    2、prototype:保存它们所有实例方法都真正所在,prototype属性不可枚举,因此使用for-in无法实现
    3、apply()和call()用途都是在特定的作用域中调用函数
    apply()接收两个参数,一个是在其中运行函数的作用域,另一个是参数数组

    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]); //传入数组
    }
    console.log(callSum1(10,10)); //20
    console.log(callSum2(10,10)); //20

    这里面的this值因为是在全局作用域中调用的,所以传入的就是window对象

    function sum(num1,num2){
        return num1 + num2;
    }
    function callSum(num1,num2){
        return sum.call(this,num1,num2); 
    }
    console.log(callSum(10,10));//20

    使用apply()还是call(),完全取决于你采用哪种给函数传递参数的方式最方便。

    事实上,传递参数并非apply()和call()真正的用武之地;它们真正强大的地方是能够扩充函数赖以运行的作用域

    window.color = 'red';
    var o = {color:'blue'};
    function sayColor(){
        console.log(this.color);
    }
    console.log(sayColor()); //red
    console.log(sayColor.call(this)); //red
    console.log(sayColor.call(window)); //red
    console.log(sayColor.call(o)); //blue

    使用call()或者apply()来扩充作用域的最大好处,就是对象不需要与方法有任何耦合关系
    4、bind()会创建一个函数的实例,其this值会被绑定到传给bind()函数的值。

    window.color = 'red';
    var o = {color:'blue'};
    
    function sayColor(){
        console.log(this.color);
    }
    
    var objectSayColor = sayColor.bind(o);
    objectSayColor(); //blue

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

  • 相关阅读:
    机房收费系统重构(三)—工厂+反射+DAL
    机房收费系统重构(二)—菜鸟入门
    机房收费系统重构(—)—小试牛刀
    vb.net机房收费登录功能
    设计模式总结之结构型模式
    设计模式总结之创建型模式
    大话设计之桥接模式
    大话设计之单例模式
    大话设计之适配器模式
    大话设计之抽象工厂模式
  • 原文地址:https://www.cnblogs.com/wzndkj/p/8476888.html
Copyright © 2011-2022 走看看