zoukankan      html  css  js  c++  java
  • JavaScript 函数详解

    JavaScript函数的概述

    什么是函数

    函数是定义一次但却可以调用或执行任意多次的一段 JS 代码。具有特定功能的n条语句的封装体,只有函数是可执行的, 其它类型的数据是不可执行的

    函数有时会有参数,即函数被调用时指定了值的局部变量。函数常常使用这些参数来计算一个返回值,这个值也成为函数调用表达式的值。(简单的说就是完成一个特定功能的代码块)

    通过函数可以封装任意多条语句,而且可以在任何地方、任何时候调用执行。

    javaScript 中,Function(函数)类型实际上是对象(使用typeof检查一个函数对象时,会返回function)。每个函数都是 Function 类型的实例,而且都与其他引用类型一样具有属性和方法。

    由于函数是对象,因此函数名实际上也是一个指向函数对象的指针。我们在实际开发中很少使用构造函数来创建一个函数对象

    创建一个函数对象,可以将要封装的代码以字符串的形式传递给构造函数

    var fun = new Function("console.log('Hello 这是我的第一个函数');");
    console.log(typeof fun) // function
    console.log(fun) // [Function: anonymous]
    fun() // Hello 这是我的第一个函数,当调用函数时,函数封装的代码会按照顺序执行

    显示的构造函数来创建对象

    任何一个函数只要被new运算符使用了,那么这个函数就可以变成构造函数

    var M = function(){
      this.name = "o2";
      var a = '123'
      console.log(a)
    }
    var o2 = new M(); // 创建对象的时候,里面的代码就会被执行:123
    console.log(o2.name);
    console.log(typeof o2); // object

    函数的声明

    函数的声明不带参数

    function box1() {
        console.log('只有函数被调用,我才会被之执行');
    }
    box1(); //直接调用函数,函数本身不会自己执行,需要调用才会执行

    函数的声明带参数(基本数据类型)

    多个形参之间使用,隔开,声明形参就相当于在函数内部声明了对应的变量,但是并不赋值,所以如果调用函数没有传参数的话,那么参数的值就是undefined

    调用函数时解析器不会检查实参的类型,所以要注意,是否有可能会接收到非法的参数,如果有可能则需要对参数进行类型的检查,函数的实参可以是任意的数据类型

    调用函数时,解析器也不会检查实参的数量,多余实参不会被赋值,如果实参的数量少于形参的数量,则没有对应实参的形参将是undefined

    function box(name, age) { 
      console.log('你的姓名:'+name+',年龄:'+age); 
    } 
    box('huang', 23);//调用函数,并传参,执行结果:你的姓名:huang,年龄:23 box();//如果没传参数,也不会报错,但两个参数的值都是undefined,执行结果:你的姓名:undefined,年龄:undefined

    函数的声明带参数(函数作为参数)

    传递的实参可以是一个函数,将函数作为参数传递给另一个函数有很多种情况,通常是将一个函数的返回值传递给另一个函数(将带返回值的函数作为参数传递)

    function fun(a){
      console.log("a = "+a);
    }
    function mianji(r){
      return 3.14*r*r;
    }
    fun(mianji(10)); //a = 314,相当于将mianji函数的返回值传给了fun函数的a参数
    function box(sum, num) {
         return sum+num;
     }
     function sum(num) {
         return num + 10;
     }
     var result = box(sum(10), 10);//将第二个函数的返回值传递给第一个函数的第一个参数
     console.log(result); // 30
    function box(sum, num) {
       return sum(num);
     }
     function sum(num) {
         return num + 10;
     }
     var result = box(sum, 10);//传递函数到另一个函数里   实际上执行的还是sum这个方法
     console.log(result); // 20

    但是如果作为参数传递的这个函数没有返回值,那么接收参数的那个函数的参数值将为undefined(函数默认返回undefined)

    function fun1(a) {
      console.log("a = "+a); // 结果是:a = undefined,因为传递进来的函数mainji没有写return ,所以默认返回undefined
    }
    
    function mianji(r) {
       console.log(3.14 * r * r) // 会先执行,结果是314
      // 函数如果不写return ,那么会默认返回undefined
    }
    
    fun1(mianji(10))
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>Title</title>
    </head>
    <body>
    <script>
      function fun1(a) {
        console.log(this) // window
        console.log("a = "+a); // 结果是:a = undefined,因为传递进来的函数mainji没有写return ,所以默认返回undefined
      }
    
      function mianji(r) {
        console.log(r) // window
        console.log(this) // window
        // 函数如果不写return ,那么会默认返回undefined
      }
    
      fun1(mianji(this))
    
    </script>
    </body>
    </html>

    传递的是一个匿名函数,那么接收参数的函数的参数值是传递进来的函数声明(因为这个匿名函数没有被调用)

    function fun1(a) {
      console.log("a = "+a);
    }
    
    
    fun1(function () {
      console.log('hello')
    })
    
    /*
    结果是:
    a = function () {
      console.log('hello')
    }
    */

    函数的声明带参数(对象作为参数)

    当我们的参数过多时,可以将参数封装到一个对象中,然后通过对象传递

    function sayHello(o){
      console.log("我是"+o.name+",今年我"+o.age+"岁了,"+"我是一个"+o.gender+"人"+",我住在"+o.address);
    }
    
    //创建一个对象
    var obj = {
      name:"孙悟空",
      age:18,
      address:"花果山",
      gender:"男"
    
    };
    
    sayHello(obj); // 我是孙悟空,今年我18岁了,我是一个男人,我住在花果山

    还可以用表达式的方式声明函数

    var f2 = function () { // 表达式
        console.log('f2()')
    }

    函数的return 返回值

    实际上,任何函数都可以通过 return 语句跟后面的要返回的值来实现返回值。

     function box1() {
         return '我被返回了!'; //通过 return 把函数的最终值返回
     }
     console.log(box1()); //调用函数会得到返回值,然后输出
     function box2(name, age) {
         return '你的姓名:'+name+',年龄:'+age;//通过 return 把函数的最终值返回
     }
     console.log(box2('李炎恢', 28)); //调用函数得到返回值,然后外面输出

    还可以把函数的返回值赋给一个变量,然后通过变量进行操作。

     function box3(num1, num2) {
         return num1 * num2;
     }
     var num = box3(10, 5); //函数得到的返回值赋给变量
     console.log(num);

    return 语句还有一个功能就是退出当前函数,注意和 break 的区别:break 用在循环和 switch 分支语句里

    在函数中return后的语句都不会执行,如果return语句后不跟任何值就相当于返回一个undefined,如果函数中不写return,则也会返回undefined

    function box(num) {
      if (num < 5)
        return num; //满足条件,就返回 num,返回之后,就不执行下面的语句了
      return 100;
    }
    console.log(box(2));

    定义一个函数,判断一个数字是否是偶数,如果是返回true,否则返回false

    function isOu(num){
      return num % 2 == 0;
    }
    
    var result = isOu(15);

    定义一个函数,可以根据半径计算一个圆的面积,并返回计算结果:3.14*r*r

    function mianji(r){
      return 3.14*r*r;
    }
    result = mianji(5);
    console.log("result = "+result); // result = 78.5

    return和break,continue的区别

    function fun(){
      console.log("函数要执行了~~~~");
      for(var i=0 ; i<5 ; i++){
        if(i == 2){
          //使用break可以退出当前的循环
          break;
    
          //continue用于跳过当次循环
          //continue;
    
          //使用return可以结束整个函数
          //return;
        }
        console.log(i);
      }
      console.log("函数执行完了~~~~");
    }
    fun();
    /*
    * 执行结果:
    * 函数要执行了~~~~
    * 0
    * 1
    * 函数执行完了~~~~
    * */

    return后可以跟任意类型的值,可以是一个对象,也可以是一个函数

    function fun2(){
      //返回一个对象
      return {name:"沙和尚"};
    }
    
    var a = fun2();
    
    console.log("a = "+a); // a = [object Object]
    console.log("a = "+a.name); // a = 沙和尚
    function fun3(){
      //在函数内部再声明一个函数
      function fun4(){
        console.log("我是fun4");
      }
      return fun4; //将fun4函数对象作为返回值返回
    }
    
    a = fun3();
    console.log(a);//[Function: fun4]
    a(); // 我是fun4
    fun3()(); //我是fun4,第一个()执行了fun3返回fun4,第二个()就是执行了fun4

    函数的调用

    通过DOM元素的事件进行触发

    <script type="text/javascript">
        function box() { //没有参数的函数
            alert('只有函数被调用,我才会被之执行');
        }
    </script>
    </head>
    <body>
        <input  type="button" value="点我" onclick="box()"/>
    </body>

    将函数赋值给一个变量

    function sum1(num) {
      return num + 10;
    }
    var box1 = sum1;
    console.log(box1()); //NaN
    
    function sum2(num) {
      return num + 10;
    }
    var box2 = sum2(2);
    console.log(box2); // 12
    function getSum(){
      return 100;
    }
    
    var sum = getSum;
    //getSum本身是一个函数名,而函数本身在js中就是一个对象,getSum就是这个函数对象的引用,
    //将getSum这个引用的地址赋给了sum,这时sum也指向了这个函数对象,相当于这个函数有两个函数名
    
    console.log("sum:"+sum);//打印结果是这个函数的定义格式,也就是将函数本体打印出来了
    console.log("sum:"+sum());//打印结果是这个函数的返回

     将一个函数赋值给一个变量的时候,注意不能将调用函数写在定义函数之前详情查看:执行上下文与执行上下文栈中的变量声明提升与函数声明提升

    JavaScript函数的arguments对象

    JavaScript函数的arguments对象概述

    在调用函数时,浏览器每次都会传递进两个隐含的参数:函数的上下文对象 this和封装实参的对象 arguments

    ECMAScript 函数不介意传递进来多少参数,也不会因为参数不统一而错误。

    函数体内可以通过 arguments 对象来接收传递进来的参数,并且保存函数参数

     function box() {
         return arguments[0]+' | '+arguments[1]+' | '+arguments[5]; //得到每次参数的值,最后结果是1 | 2 | 6
     }
     console.log(box(1,2,3,4,5,6)); //传递参数:1 | 2 | 6

    JavaScript函数的arguments 的length 属性

    arguments 对象的下的length 属性可以得到参数的数量

    function box() {
      return arguments.length;
    }
    console.log(box(1,2,3,4,5,6));//得到 6

    利用 length 这个属性,来智能的判断有多少参数,然后把参数进行合理的应用。比如,要实现一个加法运算,将所有传进来的数字累加,而数字的个数又不确定。

    function box() {
      var sum = 0;
      if (arguments.length == 0){
        return sum;  //如果没有参数,退出并且返回0
      }else{
        for(var i = 0;i < arguments.length; i++) {             //如果有,就累加
          sum = sum + arguments[i];
        }
        return sum;                                         //返回累加结果
      }
    }
    console.log(box(5,9,12)); // 26

    一个简单的递归算法

    function box(num) {
      if (num <= 1) {
        return 1;
      } else {
        return num * box(num-1); //一个简单的的递归
      }
    }
    console.log(box(4));//4*3*2*1 = 24  结果是24

    arguments 的callee 的属性与递归算法

    表示的是一个指针,指向拥有这个 arguments 对象的函数

    对于阶乘函数一般要用到递归算法,所以函数内部一定会调用自身

    如果函数名不改变是没有问题的,但一旦改变函数名,内部的自身调用需要逐一修改

    为了解决这个问题,我们可以使用 arguments.callee 来代替

    function box(num) {
      if (num <= 1) {
        return num;
      } else {
        return num * arguments.callee(num-1);//使用 callee 来执行自身,实现递归
      }
    }
    console.log(box(5)); // 120

    递归算法详情查看:递归算法

    函数的重载问题

    ECMAScript 中的函数,没有像其他高级语言那种函数重载功能。(如果有相同名称的函数,则以最后一个为主)

    function box(num) {
      return num + 100;
    }
    function box (num) {
      return num + 200;
    }
    console.log(box(50));//会执行第二个函数,返回结果是250,即调用的是第二个函数
    function box(num,a) {
      return num + 100;
    }
    function box (num) {  //会执行这个函数
      return num + 200;
    }
    console.log(box(50,3)); //结果还250  说明还是执行第二个函数

    JavaScript函数的length属性

    length 属性表示函数希望接收的命名参数的个数

    function box(name, age) {
      console.log(name + age);
    }
    box("中国",20); // 中国20
    console.log(box.length); //2   即该函数中参数的个数

    JavaScript函数的this属性

    JavaScript函数的this属性详解

    函数内部另一个特殊对象是 this,其行为与 Java 和 C#中的 this 大致相似。

    换句话说,this 引用的是函数据以执行操作的对象,或者说函数调用语句所处的那个作用域。

    解析器在调用函数每次都会向函数内部传递进一个隐含的参数,这个隐含的参数就是this,this指向的是一个对象,这个对象我们称为函数执行的 上下文对象

    当在全局作用域中调用函数时,this 对象引用的就是 window(是一个对象,而且是js里面最大的对象,是最外围的对象)。在定义函数时, this还没有确定, 只有在执行时才动态确定(绑定)的

    alert(window);//结果[object Window]   是object类型
    alert(this);//结果[object Window]   是object类型    因为在window范围下   所以this就是window
    window.color = '红色的'; //全局的,或者 var color = '红色的';也行
    alert(this.color); //打印全局的 color
    var color = '红色的'; //全局的,或者 var color = '红色的';也行
    var box = {
      color : '蓝色的',//局部的 color
      sayColor : function () {
        console.log(this.color); //此时的 this 只能 box 里的 color
      }
    };
    box.sayColor();
    console.log(this.color);        //还是全局的,结果是:红色的
    window.color = '红色的';         //全局的,或者 var color = '红色的';也行
    function sayColor(){
        console.log(this.color);
    }
    sayColor(); //这里调用sayColor,其实还是在window范围下的   这种调用函数的方式,在函数中的this指的是window
    window.color = '红色的';         //全局的,或者 var color = '红色的';也行
    var box={
      color:'蓝色'
    }
    function sayColor(){
      console.log(this.color);
    }
    box.sayColor = sayColor;
    box.sayColor();//蓝色

    根据函数的调用方式的不同,this会指向不同的对象:以函数的形式调用时,this永远都是window,作为某个对象的方法的形式调用时,this就是调用方法的那个对象

    function fun1() {
      function fun2() {
        console.log(this);// this是window
      }
      fun2();
    }
    fun1();
    function Person(color) {
      console.log(this)
      this.color = color;
      this.getColor = function () {
        console.log(this)
        return this.color;
      };
      this.setColor = function (color) {
        console.log(this)
        this.color = color;
      };
    }
    
    Person("red"); //this是window
    
    var p = new Person("yello"); //this是p这个对象
    
    p.getColor(); //this是p这个对象
    
    var obj = {};
    p.setColor.call(obj, "black"); //this是obj这个对象
    
    var test = p.setColor;
    test(); //this是window
    // 如何确定this的值?
    test() //window
    obj.test()// obj
    new test() // 新创建的那个对象
    test.call(obj) //obj

    this应用

    在实际的应用开发过程中,通常需要使用到事件的绑定一个函数来执行比较复杂的逻辑,那么这时候函数里面的this表示的是谁呢

    <script>
        window.onload = function(){
            var aBtn = document.getElementsByTagName('input');
    
            for(var i=0; i<aBtn.length; i++){
                aBtn[i].onclick = function (){
                    this.style.background = 'yellow'; //这里this表示的是aBtn[i],而不是window
                }
            }
        }
    </script>
    </head>
    <body>
        <input type="button" value="按钮1" />
        <input type="button" value="按钮2" />
        <input type="button" value="按钮3" />
    </body>
    <script>
        window.onload = function(){
            var aBtn = document.getElementsByTagName('input');
    
            for(var i=0; i<aBtn.length; i++){
                aBtn[i].onclick = fn1;
            }
    
            function fn1(){
                this.style.background = 'yellow';  //这里this同样表示的是aBtn[i]
            }
        }
    </script>
    </head>
    <body>
        <input type="button" value="按钮1" />
        <input type="button" value="按钮2" />
        <input type="button" value="按钮3" />
    </body>
    <script>
        window.onload = function(){
            var aBtn = document.getElementsByTagName('input');
           
            for(var i=0; i<aBtn.length; i++){
                aBtn[i].onclick = function (){
                    fn1();         //如果这样调用的话,fn1函数里面的this代表的是window
                }
            }
            function fn1(){
                this.style.background = 'yellow'; //this代表的是window
            }
        }
    </script>
    </head>
    <body>
        <input type="button" value="按钮1" />
        <input type="button" value="按钮2" />
        <input type="button" value="按钮3" />
    </body>
    <script>
        window.onload = function(){
            var aBtn = document.getElementsByTagName('input');
            var that = null;            //
    
            for(var i=0; i<aBtn.length; i++){
                aBtn[i].onclick = function (){
                    that = this;
                    fn1();      //这里this就表示aBtn[i]
                }
            }
            function fn1(){
                that.style.background = 'yellow';  //这里this同样表示的是aBtn[i]
            }
        }
    </script>
    </head>
    <body>
        <input type="button" value="按钮1" />
        <input type="button" value="按钮2" />
        <input type="button" value="按钮3" />
    </body>
    <script>
        window.onload = function (){
            var aLi  = document.getElementsByTagName('li');
            var that = null;
    
            for( var i=0; i<aLi.length; i++ ){
                aLi[i].onmouseover = function (){
                    //this.getElementsByTagName('div')[0].style.display = 'block';     //这里this代表的是aLi[i]
                    //如果这里要调用外部的方法,将this这个属性赋值给一个变量,然后调用这个函数,那么函数里的this就表示的是aLi[i]它了
                    that = this;
                    fn1();
                };
                aLi[i].onmouseout = function (){
                    this.getElementsByTagName('div')[0].style.display = 'none';
                };
            }
    
            function fn1(){
                that.getElementsByTagName('div')[0].style.display = 'block';
            }
        };
    </script>
    <style>
        li { width:100px; height:150px; float:left; margin-right:30px; background:#f1f1f1; position:relative; z-index:1; }
        div { width:80px; height:200px; background:red; position:absolute; top:75px; left:10px; display:none; }
    </style>
    </head>
    <body>
        <ul>
            <li>
                <div></div>
            </li>
            <li>
                <div></div>
            </li>
            <li>
                <div></div>
            </li>
        </ul>
    </body>

    JavaScript函数的prototype属性

    prototype 下有两个方法:apply()和 call(),每个函数都包含这两个非继承而来的方法。

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

    call()方法于 apply()方法相同,他们的区别仅仅在于接收参数的方式不同。

    对于 call()方法而言,第一个参数是作用域,没有变化,变化只是其余的参数都是直接传递给函数的。

    apply()方法

     function box(num1, num2){
         return num1 + num2;
     }
     console.log(box(10,10));  //结果是20
    
     function sayBox(num1, num2) {
         return box.apply(this, [num1, num2]); //this 表示作用域,这里是 window  []表示 box 所需要的参数
     }
     console.log(sayBox(10,10));//结果也是20    该函数含义是冒充box函数执行  因为sayBox函数没有相加的功能,需要冒充box函数来执行,这样sayBox就有了相加的功能

    如果box函数的参数过多的话,还可以使用下面这种方式

    function box(num1,num2){
      return num1+num2;
    }
    console.log(box(10,10));  //结果是20
    
    function sayBox(num1, num2) {
      return box.apply(this,arguments);//arguments 对象表示 box 所需要的参数
    }
    console.log(sayBox(10,10));//结果也是20    该函数含义是冒充box函数执行  因为sayBox函数没有相加的功能,需要冒充box函数来执行,这样sayBox就有了相加的功能了
    console.log(sayBox(10,90,90,20));//这里不管你传入了多少个参数,box函数只有两个参数的话,那么这里传入有效的参数是前面两个

    call()方法

    function box(num1, num2) {
      return num1 + num2;
    }
    console.log(box(10,10));     //20
    
    function callBox(num1, num2) {
      return box.call(this, num1, num2);         //和 apply 区别在于后面的传参
    }
    console.log(callBox(10,10));    //20

    apply()和 call()方法其他用武之地

    事实上,传递参数并不是 apply()和 call()方法真正的用武之地;它们经常使用的地方是能够扩展函数赖以运行的作用域

    var color = '红色的'; //或者 window.color = '红色的';也行
    var box = {
      color : '蓝色的'
    };
    
    function sayColor() {
      console.log(this.color);
    }
    
    sayColor(); //作用域在 window
    sayColor.call(this);  //作用域在 window
    sayColor.call(window); //作用域在 window
    sayColor.call(box);  //作用域在 box,对象冒充

    我们可以发现当我们使用 call(box)方法的时候,sayColor()方法的运行环境已经变成了 box 对象里了。

    使用 call()或者 apply()来扩充作用域的最大好处,就是对象不需要与方法发生任何耦合关系(耦合,就是互相关联的意思,扩展和维护会发生连锁反应)。

    也就是说,box 对象和sayColor()方法之间不会有多余的关联操作,比如 box.sayColor = sayColor;

    JavaScript的匿名函数

    把匿名函数赋值给变量

    var box = function () { //将匿名函数赋给变量
      return 'Lee';
    };
    console.log(box()); //调用方式和普通函数调用相似

    通过表达式自我执行

    全称: Immediately-Invoked Function Expression 立即调用函数表达式(IIFE),别名: 匿名函数自调用

    作用:隐藏内部实现,不污染外部命名空间,可以用来编写js模块

    (function() {             //封装成表达式
      console.log('Lee');
    })();                     //()表示执行函数
    
    
    (function(num1,num2) {             //封装成表达式
      console.log(num1+num2)
    })(100,90);                     //()表示执行函数,并且传参
    (function (i) {
      var a = 4
      function fn() {
        console.log('fn ', i+a)
      }
      fn()
    })(3)
    (function () {
      var a = 1
      function test() {
        console.log(++a)
      }
      window.$ = function () { // 向外面暴露一个全局的函数
        return {
          test: test
        }
      }
    })()
    
    console.log($)
    console.log($()) //{test: ƒ},是一个对象,里面有个test方法
    $().test() //$是一个函数,调用$这个函数返回一个对象,对象里有一个test方法

    把匿名函数自我执行的返回值赋给变量

    匿名函数自我执行的写法必须要有返回值才能赋值给变量,没有返回值的情况下赋值给变量,变量的值是undefined

     var box = (function() {
         return('Lee');
     })();
     console.log(box); // Lee

    函数里的匿名函数

    function box () {
      return function () { //函数里的匿名函数,产生闭包
        return 'Lee';
      }
    }
    console.log(box()()); //调用匿名函数
    /*
     还可以这样调用:
     var b = box();
     console.log(b());
     */

    函数闭包详情查看:JavaScript的函数闭包详细解释 和 JavaScript 函数闭包的应用 

    回调函数

    什么函数才是回调函数

    你定义的,你没有直接调用,但最终它执行了(在特定条件或时刻)

    常见的回调函数—DOM事件函数

    //1. DOM事件函数
    var btn = document.getElementById('btn')
    btn.onclick = function () {
      alert(this.innerHTML)
    }

    常见的回调函数—定时器函数

    //2. 定时器函数
    setInterval(function () {
      alert('到点啦!')
    }, 2000)

    常见的回调函数—ajax回调函数

    常见的回调函数—生命周期回调函数

    js调用函数时传递变量参数时, 是值传递还是引用传递

    只有值传递, 没有引用传递, 传递的都是变量的值, 只是这个值可能是基本数据, 也可能是地址(引用)数据

    如果后一种看成是引用传递, 那就值传递和引用传递都可以有

    function f(a) {
      console.log(a)
    }
    var n = 4
    f(n) //传递的是n的值 --->值传递
    
    function f2(a) {
      a.name = 'atguigu'
    }
    n = {}
    f2(n) // 传递的是n指向的对象 ---> 引用传递
    console.log(n.name) // atguigu
  • 相关阅读:
    js---小火箭回到顶部
    JS小案例--简单时钟
    堆排序以及TopK大顶堆小顶堆求解方式(js版)
    svg-icon
    Vue 点击按钮 触发 input file 选择文件
    图片裁剪放大缩小旋转 Cropper.js
    Cytoscape
    vue d3 force cytoscape
    获取当月多少天
    谷歌打印去页脚
  • 原文地址:https://www.cnblogs.com/LO-ME/p/10708069.html
Copyright © 2011-2022 走看看