JavaScript函数

简介: JavaScript函数

1. 函数的声明与使用

  • 函数命名尽量做到语义化表达
  • 函数定义时里面的代码不会执行, 函数必须调用才能执行
  • 函数开可以多次调用, 调用几次执行几次
  // function 函数名() {
  //   函数封装的代码;
  // }
  // 调用 : 函数名()
  function sayHello() {
    console.log("Hello");
  }
  sayHello();
  sayHello();

2. 函数的参数与返回值

  • 形参(parmaters): 用来接收实际参数, 在函数内部作为变量使用
  • 实参(arguments): 实际的参数, 用来把参数传递到函数内部
  • var 变量 = 函数名() 函数执行后会返回一个值, 使用变量来进行接收
  • 可以使用return关键字来返回结果
  • 一旦函数中执行return语句, 那么当前函数就会被终止
  • 函数都是有返回值的, 如果没有使用return语句, 那么函数有默认的返回值undefined
  • 如果使用return语句, 但是return后面没有任何值, 那么函数的返回值也是undefined
  // 1. 函数的参数
  function sayHello(name, age) {
    // name, age: 形参, parmaters
    console.log(`Hello, my name is ${name}, ${age} yaers old`);
  }
  sayHello("zgc", 18); // "zgc", 18: 实参, arguments
  // 2. 函数的返回值
  // var 变量 = 函数名() 函数执行后会返回一个值, 使用变量来进行接收
  // 可以使用return关键字来返回结果
  // 一旦函数中执行return语句, 那么当前函数就会被终止
  // 函数都是有返回值的, 如果没有使用return语句, 那么函数有默认的返回值`undefined`
  // 如果使用return语句, 但是return后面没有任何值, 那么函数的返回值也是undefined
  function foo(name) {
    return name === "zgc";
    console.log("这句不会执行");
  }
  var bar = foo("zgc");
  console.log(bar); // true

3. 数字的格式化工具

  var count1 = 13687; // 13687
  var count2 = 5433332; // 543万
  var count3 = 8766633333; // 87亿
  function formatCount(count) {
    var result = 0;
    if (count >= 10_0000_0000) {
      // _: es6语法糖, 无实际作用, 只是让数字更加直观, 可以加在数字的任何位置
      result = Math.floor(count / 1_0000_0000) + "亿";
    } else if (count >= 10_0000) {
      result = Math.floor(count / 1_0000) + "万";
    } else {
      result = count;
    }
    return result;
  }
  var result1 = formatCount(count1);
  console.log(result1);
  var result2 = formatCount(count2);
  console.log(result2);
  var result3 = formatCount(count3);
  console.log(result3);
  1. arguments初识
  • 在箭头函数中没有arguments
  function foo(name, age) {
    // 函数中都存在着一个变量arguments
    console.log(arguments);
    // arguments是一个对象
    console.log(typeof arguments);
    // 对象内部包含了所有传入的参数, 即使没有通过形参接收
    console.log(arguments[0], arguments[1], arguments[2]); // zgc 19 1.88
  }
  foo("zgc", 19, 1.88);
  // 案例: 求和
  function sum() {
    var total = 0;
    for (var i = 0; i < arguments.length; i++) {
      var num = arguments[i];
      total += num;
    }
    return total;
  }
  console.log(sum(10, 20)); // 30
  console.log(sum(10, 20, 30)); // 60

4. 函数的递归调用

  • 递归必须要有结束条件, 否则会产生无限调用, 造成bug
  // 1. 函数调用其他函数
  function bar() {
    console.log("bar函数执行");
  }
  function foo() {
    console.log("top");
    console.log("foo函数执行");
    bar();
    console.log("bottom");
  }
  foo();
  // 结果:
  // top;
  // foo函数执行;
  // bar函数执行;
  // bottom;
  // 2. 函数调用自己, 即递归
  // 递归必须要有结束条件, 否则会产生无限调用, 造成bug
  // 3. 案例: 封装一个函数, 函数可以实现x的n次方
  // 方法1: 循环实现
  function pow2(x, n) {
    if (n === 0) return 1;
    var result = 1;
    for (var i = 0; i < n; i++) {
      result *= x;
    }
    return result;
  }
  console.log(pow2(5, 3)); // 125
  // 方法2: 递归实现(必须有一个结束条件)
  // 递归的性能不如for循环, 但写出来的代码简洁
  function pow3(x, n) {
    if (n === 0) return 1;
    if (n === 1) return x;
    return x * pow3(x, n - 1);
  }
  console.log(pow3(5, 3)); // 125
  1. 斐波那契数列
  // 斐波那契数列: 1 1 2 3 5 8 13 21 34 55
  // 1. 递归实现
  function getNum1(n) {
    if (n === 1 || n === 2) return 1;
    return getNum1(n - 1) + getNum1(n - 2);
  }
  console.log(getNum1(6)); // 8
  console.log(getNum1(10)); // 55
  // 2. for实现
  function getNum2(n) {
    if (n === 1 || n === 2) return 1;
    var n1 = 1;
    var n2 = 1;
    var num = 0;
    for (var i = 3; i <= n; i++) {
      num = n1 + n2;
      n1 = n2;
      n2 = num;
    }
    return num;
  }
  console.log(getNum2(6)); // 8
  console.log(getNum2(10)); // 55

5. 局部-全局-外部变量

  • 在ES5之前, JS是没有块级作用域的概念, 但函数是可以定义自己的作用域的
  • 作用域: 表示一些标识符的有效范围
  • 函数的作用域表示在函数内部定义的变量, 只有在函数内部可以被访问到
  • 全局变量: 在全局(script)定义一个变量, 那么这个变量可以在定义之后的任何范围内被访问到, 那么这个变量就称之为是一个全局变量
  • 通过var声明的全局变量会在window对象上添加一个属性
  • 局部变量: 在函数内部定义的变量, 只有在函数内部才能进行访问, 称之为局部变量
  • 外部变量: 在函数内部去访问函数之外的变量, 称之为外部变量
  • 在函数内部访问变量, 先在自己的内部寻找, 如果没有找到就往上层作用域寻找, 一直到window对象都没有找到的话就报错
  // 1. 全局变量: 在全局定义一个变量, 那么这个变量可以在定义之后的任何范围内被访问到, 那么这个变量就称之为是一个全局变量
  var message = "Hello World";
  if (true) {
    console.log(message); // Hello World
  }
  function foo() {
    // 2. 局部变量: 在函数内部定义的变量, 只有在函数内部才能进行访问, 称之为局部变量
    var nickname = "zgc";
    console.log("foo", message); // foo Hello World
    console.log("foo", nickname); // foo zgc
    function bar() {
      // 3. 外部变量: 在函数内部去访问函数之外的变量, 称之为外部变量
      console.log("bar", message); // bar Hello World
      console.log("bar", nickname); // bar zgc
    }
    bar();
  }
  foo();
  // console.log("foo", nickname); // nickname is not defined
  // 4. 变量的访问顺序
  // 在函数内部访问变量, 先在自己的内部寻找, 如果没有找到就往上层作用域寻找, 一直到window对象都没有找到的话就报错
  var info = "wlc";
  function name() {
    var info = "zgc";
    function bar() {
      // var info = "wf";
      console.log("访问顺序", info);
    }
    bar();
  }
  name(); //访问顺序 zgc

6. 函数式编程

  • 函数可以作为一等公民
  • 函数可以赋值给变量(函数表达式写法)
  • 函数可以在变量之间来回传递
  • 函数可以作为另一个函数的参数
  • 函数作为另一个函数的返回值
  • 函数存储在另一个数据结构中
  • 函数式编程: 通常我们对函数作为头等公民的编程方式, 称之为函数式编程
  // 1. 函数的声明(推荐)
  function foo() {
    console.log("foo");
  }
  // 2. 函数的表达式
  var bar = function () {
    console.log("bar");
  };
  // 3. 函数可以作为一等公民
  // 函数可以赋值给变量(函数表达式写法)
  var bar1 = function () {
    console.log("bar");
  };
  // 函数可以在变量之间来回传递
  var bar2 = bar1;
  bar2(); // bar
  // 函数可以作为另一个函数的参数
  function foo1(fn) {
    fn();
  }
  foo1(bar1); // bar
  // 函数作为另一个函数的返回值
  function foo2() {
    return bar;
  }
  foo2()(); //bar
  // 函数存储在另一个数据结构中
  var obj = {
    name: "zgc",
    eating: function () {
      console.log("eating");
    },
  };
  obj.eating(); // eating
  // 4. 函数式编程: 通常我们对函数作为头等公民的编程方式, 称之为函数式编程

7. 函数回调与匿名函数

  • 回调函数被认为是一种高级函数,一种被作为参数传递给另一个函数(在这称作"otherFunction")的高级函数,回调函数会在otherFunction内被调用(或执行)。
  • 如果在传入一个函数时, 没有指定函数的名称或者通过函数表达式指定函数对应的变量, 那么这个函数称之为匿名函数
  function foo(fn) {
    // 回调函数: 通过fn调用bar函数的过程, 称之为函数的回调
    fn();
  }
  function bar() {
    console.log("bar");
  }
  bar(); // 函数的调用
  foo(bar);
  // 案例:
  function request(url, callback) {
    // .....
    var list = ["a", "b"];
    callback(list);
  }
  // function getList(list) {
  //   console.log(list);
  // }
  // request("www", getList);
  // 匿名函数写法:
  request("www", function (res) {
    console.log(res);
  });

8. 立即执行函数

  • IIFE(立即调用函数表达式)是一个在定义时就会立即执行的 JavaScript函数
  • 第一部分是包围在 圆括号运算符 () 里的一个匿名函数,这个匿名函数拥有独立的词法作用域
  • 第二部分再一次使用 () 创建了一个立即执行函数表达式,JavaScript 引擎到此将直接执行函数。
  • 当函数变成立即执行的函数表达式时,表达式中的变量不能从外部访问
  • 将 IIFE 分配给一个变量,不是存储 IIFE 本身,而是存储 IIFE 执行后返回的结果
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <button class="btn">1</button>
    <button class="btn">2</button>
    <button class="btn">3</button>
    <button class="btn">4</button>
    <script>
      // 1. 立即执行函数
      // (function () {
      //   代码块;
      // })();
      // 当函数变成立即执行的函数表达式时,表达式中的变量不能从外部访问。
      (function () {
        var nickname = "Barry";
      })();
      // 无法从外部访问变量 name
      // console.log(nickname); // 抛出错误:"Uncaught ReferenceError: name is not defined"
      // 将 IIFE 分配给一个变量,不是存储 IIFE 本身,而是存储 IIFE 执行后返回的结果。
      var result = (function () {
        var name = "Barry";
        return name;
      })();
      // IIFE 执行后返回的结果:
      console.log(result); // "Barry"
      // 2. 应用:
      // (1) 防止全局变量的命名冲突
      // 写法一
      var tmp = newData;
      processData(tmp);
      storeData(tmp);
      // 写法二
      (function () {
        var tmp = newData;
        processData(tmp);
        storeData(tmp);
      })();
      // (2) 形成单独的作用域, 避免外界访问和修改内部变量
      var btnEls = document.querySelectorAll(".btn");
      for (var i = 0; i < btnEls.length; i++) {
        var btn = btnEls[i];
        btn.onclick = function () {
          // onclick回调是在点击时才会触发, 在这时for循环早已经执行完毕
          alert(i); // 结果总是4, 而不是0,1,2, 3
        };
      }
      // 解决方法:
      // 1. for循环中用let代替var
      // 2. 立即执行函数
      var btnEls = document.querySelectorAll(".btn");
      for (var i = 0; i < btnEls.length; i++) {
        var btn = btnEls[i];
        (function (m) {
          // 将i当参数传递进去, 立即执行函数有单独的作用域, 当点击事件回调时读取到作用域中的参数 m
          // i 是实参, m 是形参
          btn.onclick = function () {
            alert(m); // 0, 1, 2, 3
          };
        })(i);
      }
    </script>
  </body>
</html>

9. this关键字

在函数中this到底取何值,是在函数真正被调用执行的时候确定的,函数定义的时候确定不了,也就是说,this的指向完全取决于函数调用的位置。

  • 函数调用: 当一个函数不是一个对象的属性时,直接作为函数来调用时,this指向全局对象,立即执行函数,默认的定时器等函数,this也是指向window。
  • 方法调用: 如果一个函数作为一个对象的方法来调用时,this指向这个对象。
  • 构造函数调用: this指向这个用new新创建的对象。
  • apply 、 call 和 bind 调用模式: 这三个方法都可以显示的指定调用函数的 this 指向。
  • 箭头函数的this: 指向声明时所在作用域下 this 的值,即箭头函数的this去他的上级作用域下寻找,任何方法都改变不了他的指向
  // 1. 函数调用
  function foo() {
    console.log(this === window); //true
  }
  foo();
  // 2. 方法调用
  var obj = {
    name: "zgc",
    running: function () {
      // 这里的上级作用域是window, 对象是数据类型, 不是代码块, 没有作用域
      console.log(this);
    },
  };
  obj.running(); //obj对象
  var fn = obj.running;
  fn(); // window对象
  function bar() {
    console.log(this);
  }
  var baz = {
    name: "wf",
    bar: bar,
  };
  baz.bar(); // baz对象
相关文章
|
24天前
|
JavaScript
变量和函数提升(js的问题)
变量和函数提升(js的问题)
|
24天前
|
JavaScript
常见函数的4种类型(js的问题)
常见函数的4种类型(js的问题)
11 0
|
25天前
|
JavaScript
写一个函数将N组<>(包含开始和结束),进行组合,并输出组合结果 (js)
写一个函数将N组<>(包含开始和结束),进行组合,并输出组合结果 (js)
10 0
|
1月前
|
自然语言处理 JavaScript 网络架构
js开发:请解释什么是ES6的箭头函数,以及它与传统函数的区别。
ES6的箭头函数以`=&gt;`定义,简化了函数写法,具有简洁语法和词法作用域的`this`。它无`arguments`对象,不能用作构造函数,不支持`Generator`,且不改变`this`、`super`、`new.target`绑定。适用于简短表达式,常用于异步编程和高阶函数。
18 5
|
1月前
|
JavaScript 前端开发 网络架构
JavaScript 谈谈对箭头函数的理解及其与普通函数的区别。
JavaScript 谈谈对箭头函数的理解及其与普通函数的区别。
18 1
|
1月前
|
前端开发 JavaScript 数据处理
在JavaScript中,什么是异步函数执行的例子
在JavaScript中,什么是异步函数执行的例子
10 0
|
1月前
|
JavaScript
JS封装节流函数
JS封装节流函数
15 0
|
1月前
|
JavaScript 前端开发
javascript箭头函数
javascript箭头函数
|
1月前
|
JavaScript 小程序
微信小程序 wxml 中使用 js函数
微信小程序 wxml 中使用 js函数
76 0
|
1月前
|
JavaScript 前端开发
JavaScript函数科里化
JavaScript函数科里化