[华为云在线课程][JavaScript的函数][学习笔记]

举报
John2021 发表于 2022/02/19 07:52:47 2022/02/19
【摘要】 第1章,JavaScript函数定义和调用及作用域函数的基本概念未完成某一功能的程序指令(语句)的集合,称为函数函数是一段可以重复调用的代码块,接收相应的参数并可以返回对应的值函数的声明function命令函数表达式Function构造器 1.1,JavaScript的function命令function命令声明的代码区块,就是一个函数function命令后面是函数名,函数名后面是一对圆括...

第1章,JavaScript函数定义和调用及作用域

  • 函数的基本概念

    • 未完成某一功能的程序指令(语句)的集合,称为函数
    • 函数是一段可以重复调用的代码块,接收相应的参数并可以返回对应的值
  • 函数的声明

    • function命令
    • 函数表达式
    • Function构造器

1.1,JavaScript的function命令

  • function命令声明的代码区块,就是一个函数
  • function命令后面是函数名,函数名后面是一对圆括号,里面是传入函数的参数。函数体放在大括号里面。
  • function命令定义函数格式:
function add(x, y) {
    console.log(x + y);
}
  • 上面的代码命令了一个add函数,输出两个数字相加的和,以后使用add()这种形式,就可以调用相应的代码,这叫做函数的声明(Function Declaration)
  • JavaScript的function命令案例一:

<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
<script>
    //1,求1-100的累加值
    /*var sum = 0;
    for (var i = 0; i <= 100; i++) {
        sum += i;
    }
    console.log(sum);//5050*/

    //2,求10-50的累加值
    /*var sum = 0;
    for (var i = 10; i <= 50; i++) {
        sum += i;
    }
    console.log(sum);//1230*/

    //3,函数就是封装了一段可以被重复执行的代码块,就是让大量代码可以重用
    /*function getSum(num1, num2) {
        sum = 0;
        for (var i = num1; i <= num2; i++) {
            sum += i;
        }
        console.log(sum);
    }

    getSum(1, 100);//5050
    getSum(10, 50);//1230
    getSum(60, 50);//0 变量和参数必须以一直顺序出现,第一个变量就是被传递的第一个参数给定值*/

    //4,想要书写一个代码来弹出带有不同文字的一段话
    /*function myName(name, job) {
        alert("Welcome " + name + ", the " + job);
    }

    myName("zhy", "teacher");*/
</script>
</body>
</html>

1.2,JavaScript函数的表达式

  • 除了用function命令声明函数,还可以采用变量赋值的写法
  • 函数表达式定义函数格式:
var add = function (x, y) {
    console.log(x + y);
};
  • 这种写法将一个匿名函数赋值给变量。这时,这个匿名函数又称函数表达式(Function Expression),因为赋值语句的等号右侧只能放表达式
  • 采用函数表达式声明函数时,function命令后面不带有函数名。如果加上函数名,该函数名只在函数体内部有效,在函数体外部无效
  • JavaScript函数的表达式案例:

<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
<script>
    var fn = function (i, j) {
        return i + j;
    };
    console.log(fn(1, 2));//3
</script>
</body>
</html>

<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
<script>
    //比较类的函数
    function createComparisonFunction(propertyName) {
        return function (object1, object2) {
            var value1 = object1[propertyName];//取得object1相同属性名称所对应的值
            var value2 = object2[propertyName];//取到object2相同属性名称所对应的值
            if (value1 < value2) {
                return -1;
            } else if (value1 > value2) {
                return 1;
            } else {
                return 0;
            }
        };
    }

    var a1 = {"age": 19};
    var a2 = {"age": 20};
    var fn = createComparisonFunction("age");
    console.log(fn(a1, a2));//-1
</script>
</body>
</html>

<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
<script>
    //递归例子
    /*
    * 写一个函数,实现n的阶乘
    * n!=(n-1)!*n
    * 
    * 递归的重点
    * 1,找规律
    * 2,找出口
    * 3,很容易找出抽象概念的就适合用递归
    * */
    function fn(n) {
        return n * fn(n - 1);
    }
</script>
</body>
</html>

1.3,JavaScript的function构造函数

  • Function构造函数可以使用或不使用new命令,返回结果完全一样
  • 总的来说,这种声明函数的方式非常不直观,使用很少
  • Function构造函数定义函数格式:
var add = new Function(
    "x", "y", "console.log(x+y)"
);
add(1, 1);//2
  • 可以传递任意数量的参数给Function构造函数,只有最有一个参数会被当做函数体,如果只有一个参数,该参数就是函数体
  • Function构造函数案例:

<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
<script>
    //函数构造器 构建函数 Function()
    //1,new可写可不写,在JavaScript当中大部分时间需要避免使用new关键字
    /*
    * 因为使用new关键字就相当于建立了一个新的对象,同时就说明会多占用一段内存空间
    * 如果不及时回收的话,积累多了就会造成内存溢出,消耗资源
    * */
    //2,Function的F必须大写
    //3,Function() 函数构造器是圆括号
    var square = new Function(
            "x", "y", "let sum;sum=x+y;return sum;"
    );
    console.log("square(2,3)的结果是:" + square(2, 3));//5

    var myFunction = new Function("a", "b", "c", "return a+b+c;");
    var x = myFunction(1, 2, 3);
    console.log(x);//6

    //函数表达式的改写
    var myFunction1 = function (a, b, c) {
        return a + b + c;
    };
    var y = myFunction1(2, 3, 4);
    console.log(y);//9
</script>
</body>
</html>

1.4,JavaScript函数的调用方式和返回值

  • 调用函数时,要使用圆括号运算符
  • 圆括号之中,可以加入函数的参数
  • 函数的调用
function add(x, y) {
    return x + y;
}

var result = add(3, 4);
console.log(result);//7
  • 上面代码中,函数名后面紧跟一对圆括号,就会调用这个函数。函数体内部的return语句,表示返回。
  • JavaScript引擎遇到return语句,就直接返回return后面那个表达式的值,后面即使还有语句,也不会得到执行。
  • 也就是说,return语句所带的那个表达式,就是函数的返回值。return语句不是必须的,如果没有的话,该函数就不返回任何值,或者说返回undefined
  • JavaScript函数的调用方式和返回值案例:

<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
<script>
    function getSum(num1, num2) {
        return num1 + num2;
    }

    console.log(getSum(1, 2));//3

    ////////////////////////////////////////////////////
    function get() {
        function display() {
            console.log("内部函数");
        }

        return display;
    }

    var fn = get();//外层函数的返回值赋给了变量fn
    console.log(typeof fn);//function

    fn();//内部函数
    ////////////////////////////////////////////////////

    function show() {
        console.log("这是show函数");
        return function () {
            console.log("这是匿名函数");
        };
    }

    show();//这是show函数
    show()();//这是show函数   这是匿名函数
</script>
</body>
</html>

1.4.1,JavaScript的函数递归调用

  • 函数可以调用自身,这就是递归
  • 比如:通过递归,计算数字的阶乘

<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
<script>
    function fact(i) {
        if (i === 1) {
            return 1;
        }
        return i * fact(i - 1);
    }

    console.log(fact(3));//6
</script>
</body>
</html>

1.4.2,JavaScript中的call和apply函数调用

  • JavaScript中的call方法
    • 语法:call(对象,参数…);
    • 定义:调用一个对象的一个方法,以另一个对象替换当前对象

<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
<script>
    function add(x, y) {
        return x + y;
    }

    function myAddCall(x, y) {
        return add.call(this, x, y);
    }

    console.log(myAddCall(1, 2));//3
</script>
</body>
</html>
  • JavaScript中的apply方法
    • 语法:apply(对象,[参数]);
    • 定义:调用对象的一个方法,另一个对象替换当前对象

<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
<script>
    function add(x, y) {
        return x + y;
    }

    function myAddApply(x, y) {
        return add.apply(this, [x, y]);
    }

    console.log(myAddApply(1, 2));//3
</script>
</body>
</html>

1.4.3,JavaScript函数说明

  • JavaScript语言将函数看作一种值,与其他值(数值、字符串、布尔值等等)地位相同
  • 凡是可以使用值的地方,就能使用函数
    • 可以把函数赋值给变量和对象的属性,也可以当作参数传入其他函数,或者作为函数的结果返回
    • 函数只是一个可以执行的值,此外并无特殊之处

<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
<script>
    function add(x, y) {
        console.log(x + y);
    }

    //将函数赋值给一个变量
    var operator = add;
    operator(1, 2);

    //将函数作为参数和返回值
    function addOpera(fn) {
        return fn;
    }

    addOpera(add(1, 2));//3
    addOpera(add)(1, 2);//3
</script>
</body>
</html>

1.4.4,JavaScript不支持函数重载

  • JavaScript没有方法重载的说法,如果两个方法名字一样,即使参数个数不一样,那么后面定义的就会覆盖前面的定义,调用方法时永远是调用后定义的那个
  • JavaScript不支持函数重载的案例

<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
<script>
    function Test(a) {
        console.log(a);
    }

    function Test(a, b) {
        console.log("hello world!");
    }

    function Test(a, b) {
        console.log(a + "   " + b);
    }

    Test(20);//20 undefined
    Test(1, 2);//1 2
    //调用的是最后一个定义的Test方法
</script>
</body>
</html>

1.5,JavaScript函数的作用域

  • 作用域(scope)指的是变量存在的范围
  • 在ES5规范中,JavaScript只有两种作用域:一种是全局作用域,变量在整个程序中一直存在,所有地方都可以读取;另一种是函数作用域,变量只在函数内部存在
  • ES6又新增了块级作用域

JavaScript全局作用域

  • JavaScript函数的全局作用域,在任何地方都可以访问:

<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
<script>
    var age = "18";

    function getAge() {
        console.log(age);
    }

    console.log(age);//18
    getAge();//18
</script>
</body>
</html>
  • JavaScript函数的函数作用域,只在函数内部可以访问:

<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
<script>
    //局部变量,只能在函数体内部可以取到值
    //临时作用域,称为AO
    function getAge() {
        var age = "hello";//局部变量,只能在函数体内部可以取到值,函数体外不能取值
        console.log(age);//hello
    }

    getAge();//hello
    console.log(age);//age未定义,无法输出
</script>
</body>
</html>
  • 任何变量,如果未经过声明就赋值,此变量是全局变量所有(包括变量在函数内部)
  • 一切声明过的全局变量,都是window的属性
  • 作用域为全局,并且成为GO
  • JavaScript生命周期问题,局部变量的作用在函数执行完毕之后进行销毁,全局变量等到页面关闭之后进行销毁

JavaScript块级作用域

  • ES6新增的let声明变量

<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
<script>
    {
        let age = "25";
    }
    console.log(age);//age 未定义
</script>
</body>
</html>

<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
<script>
    {
        var a = 1;
        console.log(a);//1
    }
    console.log(a);//通过var定义的变量是可以跨块级作用域访问到的

    (function A() {
        var b = 2;
        console.log(b);//2
    })();//立即执行函数
    console.log(b);//报错 通过var定义的变量是不能跨函数作用域访问到的
</script>
</body>
</html>

第2章,JavaScript函数的参数

2.1,JavaScript函数的可变参数

  • 函数运行的时候,有时需要提供外部数据,不同的外部数据会得到不同的结果,这种外部数据就叫参数
  • 函数参数不是必须的,JavaScript允许省略参数

<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
<script>
    function square(x, y) {
        return x * y;
    }

    console.log(square(2));//NaN
    console.log(square(3, 4));//12
</script>
</body>
</html>

<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
<script>
    //非严格模式下,函数里可以出现同名形参,只能访问最后出现的该名称形参
    //严格模式下,出现同名形参的话会抛出语法错误
    function add(x, x, x) {
        return x;
    }

    console.log(add(1, 2, 3));//3
</script>
</body>
</html>
  • JavaScript函数形参和实参个数不匹配问题
参数个数 说明
实参个数等于形参个数 输出正确结果
实参个数多于形参个数 只取得形参的个数
实参个数少于形参个数 多的形参定义为undefined,结果为NaN

<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
<script>
    function sum(num1, num2) {
        console.log(num1 + num2);
    }

    sum(1, 2);//3  个数相等输出正确结果
    sum(1, 4, 5, 7);//5  形参少于实参,取到形参个数
    sum(2);//NaN  //形参多于实参
</script>
</body>
</html>

2.2,JavaScript的函数的传递方式

  • 按值传递
    • 函数参数如果是原始类型的值(数值、字符串、布尔值),则采用这种方式,此时在函数体内修改参数值,不会影响到函数外部

<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
<script>
    var age = 25;//number类型
    function setAge(age) {
        age = 26;//函数体内部变量age 外部age的复制本
    }

    console.log(age);//25
    //////////////////////////////////////////////////////////////////
    var count = 10;

    function num(num1) {
        num1 = 1;//用1覆盖了传进来的10,传进来的还是count的复制本
        return num1;//1
    }

    var result = num(count);
    console.log(result);//1
    console.log(count);//10
</script>
</body>
</html>
  • 按址传递
    • 如果函数参数是复合类型的值(数值、对象、其他函数),则采用这种方式,传入函数的原始值的地址,此时在函数体内修改参数值,会影响到函数外部。

<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
<script>
    var person = {
        "age": 25
    };

    //传入的是不是person对象存放在堆内存中的地址
    function setPerson(person) {
        person.age = 26;
    }

    setPerson(person);
    console.log(person.age);//26
</script>
</body>
</html>

<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
<script>
    var person = {
        "age": 25
    };

    //传入的还是外部person对象存放在堆内存中的地址
    function setPerson(person) {
        //开辟了新的内存地址(等于开了新的房间)
        person = {
            age: 26
        };
    }

    setPerson(person);
    console.log(person.age);//25
</script>
</body>
</html>

2.3,JavaScript的arguments对象

  • 由于JavaScript允许函数有不定数目的参数,所以需要一种机制,可以在函数体内部读取所有参数,这就是arguments对象的由来
  • arguments对象包含了函数运行时的所有参数,arguments[0]就是第一个参数,arguments[1]就是第二个参数,以此类推。这个对象只有在函数体内部,才可以使用。
  • arguments是一个类似数组的对象,并不是真正意义上的数组(1,具有数组的length属性;2,按照索引的方式进行存储;3,没有真正数组的一些方法pop(),push())

<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
<script>
    function add(one) {
        var len = arguments.length;//获取传入参数个数
        for (var i = 0; i < len; i++) {
            console.log(arguments[i]);
        }
    }

    add(1, 2, 3, 4);//1,2,3,4
</script>
</body>
</html>

<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
<script>
    /*var f = function (a, b) {
        "use strict";//开启严格模式
        arguments[0] = 3;
        arguments[1] = 2;
        return a + b;
    }
    console.log(f(1, 1));//2*/

    ////////////////////////////////////////////

    /*function add(a, b) {
        var reallen = arguments.length;//实参个数
        console.log("reallen:", arguments.length);
        var len = add.length;//形参个数
        console.log("len:", add.length);
        if (reallen === len) {
            console.log("形参和实参个数一致");
        } else {
            console.log("形参和实参个数不一致");
        }
    };
    add(1, 2);//reallen:2 len:2 形参和实参个数一致
    add(1, 2, 3);//reallen:3 len:2 形参和实参个数不一致*/

    ////////////////////////////////////////////

    /*function fn() {
        console.log(arguments);//里面存储的所有传递过来的数据
        console.log(arguments.length);//3
        console.log(arguments[2]);//3
        //按照数组方式遍历arguments
        for (var i = 0; i < arguments.length; i++) {
            console.log(arguments[i]);//1,2,3
        }
    }

    fn(1, 2, 3);*/

    ////////////////////////////////////////////

    /*//利用函数求任意两个数的最大值
    function getMax() {
        var max = arguments[0];
        for (var i = 0; i < arguments.length; i++) {
            if (arguments[i] > max) {
                max = arguments[i];
            }
        }
        return max;
    }

    console.log(getMax(1, 2, 3));//3
    console.log(getMax(1, 2, 3, 4, 5));//5
    console.log(getMax(11, 2, 34, 444, 5, 100));//444*/
</script>
</body>
</html>

第3章,JavaScript函数的闭包

3.1,JavaScript闭包的定义

  • 闭包是指在函数外部访问函数作用域中局部变量的函数
  • 或者说闭包就是能够读取其他函数内部变量的函数
  • 或者说闭包是指有权访问另一个函数作用域中的变量的函数

<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
<script>
    function f1() {
        var n = 999;//f1函数内部的局部变量n
        //在f1函数内部定义一个f2函数
        function f2() {
            //在f2函数内部可以访问局部变量n
            alert(n);//999
        }
    }

    f1();
    console.log(n);//Uncaught ReferenceError: n is not defined
</script>
</body>
</html>

<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
<script>
    function f1() {
        var n = 999;//f1函数内部的局部变量n
        //在f1函数内部定义一个f2函数
        function f2() {
            console.log(n);
        }

        return f2;//将f2函数作为f1函数的返回值
    }

    var result = f1();//f1调用完的返回值是一个f2函数
    result();//999 调用了f2函数
</script>
</body>
</html>
  • 闭包例子
function a() {
    function b() {
        var bbb = 234;
        console.log(aaa);
    }

    var aaa = 124;
    return b;
}

var glob = 100;
var demo = a();
demo();
  • 闭包特点:

    • 在一个函数内部定义另外一个函数,并返回内部函数或立即执行内部函数
    • 内部函数可以访问外部函数定义的局部变量
    • 让局部变量始终保存在内存中。也就是说,闭包可以使得它诞生的环境一直存在
    • 由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在浏览器中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除

<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
<script>
    function add() {
        var i = 100;
        return function f() {
            i++;
            console.log(i);
        }
    }

    var t1 = add();
    t1();//101
    t1();//102
    t1();//103
    t1();//104
    t1();//105
    t1();//106
</script>
</body>
</html>

JavaScript运行三部曲

  • 1,语法分析(通篇扫描,看是否有存在缺少}等低级错误)
  • 2,预编译
  • 3,解释执行
  • JavaScript脚本的运行主要的两个阶段:预编译阶段和执行阶段,先进行预编译,再执行语句

函数作用域

全局变量和局部变量

  • 全局变量

    • 1,任何变量,如果未经过声明就赋值,此变量就为全局变量所有(包括变量在函数内没有声明)
    • 2,一切声明的全局变量,都是window的属性
    • 全局变量的作用域就是全局,称为GO,全称:Global Object
  • 局部变量

    • 变量在函数内声明,变量为局部作用域
    • 局部变量:只能在函数内部访问
    • 临时作用域称为AO,全称:Activation Object
  • JavaScript变量声明周期

    • JavaScript变量声明周期在它声明时初始化
    • 局部变量在函数执行完毕后销毁
    • 全局变量在页面关闭后销毁

作用域链

  • 在JavaScript中,函数也是对象,实际上,JavaScript里一切都是对象。函数对象和其他对象一样,拥有可以通过代码访问的属性和一系列仅供JavaScript引擎访问的内部属性。其中一个内部属性是[[Scope]],该内部属性包含了函数被创建的作用域中对象的集合,这个集合被称为函数的作用域链,它决定了哪些数据能被函数访问。
  • 链式作用域:JavaScript语言特有的链式作用域结构(chain scope),子对象会一级一级地向上寻找父对象的变量。所以,父对象的所有变量,对子对象都是可见的,反之则不成立。
  • 例子:
//作用域链例子
function a() {
    function b() {
        var b = 234;
    }

    var a = 123;
    b();
}

var glob = 100;
a();

预编译(执行期上下文)

  • 简单理解,就是在内存中开辟一块空间,用来存放变量和函数

预编译公式

  • 1,创建GO/AO对象
  • 2,找形参和变量声明,将变量和形参名作为AO的属性名,值为undefined
  • 3,将实参值和形参统一
  • 4,在函数体里面找函数声明,值赋予函数体
  • 例子:
var a = 123;

function b(aa) {
    var c = 888;
}

b(a);
  • 在执行这段JavaScript代码之前,JavaScript引擎会先把JavaScript代码编译一遍
  • 这个编译的过程就是定义作用域的过程
  • 而最外层的作用域成为GO,GO里面现在就有a和b,而且值都是undefined
  • 由于目前是GO,所以公式3不用管
  • 执行公式4后,GO里面的a还是undefined,b变成fn b()
  • 这时b的值就是一个函数体
  • 完成以上4句预编译公式后,GO就算定义好了,a是123,b是fn b()
  • 当预编译完成后,代码会按顺序执行
  • 由于第2-4行代码是一个函数体,这是又需要再预编译一次
  • 函数会创建一个AO,而且还会继承父函数的作用域下的所有东西,AO里面的aa是undefined,c是undefined
  • 将实参值和形参统一

3.2,JavaScript的立即执行函数

  • JavaScript有时需要在定义函数之后,立即调用函数(函数只使用一次),这种函数就叫做立即执行函数,全称为立即调用函数表达式
  • 立即调用函数表达式是一个定义时就会立即执行的JavaScript函数
  • 立即调用函数表达式组成:
    • 第一部分是包围在圆括号运算符()里的一个匿名函数
    • 第二部分再一次使用()创建了一个立即执行函数表达式,JavaScript引擎到此将直接执行函数
(function () {
    /**Code**/
})

<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
<script>
    /*
    * 1,函数声明和函数表达式的不同:
    * (1)函数声明提升  预编译
    * (2)函数表达式执行到那之后才会执行  
    * */
    console.log(add(1, 2));//3
    console.log(sub(5, 3));//报错

    function add(a1, a2) {
        return a1 + a2;
    }

    var sub = function (a1, a2) {
        return a1 - a2;
    };
    console.log(sub(5, 3));//2
</script>
</body>
</html>

<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
<script>
    function makeCounter() {
        //只能在makeCounter内部访问i
        var i = 0;
        return function () {
            console.log(++i);
        };
    }

    var counter = makeCounter();
    counter();//1
    counter();//2

    var counter2 = makeCounter();
    counter2();//1
    counter2();//2
</script>
</body>
</html>
  • JavaScript立即调用函数表达式作用一:
    • 将立即调用函数表达式分配给一个变量,不是存储立即调用函数表达式本身,而是存储立即调用函数表达式执行后返回的结果

<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
<script>
    var result = (function () {
        var name = "局部变量";
        return name;
    })();
    console.log(result);//局部变量
    /*
    * 如果将立即调用函数后面的()去掉,那么就会打印出函数本身
    * */
</script>
</body>
</html>
  • JavaScript立即调用函数表达式作用二:
    • 使用立即调用函数表达式把计数器变量保存为私有变量更安全,同时也可以减少对全局空间的污染。

<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
<script>
    var add = (function () {
        var count = 0;
        return function () {
            return ++count;
        };
    })();
    console.log(add());//1
    console.log(add());//2
</script>
</body>
</html>
  • JavaScript立即调用函数表达式作用三:
    • JavaScript面向对象立即调用函数表达式实现单例模式

<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
<script>
    var counter = (function () {
        var i = 0;
        return {
            get: function () {
                return i;
            },
            set: function (val) {
                i = val;
            },
            increment: function () {
                return ++i;
            }
        };
    })();
    console.log(counter.get());//0
    counter.set(3);
    console.log(counter.increment());//4
    console.log(counter.increment());//5
</script>
</body>
</html>
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。