JavaScript 的数据类型【五】(函数基础上)

  • A+

JavaScript 语言的每一个值,都属于某一种数据类型。JavaScript 的数据类型,共有八种。数值字符串布尔值这三种是原始类型。对象则称为合成类型。undefinednull,为两个特殊值。function,函数也为一种基础类型。ES6 添加了Symbol类型的值。

一.概述

  1. 函数如何声明? ①使用function命令的方式; ②使用函数赋值给变量的方式; ③使用构造方式(基本不用) 具体见:函数声明
  2. 函数如何返回? ①使用return语句返回; ②若无返回,则返回undefined 具体见:return
  3. 函数名提升?①函数是第一等的公民,也和普通变量一样有变量提升的特性具体见:函数名提升
  4. 函数属性name,length,toString()?①name返回函数名称;②length形参长度;toString返回函数字符串具体见:函数的属性和方法
  5. JavaScript的作用域?①函数中定义为局部变量,其余地方定义为全局变量;具体见:函数作用域
  6. 参数省略和传参特性?①调用函数时,可以省略参数,缺省值为undefined;②基础数据为值传参,对象数组等为引用传参具体见:参数
  7. arguments对象?①arguments对象只能在函数内部使用;②用于获取所有实际调用参数具体见:arguments 对象

二.定义函数

  • 函数式第一等的公民,JavaScript 语言将函数看作一种值,与其它值(数值、字符串、布尔值等等)地位相同。凡是可以使用值的地方,就能使用函数。
  • JavaScript 有三种声明函数的方法

1.函数的声明

Ⅰ.function命令的方式(Function Declaration)

  • function命令声明函数,结构function 函数名(参数){函数体},如下范例
    function print(s) {
      console.log(s);
    }
    print('Hi Joey');
    

Ⅱ.函数表达式:函数赋值给变量的方式

  • 将一个匿名函数赋值给变量,此种匿名函数又称函数表达式(Function Expression)
    var print = function(s) {
      console.log(s);
    };
    print('Hi Joey');
    
  • 若函数表达式,添加了函数名,则函数名只能在函数体内使用,其的作用:①可以在函数体内部调用自身;②方便除错(除错工具显示函数调用栈时,将显示函数名,而不再显示这里是一个匿名函数)
    var print = function x(){
      console.log(typeof x);
    };
    
    x
    // ReferenceError: x is not defined
    
    print()
    // function
    
  • 如上:函数表达式赋值并添加函数名,这种定义函数的方式也比较常用
    var f = function f() {};
    

Ⅲ.Function 构造函数

  • 使用Function对象的构造函数声明,在形式参数中最后一个为函数体,此种定义方式几乎不会用到。如下:
    //1. x,y为参数;return x + y为函数体
    var add = new Function(
      'x',
      'y',
      'return x + y'
    );
    
    // 等同于
    function add(x, y) {
      return x + y;
    }
    //2. 若只有一个参数,此参数为函数体
    var foo = new Function(
      'return "hello world";'
    );
    // 等同于
    function foo() {
      return 'hello world';
    }
    

2.函数的重复声明

  • 若函数重复声明,则后声明的函数覆盖先声明的函数(函数也是一个变量,所以符合变量提升规则)
    function f() {
      console.log(1);
    }
    f() // 2
    
    function f() {
      console.log(2);
    }
    f() // 2
    

3.函数调用,return语句,递归

  • 函数调用,函数名加传入函数值就可以调用函数
  • return语句:一返回数据,中断函数执行,余下有语句也不执行。 二函数可没有return语句,表示不返回数据,实际返回undefined
    function add(x, y) {
      return x + y;
    }
    add(1, 1) // 2
    
  • 递归(recursion),函数调用自身,满足一定条件返回
    //计算斐波那契数列
    function fib(num) {
      if (num === 0) return 0;
      if (num === 1) return 1;
      return fib(num - 2) + fib(num - 1);
    }
    fib(6) // 8
    

4.第一等公民和函数名提升

  • JavaScript 语言将函数看作一种值,与其它值(数值、字符串、布尔值等等)地位相同。凡是可以使用值的地方,就能使用函数。
    function add(x, y) {
      return x + y;
    }
    // 将函数赋值给一个变量
    var operator = add;
    // 将函数作为参数和返回值
    function a(op){
      return op;
    }
    a(add)(1, 1)
    // 2
    
  • 函数名提升
    //一此种定义方式不会报错
    f();
    function f() {}
    //二此种定义方式报错
    f();
    var f = function (){};
    // TypeError: undefined is not a function
    //上面的代码等同于下面的形式,f()只声明未定义
    var f;
    f();
    f = function () {};
    

三.函数的属性和方法

1.函数的name属性

  • 函数的name属性返回函数的名字
    //使用function关键字定义的
    function f1() {}
    f1.name // "f1"
    
    //使用匿名函数赋值
    var f2 = function () {};
    f2.name // "f2"
    //使用命名函数赋值
    var f3 = function myName() {};
    f3.name // 'myName'
    

2.函数的length 属性

  • 判断形参的个数
    function f(a, b) {}
    f.length // 2
    

2.toString()函数

  • 函数的toString方法返回一个字符串,内容是函数的源码
  • JavaScript 引擎提供的原生函数,toString()方法就返回原生代码的提示
    //一自定义函数
    function f() {}
    f.toString() // function f() {}
    //二原生函数
    Math.sqrt.toString()
    // "function sqrt() { [native code] }"
    

四.函数作用域

1.函数作用域定义

  1. JavaScript 只有两种作用域:一种是全局作用域,变量在整个程序中一直存在,所有地方都可以读取;另一种是函数作用域,变量只在函数内部存在,叫做局部作用域。ES6 又新增了块级作用域。
  2. 局部作用域可以引用全局作用域,全局作用域不可引用内部作用域。
  3. 函数内部定义的变量会在此作用域内覆盖全局变量
  4. 对于var命令来说,局部变量只能在函数内部声明,在其他区块中声明,一律都是全局变量
    //一局部可以引用全局
    var v = 1;
    function f() {
      console.log(v);
    }
    f()
    // 1
    //二全局不可引用局部
    function f2(){
      var v2 = 1;
    }
    v2 // ReferenceError: v is not defined
    //三函数内部定义的变量覆盖全局变量
    var v3 = 1;
    function f3(){
      var v3 = 2;
      console.log(v3);
    }
    f3() // 2
    v3 // 1
    //四对于var命令来说:除去在方法中为局部变量,其他区块中都为全局变量
    if (true) {
      var x = 5;
    }
    console.log(x);  // 5
    

2.函数内部也存在变量提升

  • 在函数内部,将所有区块中的变量提升到函数的首部
    function foo(x) {
      if (x > 100) {
        var tmp = x - 100;
      }
    }
    
    // 等同于
    function foo(x) {
      var tmp;
      if (x > 100) {
        tmp = x - 100;
      };
    }
    

3.函数本身的作用域

  • 函数本身也是基础类型,本身也有作用域,其的作用域就是与其他类型相同,就是声明时所在作用域
    //范例一
    var a = 1;
    var x = function () {
      console.log(a);
    };
    
    function f() {
      var a = 2;
      //运行的是外部,外部函数a引用的变量值是1
      x();
    }
    
    f() // 1
    //范例二
    var x = function () {
      console.log(a);
    };
    
    function y(f) {
      var a = 2;
      f();
    }
    
    y(x)
    // ReferenceError: a is not defined
    //范例三:函数内部的定义函数
    function foo() {
      var x = 1;
      function bar() {
        console.log(x);
      }
      return bar;
    }
    
    var x = 2;
    var f = foo();
    f() // 1
    

五.参数

1.参数省略

  • 函数参数不是必需的,JavaScript 允许省略参数,省略的参数默认传入undefined;参数顺序省略,不可省略前部参数,只传后部参数
    function f(a, b) {
      return a;
    }
    
    f(1, 2, 3) // 1
    f(1) // 1
    f() // undefined
    
    f.length // 2
    //省略第一参数会报错
    f( , 1) // SyntaxError: Unexpected token ,(…)
    

2.参数传递方式

  1. 函数传值的方式有2种,数值传递和引用传递
  2. 数值传递:原始类型的值(数值、字符串、布尔值)的传递方式是传值传递(passes by value),在函数内修改参数值不会影响到外部数值;
  3. 引用传递:复合类型的值(数组、对象、其他函数)传递方式是传址传递(pass by reference),在函数内部修改数据,会影响原始值;
  4. 在引用传递的情况下,函数内部直接替换引用,则不会影响原值
    //一数值传递
    var p = 2;
    
    function f(p) {
      p = 3;
    }
    f(p);
    
    p // 2
    //二引用传递
    var obj = { p: 1 };
    
    function f(o) {
      o.p = 2;
    }
    f(obj);
    
    obj.p // 2
    //三直接替换引用
    var obj = [1, 2, 3];
    
    function f(o) {
      o = [2, 3, 4];
    }
    f(obj);
    obj // [1, 2, 3]
    

3.同名参数

  • 函数出现同名参数,则以最后出现的为准
    function f(a, a) {
      console.log(a);
    }
    
    f(1, 2) // 2
    //若调用时忽略参数,也取的最后一个的值
    function f(a, a) {
      console.log(a);
    }
    
    f(1) // undefined
    //可以使用arguments函数获取第一个参数
    function f(a, a) {
      console.log(arguments[0]);
    }
    
    f(1) // 1
    

4.arguments 对象

  1. arguments 对象可以在函数内部获取传递到函数的所有参数,以完善JavaScript 允许函数有不定数目参数的特性
  2. arguments 对象只有在函数内部才可以使用
  3. 不开启严格模式可修改函数的值,开启以后无法修改
    //一取值
    var f = function (one) {
      console.log(arguments[0]);
      console.log(arguments[1]);
      console.log(arguments[2]);
    }
    //二非严格模式可以修改参数
    f(1, 2, 3)
    // 1
    // 2
    // 3
    var f = function(a, b) {
      arguments[0] = 3;
      arguments[1] = 2;
      return a + b;
    }
    
    f(1, 1) // 5
    //二严格模式不可修改参数
    var f = function(a, b) {
      'use strict'; // 开启严格模式
      arguments[0] = 3;
      arguments[1] = 2;
      return a + b;
    }
    
    f(1, 1) // 2
    //三获取真实参数长度
    function f() {
      return arguments.length;
    }
    
    f(1, 2, 3) // 3
    f(1) // 1
    f() // 0
    
  4. ① length属性,获取传入时的长度; ② callee 属性 返回所对应的函数;③ 与数组的关系,arguments是一个对象可以与数组转换,从而获取数组对象操作数据的能力,例如(forEach,slice)
    //一获取真实参数长度
    function f() {
      return arguments.length;
    }
    
    f(1, 2, 3) // 3
    f(1) // 1
    f() // 0
    
    //二获取所对应的函数
    var f = function () {
      console.log(arguments.callee === f);
    }
    
    f() // true
    
    //三转换为数组对象
    var args = Array.prototype.slice.call(arguments);
    
    // 或者
    var args = [];
    for (var i = 0; i < arguments.length; i++) {
      args.push(arguments[i]);
    }
    
zhangfeng

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: