zoukankan      html  css  js  c++  java
  • JS函数

    JS函数

    函数定义

    function abs(x) {
       if (x >= 0) {
           return x;
      } else {
           return -x;
      }
    }

    abs()函数实际上是一个函数对象,而函数名abs可以视为指向该函数的变量。

    var abs = function (x) {
       if (x >= 0) {
           return x;
      } else {
           return -x;
      }
    };

    上述两种定义完全等价,注意第二种方式按照完整语法需要在函数体末尾加一个;,表示赋值语句结束。

    调用函数

    由于JavaScript允许传入任意个参数而不影响调用,因此传入的参数比定义的参数多也没有问题,虽然函数内部并不需要这些参数:

    abs(10, 'blablabla'); // 返回10
    abs(-9, 'haha', 'hehe', null); // 返回9

    传入的参数比定义的少也没有问题:

    abs(); // 返回NaN

    此时abs(x)函数的参数x将收到undefined,计算结果为NaN

    要避免收到undefined,可以对参数进行检查:

    function abs(x) {
       if (typeof x !== 'number') {
           throw 'Not a number';
      }
       if (x >= 0) {
           return x;
      } else {
           return -x;
      }
    }

    arguments

    arguments,它只在函数内部起作用,并且永远指向当前函数的调用者传入的所有参数。arguments类似Array但它不是一个Array

    function foo(x) {
       console.log('x = ' + x); // 10
       for (var i=0; i<arguments.length; i++) {
           console.log('arg ' + i + ' = ' + arguments[i]); // 10, 20, 30
      }
    }
    foo(10, 20, 30);

    arguments最常用于判断传入参数的个数。

    rest参数

    function foo(a, b, ...rest) {
       console.log('a = ' + a);
       console.log('b = ' + b);
       console.log(rest);
    }

    foo(1, 2, 3, 4, 5);
    // 结果:
    // a = 1
    // b = 2
    // Array [ 3, 4, 5 ]

    foo(1);
    // 结果:
    // a = 1
    // b = undefined
    // Array []

    rest参数只能写在最后,前面用...标识,从运行结果可知,传入的参数先绑定ab,多余的参数以数组形式交给变量rest,所以,不再需要arguments我们就获取了全部参数。

    小心你的return语句

    function foo() {
       return
          { name: 'foo' };
    }

    foo(); // undefined

    要小心了由于JavaScript引擎在行末自动添加分号的机制,上面的代码实际上变成了:

    function foo() {
        return; // 自动添加了分号,相当于return undefined;
            { name: 'foo' }; // 这行语句已经没法执行到了
    }

    嵌套函数

    function foo() {
        var x = 1;
        function bar() {
            var x = 'A';
            console.log('x in bar() = ' + x); // 'A'
        }
        console.log('x in foo() = ' + x); // 1
        bar();
    }
    
    foo();

    JavaScript的函数在查找变量时从自身函数定义开始,从“内”向“外”查找。如果内部函数定义了与外部函数重名的变量,则内部函数的变量将“屏蔽”外部函数的变量。

    变量提升

    JavaScript的函数定义有个特点,它会先扫描整个函数体的语句,把所有申明的变量“提升”到函数顶部:

    语句var x = 'Hello, ' + y;并不报错,原因是变量y在稍后申明了。但是console.log显示Hello, undefined,说明变量y的值为undefined这正是因为JavaScript引擎自动提升了变量y的声明,但不会提升变量y的赋值。

    对于上述foo()函数,JavaScript引擎看到的代码相当于:

    function foo() {
        var y; // 提升变量y的申明,此时y为undefined
        var x = 'Hello, ' + y;
        console.log(x);
        y = 'Bob';
    }

    命名空间

    // 唯一的全局变量MYAPP:
    var MYAPP = {};
    
    // 其他变量:
    MYAPP.name = 'myapp';
    MYAPP.version = 1.0;
    
    // 其他函数:
    MYAPP.foo = function () {
        return 'foo';
    };

    把自己的代码全部放入唯一的名字空间MYAPP中,会大大减少全局变量冲突的可能。

    let

    'use strict';
    
    function foo() {
        for (var i=0; i<100; i++) {
            //
        }
        i += 100; // 仍然可以引用变量i
    }

    为了解决块级作用域,ES6引入了新的关键字let,用let替代var可以申明一个块级作用域的变量:

    'use strict';
    
    function foo() {
        var sum = 0;
        for (let i=0; i<100; i++) {
            sum += i;
        }
        // SyntaxError:
        i += 1;
    }

    常量

    ES6标准引入了新的关键字const来定义常量,constlet都具有块级作用域:

    'use strict';
    
    const PI = 3.14;
    PI = 3; // 某些浏览器不报错,但是无效果!
    PI; // 3.14

    解构赋值

    var [x, y, z] = ['hello', 'JavaScript', 'ES6'];

    如果数组本身还有嵌套,也可以通过下面的形式进行解构赋值,注意嵌套层次和位置要保持一致:

    let [x, [y, z]] = ['hello', ['JavaScript', 'ES6']];
    x; // 'hello'
    y; // 'JavaScript'
    z; // 'ES6'

    解构赋值还可以忽略某些元素:

    let [, , z] = ['hello', 'JavaScript', 'ES6']; // 忽略前两个元素,只对z赋值第三个元素
    z; // 'ES6'

    如果需要从一个对象中取出若干属性,也可以使用解构赋值,便于快速获取对象的指定属性:

    var person = {
        name: '小明',
        age: 20,
        gender: 'male',
        passport: 'G-12345678',
        school: 'No.4 middle school'
    };
    var {name, age, passport} = person;

    对一个对象进行解构赋值时,同样可以直接对嵌套的对象属性进行赋值,只要保证对应的层次是一致的:

    var person = {
        name: '小明',
        age: 20,
        gender: 'male',
        passport: 'G-12345678',
        school: 'No.4 middle school',
        address: {
            city: 'Beijing',
            street: 'No.1 Road',
            zipcode: '100001'
        }
    };
    var {name, address: {city, zip}} = person;
    name; // '小明'
    city; // 'Beijing'
    zip; // undefined, 因为属性名是zipcode而不是zip
    // 注意: address不是变量,而是为了让city和zip获得嵌套的address对象的属性:
    address; // Uncaught ReferenceError: address is not defined

    如果要使用的变量名和属性名不一致,可以用下面的语法获取:

    var person = {
        name: '小明',
        age: 20,
        gender: 'male',
        passport: 'G-12345678',
        school: 'No.4 middle school'
    };
    
    // 把passport属性赋值给变量id:
    let {name, passport:id} = person;
    name; // '小明'
    id; // 'G-12345678'
    // 注意: passport不是变量,而是为了让变量id获得passport属性:
    passport; // Uncaught ReferenceError: passport is not defined

    解构赋值还可以使用默认值,这样就避免了不存在的属性返回undefined的问题:

    var person = {
        name: '小明',
        age: 20,
        gender: 'male',
        passport: 'G-12345678'
    };
    
    // 如果person对象没有single属性,默认赋值为true:
    var {name, single=true} = person;
    name; // '小明'
    single; // true

    有些时候,如果变量已经被声明了,再次赋值的时候,正确的写法也会报语法错误:

    // 声明变量:
    var x, y;
    // 解构赋值:
    {x, y} = { name: '小明', x: 100, y: 200};
    // 语法错误: Uncaught SyntaxError: Unexpected token =

    这是因为JavaScript引擎把{开头的语句当作了块处理,于是=不再合法。解决方法是用小括号括起来:

    ({x, y} = { name: '小明', x: 100, y: 200});

    使用场景

    var x=1, y=2;
    [x, y] = [y, x];//交换变量
    
    var {hostname:domain, pathname:path} = location;//快速获得域名和路径
    
    function buildDate({year, month, day, hour=0, minute=0, second=0}) {
        return new Date(year + '-' + month + '-' + day + ' ' + hour + ':' + minute + ':' + second);
    }	//如果一个函数接收一个对象作为参数,那么,可以使用解构直接把对象的属性绑定到变量中。

    闭包

    函数的返回依然是函数,嵌套函数可以访问外层函数的局部变量,从而携带了状态,模拟对象的效果。

    function lazy_sum(arr) {
        var sum = function () {
            return arr.reduce(function (x, y) {
                return x + y;
            });
        }
        return sum;
    }

    function count() {
        var arr = [];
        for (var i=1; i<=3; i++) {
            arr.push(function () {
                return i * i;
            });
        }
        return arr;
    }
    
    var results = count();
    var f1 = results[0];//16
    var f2 = results[1];//16
    var f3 = results[2];//16

    上面代码中的arr内部的i会覆盖之前入队的值,因为是同一个引用。

    返回闭包时牢记的一点就是:返回函数不要引用任何循环变量,或者后续会发生变化的变量。

    如果一定要引用循环变量怎么办?方法是再创建一个函数,用该函数的参数绑定循环变量当前的值,无论该循环变量后续如何更改,已绑定到函数参数的值不变(实质是对循环变量进行值复制,让最内层函数指向不同内存)

    function count() {
        var arr = [];
        for (var i=1; i<=3; i++) {
            arr.push((function (n) {
                return function () {
                    return n * n;
                }
            })(i));
        }
        return arr;
    }
    
    var results = count();
    var f1 = results[0];
    var f2 = results[1];
    var f3 = results[2];
    
    f1(); // 1
    f2(); // 4
    f3(); // 9

    注意这里用了一个“创建一个匿名函数并立刻执行”的语法:

    (function (x) {
        return x * x;
    })(3); /* 9,如果这么写:function (x) { return x * x } (3);由于JavaScript语法解析的问题,会报SyntaxError错误  */

    闭包就是携带状态的函数,并且它的状态可以完全对外隐藏起来。

    'use strict';
    
    function create_counter(initial) {
        var x = initial || 0;
        return {
            inc: function () {
                x += 1;
                return x;
            }
        }
    }

    它用起来像这样:

    var c1 = create_counter();
    c1.inc(); // 1
    c1.inc(); // 2
    c1.inc(); // 3
    
    var c2 = create_counter(10);
    c2.inc(); // 11
    c2.inc(); // 12
    c2.inc(); // 13
  • 相关阅读:
    bootstrap fileinput 无法显示中文bug
    js防止回车(enter)键提交表单及javascript中event.keycode
    php 生成唯一随机码
    thinksns 分页数据
    详解PHP处理密码的几种方式
    windows7 在cmd中执行php脚本
    php 无限级分类 递归+sort排序 和 非递归
    CentOS 创建SVN 服务器,并且自动同步到WEB 目录
    微擎笔记
    laravel php框架 知识点及注意问题
  • 原文地址:https://www.cnblogs.com/biwangwang/p/14263196.html
Copyright © 2011-2022 走看看