zoukankan      html  css  js  c++  java
  • 【重温基础】4.函数

    本文是 重温基础 系列文章的第四篇。
    今日感受:常怀感恩之心,对人对己。

    系列目录:

    本章节复习的是JS中的基础组件之一,函数,用来复用特定执行逻辑。

    1.定义函数

    定义函数有两种方法:函数声明函数表达式

    1.1 函数声明

    也成为函数声明,通常格式为:

    function f (a){
        return a + 1;
    }
    

    解释:这里声明一个函数 f ,并传入一个参数 a ,当函数执行以后,通过 return 关键字返回了 a+1的值。

    参数
    当传入的参数是一个数字/字符串等具体的值的时候,若参数的值被改变,不会影响到全局或调用函数。
    但如果参数是个对象,若函数内改变的这个参数的属性,则函数外部的这个参数原始的值会被修改。

    var leo = {
        age:20
    }
    function f(obj){
        obj.age = 15;
        obj.name = 'leo';
    }
    f(leo);
    console.log(leo); //{age: 15, name: "leo"}
    

    1.2函数表达式

    通过定义一个匿名的函数,来赋值给一个变量,通过这个变量来调用这个函数。

    var f = function (a){
        return a + 1;
    }
    

    但是函数表达式也可以提供函数名,用于函数内部调用,并指代本身,也可以作为调试器堆栈跟踪中识别该函数。

    var f = function g(a){
        return n < 2 ? 1 : a*g(a-1);
    }
    

    另外,函数表达式声明可以用来根据不同条件,来定义一个函数:

    var f;
    if(a == 1){
        f = function (){
            return 'when a == 1';
        }
    }else {
        f = function (){
            return 'when a != 1';
        }
    }
    

    2.函数调用

    函数定义完成后不会自动执行,需要我们通过函数名称来调用,才能真正执行:

    var f = function (){
        console.log('ok');
    }
    f(); // 'ok'
    

    另外,函数也可以调用自身,这就是递归过程:

    function f (n){
        if( n==0 || n==1) {
            return 1;
        }else {
            return n * f(n-1);
        }
    }
    // 三目运算
    function f (n){
        return (n==0 || n==1)?1: n*f(n-1);
    }
    

    3.函数作用域

    由于函数只在函数的内部有定义,所以函数内部定义的变量在函数外部不能访问,函数内部就是这个函数的作用域。
    当一个父级函数内,还定义了一个子级函数,则这个子级函数可以访问父级函数定义的变量。

    // 全局作用域 global scope
    var a = 1, b = 2;
    function f (){
        return a + b;
    }
    f(); // 3
    
    function g(){
        var a1 = 'leo', b1 = 'pingan';
        function hi (){
            return a1 + '和' + b1
        } 
        return hi();
    }
    g(); // 'leo和pingan'
    

    3.1 闭包

    闭包是 JavaScript 中最强大的特性之一,并且JS允许函数嵌套。
    在一个函数内部在嵌套一个函数,而嵌套的这个函数对外面的函数是私有的,则形成一个闭包,闭包是一个可以自己拥有独立的环境和变量的表达式,通常是函数。
    理解一下,前面说的内部函数可以调用外部函数的变量和方法,那么可以这么理解:闭包的函数继承了父级容器函数的参数和变量,即内部函数包含外部函数的作用域
    总结一下:

    • 内部函数只能在外部函数中访问;
    • 内部函数形成闭包:可以访问外部函数的参数和变量,但外部函数却不能使用这个内部函数的参数和变量;
    function f(a) {
        function g(b){
            return a + b;
        }
        return g;
    }
    var a1 = f(5);  // ƒ g(b){ return a + b; }
    var a2 = a1(6); // 11
    var a3 = f(5)(6); // 11
    

    闭包可以给内部函数的变量提供一定的安全保障
    另外,闭包还有复杂的用法:

    var f = function (name){
        var age ;
        return {
            setName : function (newName){
                name = newName;
            },
    
            getName : function (){
                return name;
            },
    
            getAge : function (){
                return age;
            },
            setAge : function (newAge){
                age = newAge;
            }
        }
    }
    
    var leo = f('leo');
    leo.setName('pingan');
    leo.setAge(20);
    leo.getName();     // 'pingan'
    leo.getAge();      // 20
    

    3.2命名冲突

    在同一个闭包作用域下若参数或变量名相同,产生冲突,则优先使用作用域最近

    function f(){
        var a = 1;
        function g(a){
            return a + 1;
        }
        return g;
    }
    f()(3); // 4
    

    4.arguments对象

    函数的实际参数会被保存在一个类数组对象 arguments 对象中,通过索引访问具体的参数:

    var a = arguments[i]
    

    arguments的索引从0开始,也有arguments.length属性获取长度。
    当我们不知道参数的数量的时候,可以使用arguments.length来获取实际传入参数的数量,再用arguments对象来获取每个参数。
    例如:

    // 拼接所有参数为一个字符串
    // 参数 s 为分隔符
    function f( s ){
        var text = '';
        for(var i = 0;i<= arguments.length; i++){
            text += arguments[i] + s ;
        }
        return text;
    }
    
    f('--','leo','pingan','robin');
    // "----leo--pingan--robin--undefined--"
    f('**','leo','pingan','robin');
    // "****leo**pingan**robin**undefined**"
    

    5.函数参数

    ES6开始,新增两个类型的参数:默认参数剩余参数

    5.1默认参数

    若函数没有传入参数,则参数默认值为undefined,通常设置参数默认值是这样做的:

    // 没有设置默认值
    function f(a, b){
        b = b ? b : 1;
        return a * b;
    }
    f(2,3);  // 6
    f(2);    // 2
    
    // 设置默认值
    function f(a, b = 1){
        return a * b;
    }
    f(2,3);  // 6
    f(2);    // 2
    

    5.2剩余参数

    可以将参数中不确定数量的参数表示成数组,如下:

    function f (a, ...b){
        console.log(a, b);
    }
    f(1,2,3,4); // a => 1 b => [2, 3, 4]
    

    6.箭头函数

    函数箭头表达式是ES6新增的函数表达式的语法,也叫胖箭头函数,变化:更简洁的函数和this

    • 更简洁的函数
    // 有1个参数
    let f = v => v;
    // 等同于
    let f = function (v){return v};
    
    // 有多个参数
    let f = (v, i) => {return v + i};
    // 等同于
    let f = function (v, i){return v + i};
    
    // 没参数
    let f = () => 1;
    // 等同于
    let f = function (){return 1};
    
    let arr = [1,2,3,4];
    arr.map(ele => ele + 1);  // [2, 3, 4, 5]
    
    • this
      注意这几点:
      1. 箭头函数内的this总是指向定义时所在的对象,而不是调用时。
      2. 箭头函数不能当做构造函数,即不能用new命令,否则报错。
      3. 箭头函数不存在arguments对象,即不能使用,可以使用rest参数代替。
      4. 箭头函数不能使用yield命令,即不能用作Generator函数。

    一个简单的例子:

    function Person(){
      this.age = 0;
    
      setInterval(() => {
        this.age++;
      }, 1000);
    }
    var p = new Person(); // 定时器一直在执行 p的值一直变化
    

    参考资料

    1.MDN 函数


    本部分内容到这结束

    Author 王平安
    E-mail pingan8787@qq.com
    博 客 www.pingan8787.com
    微 信 pingan8787
    每日文章推荐 https://github.com/pingan8787/Leo_Reading/issues
    JS小册 js.pingan8787.com

    bg

    个人博客:http://www.pingan8787.com 微信公众号【前端自习课】和千万网友一起,每日清晨,享受一篇前端优秀文章。 目前已连续推送文章 600+ 天,愿每个人的初心都能一直坚持下去!
  • 相关阅读:
    gridview展示行号
    DateEdit如果开启Vista模式并显示日期+时间模式
    DevExpress GridView 添加和设置右键菜单
    C# WinForm实现粘贴图片到PictureBox及复制PictureBox中的图片
    dll反编译工具(ILSpy)的使用
    Dev的双击Gridview的DoubleClick
    SQL Server日期时间格式转换字符串详解
    LabelControl文本居中显示
    C# winform 判断click事件点击的是左键还是右键
    Winform窗体状态的判断及调用打开的窗体的方法
  • 原文地址:https://www.cnblogs.com/pingan8787/p/11838221.html
Copyright © 2011-2022 走看看