zoukankan      html  css  js  c++  java
  • js原生之函数

    1.函数作为参数传给其他函数:
        data.sort(function(a,b){return a-b})
        //关于数组的sort函数,其回调函数返回负值,a在b之前
        //正值,b在a之前

    2.函数定义好后立即调用
        var temp=(function(x){return x*x})(10);

        function foo(){};//函数声明,可以提前
        var foo=function(){};//表达式定义函数,不能提前

    3.JAVASCRIPT的函数可以嵌套在其他函数里面
        function hy(a,b){
            function square(x){return x*x;}
            return Math.sqrt(square(a)+square(b));
        }

    4.函数声明语句并非真正的语句,他们可以出现在全局代码里,或者内嵌在其他函数内,
    但是它们并不能出现在循环,条件判断或者try/catch语句中。
    但函数定义表达式可以出现在javascript的任何地方

    5.jquery的方法链式调用原理
        $(":header").map(function(){return this.id;}).get().set();
        当方法不需要返回值时,建议将其返回值定为return this;
        这样就可以实现方法的链式调用。

    6.嵌套函数中this的指向问题
     var o={
               m:function(){
                   var This=this;
                   f();

                   function f(){
                       console.log(this);
                       console.log(This);
                   }
               }
            };
        o.m();
    以上示例中,对象o内定义了函数m(),在函数m()内定义函数f()并调用
    在f内的this并不指向调用外层函数的上下文,而是指向全局对象
    其返回值:

                Window Html001.html//严格模式下返回undefined
                Object { m=function()}

    7.构造函数的调用
    如果在函数或者方法调用前带有关键字new,就形成了构造函数调用
    var o=new Object();
    var o=new Object;//没有形参时可以省略掉Object后面的括号
    一旦使用了new关键字调用构造函数,构造函数内的this就会指向new出来的新对象
            var o={
               m:function(){
                   var This=this;
                   f();

                   function f(){
                       //console.log(this);
                       console.log(This===o);
                   }
               }
            };
            new o.m();//函数内部this不指向上下文,也就是不指向o

    8.间接调用,call()与aplly()
    允许函数显示的指定函数运行的域,也就是规定this的指向
    foo.call(obj1,args1...);
    foo.apply(obj2,arg[...]);

    9.函数调用的形参与实参
    function foo(arg1,arg2,arg3....){}

    foo(arg1);
    如果函数调用时传入的实参比函数定义时的形参数要少,则多的形参被定义为undefined
    这个特性可用来实现类似函数重载的功能

    function getPropertyName(o,/*可选参数,数组a*/arr) {
        // body...
        var arr=arr||[];//undefined在逻辑运算中返回false,若调用时a不存在,则将arr赋予新的空数组[]
        for(var p in o) arr.push(p);
            return arr;
    }
    getPropertyName(o);
    getPropertyName(o,myArray);

    设计原则:
        当使用这种可选参数来创建函数时,满足原则1.可选参数放在参数列表最后。2./**/用注释说明可选参数
            3.调用函数时,可选参数位置最好显式的传入null作为占位符。

    function foo(arg1,arg2,arg3....){}

    foo(arg1);
    再回到这个函数调用例子,当实参列表超过形参列表时,函数内部无法显式获得超过部分的引用
    此时可用arguments类数组来调用,事实上arguments[]数组内存储着传进来的所有实参
    arguments也包含着length属性;

    实参对象的应用实例:
            (function (x,y,z){
                if(arguments.length!=3){
                    throw new Error(
                            "该函数必须传入3个参数"
                    );
                }
                console.log(x+y+z);
            }(1,2));
            运行结果:Error: 该函数必须传入3个参数
        用于验证实参列表个数,只有在实参数目为3时,才执行函数。
    /**
    复习:类数组对象
        js的数组有四大特性:
            1.有新元素加入数组时,自动更新length
            2.设置length为一个较小值时将截断数组
            3.从Array.prototype继承一些方法
            4.其类属性为“Array”

        这四个特性让数组与类数组对象区分开来
        而像arguments这样的类数组对象,可以理解为其属性名刚好为从0开始的非负整数序列
        且有个length属性记录它的属性长度

        而区别在于length没有数组的特性1,2.
        且没有从Array.prototype继承方法

        但是可以利用call临时继承调用
        var a={"0":"a","1":"b","2":"c"};

        Array.prototype.join.call(a,"+") //"a+b+c"
        
        Array.map(function(x){return x*x}) //ecma5,不修改原数组,返回一个数组副本,其中的每一项都是
                                            参数内函数的返回值。

    */
    细说实参对象arguments://该对象的可枚举属性仅有实参
        属性callee与caller
                callee:指代当前正在执行的函数
                caller:调用当前正在执行的函数的函数//!--暂时无法理解这句话--!

                callee可用在匿名函数中实现递归
                function (x) {
                    // body...
                    if(x<1)return 1;
                    return x*arguments.callee(x-1);//此处的arguments.callee()就是在调用函数本身
                }

    10.将对象属性用作实参,减少记忆参数列表顺序,在参数列表巨大的情况下
        function arrayCopy(a,a_start,b,b_start,length) {//该函数将a数组从a_start处复制length个元素至b数组中起始位置b_start位置
                        // 要记忆这些形参的顺序实在不容易
                    }

        function easyCopy(argument) {
                        // body...
                        arrayCopy(arguments.from,
                                arguments.from_start||0,
                                arguments.to,
                                arguments.to_start||0,
                                arguments.length
                            )
                    }
        //在调用easyCopy时,只需传入一个匿名对象就可以了
        a=[1,2,3,4];
        b=[];
        easyCopy(
            {from:a,to:b,length:4}//传入匿名对象
            );

    11.作为值的函数
        在js中,函数也可以作为--实参,传入别的函数参数列表;
        看如下一个函数的定义
            function square(x) {return x*x}
        这个定义创建一个新的函数对象,并将其值赋给square;
        var s=square;//现在s和square指代同一个函数

        类似数组的sort()方法,接受一个函数
        写法Array.sort(function (a,b) {return a-b;
            // body...
        })
        或者Array.sort(bj);//bj指代一个函数

    12.自定义函数属性
        js中函数是对象,所以可以定义函数的属性,用于计数之类的工作
        比如要计算一个函数的调用次数,又不想定义一个全局变量
        则可以:
        foo.times=0;
        function foo(argument) {
            // body...
            foo.times++;
        }//还有更多用途,

        //定义缓存
        foo.times=0;//判断if语句执行次数的标记
            function foo(x){
                if(x<1&&x!=Math.round(x)) throw new Error("foo()函数参数为大于1的正整数");
                if( !(x in foo)){
                    foo.times++;
                    console.log(foo.times);
                    foo[x]=x*foo(x-1);//将已经计算出来的值存储进foo[]类数组之中
                }
                return foo[x];
            }
            foo[1]=1;

            foo(11);
            foo(10);//从返回结果可以看到,此处并没进入if判断中,减少了很多次运算
            foo(12);
            foo(0.6);//验证数值输入的正确性

                代码运行返回结果:
                        1
                        Html001.html (第 23 行)
                        2
                        Html001.html (第 23 行)
                        3
                        Html001.html (第 23 行)
                        4
                        Html001.html (第 23 行)
                        5
                        Html001.html (第 23 行)
                        6
                        Html001.html (第 23 行)
                        7
                        Html001.html (第 23 行)
                        8
                        Html001.html (第 23 行)
                        9
                        Html001.html (第 23 行)
                        10
                        Html001.html (第 23 行)
                        11
                        Html001.html (第 23 行)
                        Error: foo()函数参数为大于1的正整数

    13.作为命名空间的函数
        类似jquery这样的库的做法
        a.(function (argument) {
            // body...
        }())
        b.(function (argument) {
            // body...
        })();
        定义一个匿名函数并立即调用,在匿名函数内的变量就不会污染全局命名空间
        对于包裹function的圆括号的解释:
            function前面没有圆括号时,js会将function解释为函数声明语句,这样就无法立即执行
            而加上圆括号,或是其他运算符,则会被解释为函数定义表达式。

    14.闭包
        js中,函数的执行依赖于变量作用域,这个作用域是在函数定义时创建的,而不是在函数执行时。
        例子1:
            var scope="global scope";
            function checkscope() {
                // body...
                var scope="local scope";
                function f() {
                    // body...
                    return scope;
                }
                return f();//f在checkscope内部执行时很容易理解,它寻找f的作用域,在checkscope函数内找到scope就返回了
            }
            checkscope();
        例子2:
            var scope="global scope";
            function checkscope() {
                // body...
                var scope="local scope";
                function f() {
                    // body...
                    return scope;
                }
                return f;
            }
            checkscope()();    //在全局执行chechscope内部的函数f;因为闭包的存在,f()也是在其作用域链内寻找scope
                            //这个作用域链在函数定义之初就开始存在

        例子3:                    
        foo.times=0;
        function foo(argument) {
            // body...
            foo.times++;
        }//这是12小结的一个例子,foo.times来记录函数foo的调用次数
        下面用闭包来改写它。
            var times=(function () {
                    var count=0;
                    return function(){
                        return count++;
                    }
            }())//当外层作为命名空间的函数返回之后,除了timer(),任何函数或语句都访问不到其内部的count
                //而又因为times在外部使用其作用域链上的count,所以count会作为一个变量一直存在内存
                //times()每次调用都访问的是内存中已经存在的count;故可以充当计数器
            function foo(argument) {
            // body...
                times();
            }


        例子4:
        function counter(){
            var count=0;
            return {
                count:function(){return ++count;},
                reset:function(){return count=0;}
            }//返回一个包含两个方法的匿名对象
        }

        var obj1=counter();
        var obj2=counter();//obj1和obj2接受到的是不同的Object。所以他们的count互不干涉
         console.log(obj1.count());
         console.log(obj1.count());
         console.log(obj1.count());

         例子5://ECMA5中的变体
         function counter(n){
             return {
                 get count(){
                     return ++n;
                 },
                 set count(m){
                     if (m<=n) {throw Error("您设置的n太小了")}
                     return n=m;
                 }
             }
         }
         var obj=counter(100);
         console.log(obj.count);
         console.log(obj.count);
         console.log(obj.count);
         console.log(obj.count=200);
         console.log(obj.count);
         console.log(obj.count);
             利用setter和getter来定义计数器count,给予计数器设置初值和临时更改的方法
             而count则是obj的私有属性,且通过赋值修改必须遵循get方法设置的条件

         例子6://闭包实现私有属性的经典做法
              function addPrivateProperty(o,name,predicate) {
                // body...
                var value;
                o["get"+name]=function () {
                    return value;
                };
                o["set"+name]=function(v){
                    if (predicate&&!predicate(v)) {throw new Error("数据未通过predicate函数合法性验证")}
                    value=v;
                }
            }

            var o={};
            addPrivateProperty(o,"Name",function(v){return typeof v=="string";});
            addPrivateProperty(o,"Age");//未进行合法性验证

            o.setName("wangjue");
            o.setAge(18);
            console.log(o.getName());
            console.log(o.getAge());
            为何能够不断添加属性,是因为每个属性的set和get方法组--存在于不同的作用域链,
            可以利用如下例子进行例证:
            function constfunc(v){
                var value;
                return function(){
                    value=v;
                    return value;};

            }
            var fun1=constfunc("arr1");
            var fun2=constfunc();//发现只要传递的参数不一样,就会创建新的闭包

            console.log(fun1());//=>arr1
            console.log(fun2());//=>undefine    value并未共享


         例子7:
             function constfunc(v) {
                 // body...
                 return function(){return v;};
             }//一个总是返回v的函数
             for(var i=0;i<10;i++) fun[i]=constfunc(i);
             fun[5]();//=》返回值为5

             上述代码循环创建了很多闭包

         总结性语言:多个嵌套函数并不会将作用域链上的私有成员复制一份,他们是共享的。

         例子8:
             嵌套函数内,也就是闭包,无法访问外部函数的arguments和this
             function foo(arg1,arg2) {
                 // body...
                 var self=this;
                 var outArguments=arguments;

                 return function () {
                     return self+outArguments.toString(" ");
                 }
             }
             前文已经提过,嵌套函数内部使用this,指代的是全局环境window,或者undefined
             //闭包  end

    15.函数的属性,方法和构造方法
        在js中,函数是特殊的对象,故其也有对象的一些性质
            所以其也拥有属性和方法,能够用Function()构造函数创建。

        (1)length:函数对象也拥有一个length属性,其值为函数声明时的形参列表的表长。
                    //一个检测实参与形参是否数量相等的函数
                    function check(argument) {
                        // body...
                        if(argument.length!=argument.callee.length) throw new Error("传入参数数量不对")
                    }

                    function test(arg1,arg2) {
                        // body...
                        check(arguments);
                        return arg1+arg2;
                    }

                    test(1,2);

        (2)prototype每一个函数都有一个原型属性
        (3)call(),与aplly()方法

        (4)bind(); //ECMA5中新增的方法,f.bind(obj);将函数f绑定至obj上
            兼容性解决:
                function bind(f,o) {
                    // body...
                    if(f.bind) return f.bind(o);
                    else{
                        return f.apply(o,arguments);
                    }
                }//这里的自定义bind()函数就是将f绑定至o下,且立即执行

            function f(argument) {
                // body...
            }
            f.bind(o,agr1,arg2);//bind除第一个参数外,后面的几个参数都将传入f,参与函数调用

        (5)toString()//返回函数的完整源码

        (6)Function()构造函数;


    16.函数式编程
        类似数组的reduce(),map()之类的方法就是函数式编程的例子


    17.高阶函数,所谓高阶函数就是操作函数的函数,其传入函数作为参数,返回值也是函数

    18.记忆函数
        //闭包的实例
            function memorize(foo){
                var cache={};
                return function () {
                    // body...
                    var key=arguments.length+Array.prototype.join.call(arguments,",");//作为缓存的键
                    if(key in cache)  return cache[key];//若在cache中找到了key,则直接返回内存中的值
                    else {
                        cache[key]=foo.apply(this,arguments);//内存中没有,则赋值。
                        return counter[key];//
                    }
                }
            }

            //当我们使用一个递归函数时,往往要使用记忆功能
            var factorial=memorize(function (n) {
                // body...
                return n<=1?1:n*factorial(n-1);
            })
            factorial(5);

  • 相关阅读:
    Ubuntu安装GTK+教程
    Qt 错误GL/gl.h: No such file or directory的解决方法
    Qt 解决Could not start process "make" qmake_all问题
    Feign解决服务之间调用传递token
    python闭包和装饰器
    python高阶函数
    ping 和 traceroute 的区别
    ICMP协议
    OSPF协议
    RIP协议
  • 原文地址:https://www.cnblogs.com/koleyang/p/4939873.html
Copyright © 2011-2022 走看看