js函数总结

1.函数声明提升

函数声明与变量声明会被JavaScript引擎隐式地提升到当前作用域的顶部,但是只提升名称不会提升赋值部分。

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <title>函数声明提升</title>
    </head>
    <body>
    <script type="text/javascript">
        fn();//我是函数声明
        var fn=function(){
            console.log("我是函数表达式");
        };
        function fn(){
            console.log("我是函数声明");
        }
        fn();//我是函数表达式
        /*
        实际执行顺序:
        var fn;
        function fn(){
           console.log("我是函数声明");
        }
        fn();//我是函数声明
        fn=function(){
            console.log("我是函数表达式");
        };
        fn();//我是函数表达式
         */
         /*
         函数表达式最大的问题,在于js会将此代码拆分为两行代码分别执行。
         所以函数声明覆盖了变量声明
          */
    </script>
    </body>
</html>

<pre name="code" class="html">//封装函数
//1、传统方式封装
//该方式允许封装的函数"先调用,后声明",该过程称为函数的"预加载"
//预加载:程序代码没有执行之前,函数的声明先进入内存
//表面上是函数调用在前、函数声明在后,本质上是函数声明在前、调用在后
//预加载条件:"声明和调用"必须在同一个script标记里面
//原因是每个script标记是独立加载、解释和运行的
//getInfo();//也可以成功调用
function getInfo(){
console.log('beijing');
}
getInfo();
//js中一切都是对象
//函数也是对象(数据类型的一种)
//2、变量赋值方式声明函数,就是函数表达式(不支持函数预加载)
//getWeather();//TypeError: getWeather is not a function
var getWeather=function(){
console.log('晴朗');
}

getWeather();//晴朗

 
 

2.函数-callee

arguments.callee是一个指向正在执行的函数的指针

function jisuan(n){
            if(n<=1){
                return 1;
            }else{
                //return n*jisuan(n-1);//TypeError: jisuan is not a function
                return n*arguments.callee(n-1);
            }
        }
        var js=jisuan;
        jisuan=null;
        console.log(js(5));//120

3.caller属性

caller属性保存着调用当前函数的函数引用

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <title>caller属性</title>
    </head>
    <body>
    <script type="text/javascript">
        function outer(){
            inner();
        }
        function inner(){
            //inner.caller指向outer
            console.log(inner.caller);// outer()
            //alert(inner.caller);//显示outer函数的源代码
            //这样写实现了更松散的耦合
            alert(arguments.callee.caller);//显示outer函数的源代码
        }
        outer();
    </script>
    </body>
</html>

4.匿名函数自执行

//变量赋值声明一个函数
   var getInfo=function(){
    console.log("Today is sunshine");
   }
   //var getInfo="okok";//使用一个同名的getInfo变量把上面的函数给污染覆盖
   getInfo();//Today is sunshine

   //使得以下匿名函数发生执行
   //特点:程序代码没有停顿,立即执行
   //好处:可以避免变量污染
   (function(){
    console.log("I am no name function");
   })();//I am no name function

5.arguments

arguments属性是一个类数组对象,保存着传入函数的所有参数,主要用途就是保存函数参数
arguments有一个callee属性,该属性是一个指针,指向拥有arguments属性的函数

//arguments关键字 在函数内部可以接受传递进来的实参
    //可以通过数字下标形式来访问各个实参信息
    //其length属性可以获得实参个数
    function getInfo(){
         var num=arguments.length;
        //根据传递参数的个数做灵活处理
        if(num==0){
             console.log('个人信息');
        }else if(num==1){
            console.log('名字:'+arguments[0]);
        }else if(num==2){
           console.log('名字:'+arguments[0]+' age:'+arguments[1]);
        }else if(num==3){
         console.log('名字:'+arguments[0]+' age:'+arguments[1]+' job:'+arguments[2]);
        }
        //console.log('名字:'+arguments[0]+' age:'+arguments[1]+' job:'+arguments[2]);
        //console.log(arguments);
    }
    //同一个函数调用的时候,由于传递不同个数的实参,最终导致的结果也不一样
    //其实是模仿方法重载(定义了许多名称一致的方法,这些方法的参数个数或者参数类型不一样
    //这些方法就构成了重载)
    //在php和js中没有重载
    //
    //重写:子类的同名方法去覆盖父类的同名方法
    //重载:一个类里面有许多同名的方法
    getInfo();
    getInfo('liujie');
    getInfo('liujie',23);
    getInfo('liujie',23,'student');

6.apply和call

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

apply()方法接收两个参数:一是在其中运行函数的作用域;二是参数数组。第二个参数可以是Array的实例,也可以是arguments对象。
apply()和call()方法的作用相同,它们的区别仅在于接收参数的方式不同。对于call()方法而言,第一个参数this值没有变化,变化的是其余参数都是直接传递给函数,参数必须逐个列举出来。

第一个参数传null或undefined时,将是JS执行环境的全局变量。浏览器中是window,其它环境(如node)则是global。

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <title>apply和call</title>
    </head>
    <body>
    <script type="text/javascript">
         /*apply()和call()方法真正强大的地方是能够扩充函数赖以运行的作用域
         */
        var color="red";
        var o={color:"blue"};
        function sayColor(){//全局函数
            console.log(this.color);
        }
        sayColor();//red  //这里在全局环境调用,this.color会转化为window.color
        sayColor.call(this);//red   显示的在全局作用域中调用函数
        sayColor.call(window);//red  显示的在全局作用域中调用函数
        sayColor.call(o);//blue  使用call()方法将函数内部的this指向了对象o
    </script>

    </body>
</html>

7.bind方法

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <title>bind方法</title>
    </head>
    <body>
    <script type="text/javascript">
    /*
     bind方法的主要作用是将函数绑定至某个对象
     */
    function f(y){//待绑定的函数
        return this.x+y;
    }
    var o={x:1};//将要绑定的对象
    var g=f.bind(o);//通过调用g(x)来调用o.f(x)
    console.log(g(2));//3

    var color="red";
    var o={color:"blue"};
    function sayColor(){
        console.log(this.color);//blue
    }
    var objectSayColor=sayColor.bind(o);
    //sayColor()调用bind()并传入对象o,创建了objectSayColor()函数
    //objectSayColor()函数的this值等于o
    objectSayColor();
    </script>
    </body>
</html>

8.私有变量

 /*
    任何在函数中定义的变量,都成为私有变量,因为在函数外部无法访问它们
    私有变量包括:函数参数、局部变量以及函数内部定义的其他函数
     */
    function Person(name){
        //在构造函数内部定义了两个特权方法:getName()和setName()。这两个方法都可以在构造函数外部使用,而且都有权访问私有变量name
        this.getName=function(){
            return name;
        };
        this.setName=function(value){
            name=value;
        };
    }
    var person1=new Person("liujie");
    console.log(person1.getName());//liujie
    person1.setName("liujiejie");
    console.log(person1.getName());//liujiejie

9.块级作用域

 function outputNumbers(count){
        //由于JS中没有块级作用域,所以这里的i实际是在包含函数outputNumbers创建的,而不是在块语句中创建的
        //在java、C++等语言中,变量i只会在for循环的块语句中有定义,循环一旦结束,变量i就会被销毁
        //而在js中,变量i是定义在outputNumbers()函数的活动对象中的,因此,从它开始有定义起,就可以在函数内部随处访问它
        for(var i=0;i<count;i++){
            console.log(i);//0、1、2、3、4
        }
        console.log("块语句外:"+i);//5
    }
    outputNumbers(5);

10.模仿块级作用域

 /*
    匿名函数可以用来模仿块级作用域

    (function(){
        //这里是块级作用域,通常称为私有作用域
        //将函数声明放在圆括号中表名它是一个表达式
    })();
     */
    function outputNumbers(count){
        (function(){
            //这里在for循环的外部插入了一个私有作用域,在匿名函数中定义的任何变量,都会在执行结束时被销毁。因此,变量i只能在循环中使用,使用后即被销毁。
                for(var i=0;i<count;i++){
                    console.log(i);//0、1、2、3、4
                }
        })();
        console.log(i);//ReferenceError: i is not defined
    }
    outputNumbers(5);

11.静态方法

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <title>静态方法</title>
    </head>
    <body>
    <script type="text/javascript">
        function Person(name){
            this.name=name;
            this.sayName=function(){
                console.log(this.name);
            }
        }
        Person.sayAge=function(){//静态方法
                console.log(1);
            };
        var person1=new Person('lisi');
        person1.sayName();//lisi
        Person.sayAge();//1
    </script>
    </body>
</html>

静态方法可以通过构造函数名来调用

12.用apply改变函数作用域

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <title>用apply改变函数作用域</title>
    </head>
    <body>
    <script type="text/javascript">
    /*
    apply、call、bind都有个作用就是改变作用域,这里用apply将foo函数的作用域指向obj对象,同时传入参数。
再简单分析一下bind函数内部的嵌套,执行bind函数的时候返回的是一个匿名函数,所以执行bar(3)的时候实际上是执行的bind内部的匿名函数,返回的是之前传入的foo函数的执行结果。
函数没有返回值的情况下默认返回undefined。
     */
        function foo(somthing){
            console.log("一:"+this.a, somthing);//一:2 3
        }
        function bind(fn, obj){
            return function(){
                return fn.apply(obj, arguments);
            }
        }
        var obj = {
            a:2
        }
        var bar = bind(foo, obj);//foo中的this指向了obj
        /*
        bar=function(){
                return fn.apply(obj, arguments);
        }
         */
        var b = bar(3);
        console.log("二:"+b);//二:undefined
    </script>
    </body>
</html>


你可能感兴趣的:(JavaScript,函数)