美文网首页
阮一峰ES6教程读书笔记(三)数值、函数的扩展

阮一峰ES6教程读书笔记(三)数值、函数的扩展

作者: 前端艾希 | 来源:发表于2019-08-03 22:31 被阅读0次

    About

    本文只记录了一些我比较感兴趣的新增方法,以及特性,更加详细的介绍请参考阮一峰的《ECMAScript 6 入门》

    一、数值的扩展

    1. 进制表示法

    • 0b 二进制
    • 0o 八进制
    • 0d 十进制
    • 0x 十六进制

    如果要将其它进制字符串数值转为十进制,要使用Number方法。

    Number('0b111')  // 7
    Number('0o10')  // 8
    Number(0x11) // 17
    

    2. Number.parseInt() 和 Number.parseFloat()

    ES6 将全局方法parseInt()和parseFloat(),移植到Number对象上面,行为完全保持不变。

    // ES5的写法
    parseInt('12.34') // 12
    parseFloat('123.45#') // 123.45
    
    // ES6的写法
    Number.parseInt('12.34') // 12
    Number.parseFloat('123.45#') // 123.45
    

    3. Number.isInteger()

    Number.isInteger()用来判断一个数值是否为整数。JavaScript 内部,整数和浮点数采用的是同样的储存方法,所以 25 和 25.0 被视为同一个值。

    Number.isInteger(25) // true
    Number.isInteger(25.0) // true
    Number.isInteger(25.1) // false
    

    如果传入的参数不是数值,这里不会发生自动转换,而是直接返回false

    Number.isInteger() // false
    Number.isInteger(null) // false
    Number.isInteger('15') // false
    Number.isInteger(true) // false
    

    4. Number.EPSILON

    Number对象新增了属性EPSILON,这个属性其实是一个数字即2的-52次方,用来表示JavaScript的最小精度。我们可以把它用在浮点运算中用来设置运算精度。

    function withinErrorMargin (left, right) {
      return Math.abs(left - right) < Number.EPSILON * Math.pow(2, 2) // 误差范围2的-50次方
    }
    
    0.1 + 0.2 === 0.3 // false
    withinErrorMargin(0.1 + 0.2, 0.3) // true
    
    1.1 + 1.3 === 2.4 // false
    withinErrorMargin(1.1 + 1.3, 2.4) // true
    

    二、Math对象上的扩展

    ES6 在 Math 对象上新增了 17 个与数学相关的方法。所有这些方法都是静态方法,只能在 Math 对象上调用。

    1. Math.trunc()

    Math.trunc方法用于去除一个数的小数部分,返回整数部分。

    Math.trunc(4.1) // 4
    Math.trunc(4.9) // 4
    Math.trunc(-4.1) // -4
    Math.trunc(-4.9) // -4
    Math.trunc(-0.1234) // -0
    

    如果参数不是数值,那么会先使用Number方法将其转为数值,然后在取整,如果无法转为数值,或者参数为空,就返回NaN,对于没有部署这个方法的环境,可以用下面的代码模拟。

    Math.trunc = Math.trunc || function(x) {
      return x < 0 ? Math.ceil(x) : Math.floor(x);
    };
    

    2. Math.hypot()

    Math.hypot方法返回所有参数的平方和的平方根。用来做勾股定理不要太爽~

    Math.hypot(3, 4);        // 5
    Math.hypot(3, 4, 5);     // 7.0710678118654755
    

    如果传入的参数有一个无法转为数字就会返回NaN

    3. Math.log10(),Math.log2()

    分别是求以10为底的对数和以2为底的对数,可以用以下方法替代:

    Math.log2 = Math.log2 || function(x) {
      return Math.log(x) / Math.LN2;
    };
    
    Math.log10 = Math.log10 || function(x) {
      return Math.log(x) / Math.LN10;
    };
    

    4. 新增指数运算符

    ES2016 新增了一个指数运算符(**)。

    2 ** 2 // 4
    2 ** 3 // 8
    
    let a = 1.5;
    a **= 2;
    // 等同于 a = a * a;
    
    let b = 4;
    b **= 3;
    // 等同于 b = b * b * b;
    

    三、函数的扩展

    1. 函数参数的默认值

    ES6 之前,不能直接为函数的参数指定默认值,只能采用变通的方法。比如在函数体内通过typeOf检测形参是否为空,如果为空就赋给形参默认值,这样写起来不仅代码非常臃肿且十分丑陋,其他语言都能给参数默认值,而在JavaScript中却不能,所以ES6 允许我们给函数参数传入默认值。

    function print (x, y = 'world') {
        console.log(`${x} ${y}`)
    }
    print('hello') // hello world
    

    1.1 使用参数默认值时,变量不能重名

    例如:

    function foo(x, x, y) {
      console.log(x,x,y)
    }
    foo(1,2,3) // 2 2 3
    
    function foo(x, x, y = 3) {
      console.log(x,x,y)
    }
    foo(1,2,3) // Uncaught SyntaxError: Duplicate parameter name not allowed in this context
    

    1.2 参数默认值是惰性求值的

    参数默认值不是传值的,而是每次都重新计算默认值表达式的值。

    let x = 99;
    function foo(p = x + 1) {
      console.log(p);
    }
    
    foo() // 100
    
    x = 100;
    foo() // 101
    

    形参p虽然有默认值,但是并非定义时就已经计算出来,而是等到调用时才计算该默认值。

    1.3 与解构赋值结合使用

    如果想在形参上使用结构赋值,那么必须要给他们赋一个默认值{}

    function foo({x, y}) {
      console.log(x, y)
    }
    
    foo() // 报错
    
    function foo({x, y} = {}) {
      console.log(x, y)
    }
    
    foo() // undefined undefined
    foo({x:1, z:2}) // 1 undefined
    foo({x:1, y:2}) // 1 2
    

    为进一步加深理解,我们来看这样一个例子:

    function m1({x = 0, y = 0} = {}) {
      return [x, y];
    }
    
    // 写法二
    function m2({x, y} = { x: 0, y: 0 }) {
      return [x, y];
    }
    

    写法二与一不同的地方在于:写法一中虽然也使用了解构赋值,但是在解构之前形参xy都已经有默认值为0了,即使在调用函数时只传了一个值,那么另一个值也不会为undefined而是为0;而写法二中形参xy采用了惰性求值,也就是说其并没有真正的默认值,如果调用m2时,没有传入参数,那么启动解构赋值,形参xy0,但是如果只传了1个参数,那么就不会启动形参内的解构赋值,这样的话,另一个参数就为undefined

    m1({x: 3}) // [3, 0]
    m2({x: 3}) // [3, undefined]
    
    m1({z: 3}) // [0, 0]
    m2({z: 3}) // [undefined, undefined]
    

    1.4 带有默认值的参数位置

    通常情况下,定义了默认值的参数,应该是函数的尾参数。因为这样比较容易看出来,到底省略了哪些参数。如果非尾部的参数设置默认值,实际上这个参数是没法省略的。

    1.5 函数的length属性

    函数的length返回该函数的形参数量,但不包括带有默认值的参数,总的来说就是调用该函数时必须传入多少个参数。

    (function (a) {}).length // 1
    (function (a = 5) {}).length // 0
    (function (a, b, c = 5) {}).length // 2
    

    1.5 默认参数的应用

    利用参数默认值,可以指定某一个参数不得省略,如果省略就抛出一个错误。

    function missparams () {
        throw new Error('Missing a paramater')
    }
    
    function foo (x = missparams(), y = 1) {
        console.log(x, y)
    }
    
    foo() // Error: Missing a paramater
    

    2. rest参数

    ES6 引入 rest 参数(形式为...变量名),用于获取函数的多余参数,这样就不需要使用arguments对象了。rest 参数搭配的变量是一个数组,该变量将多余的参数放入数组中。

    这其实很好理解,在其他语言中,无论是C还是Python中我们会在定义形参时为了提升函数的稳定性,会在形参后定义*args,如果有多的参数就会被放在数组中传入函数,而不会引发错误。

    function add(...values) {
      let sum = 0;
    
      for (var val of values) {
        sum += val;
      }
    
      return sum;
    }
    
    add(2, 5, 3) // 10
    

    值得注意的是,...rest只能是作为形参的最后一个参数,正如*args一样,必须放在最后,否则会报错。

    3. name属性

    这个属性早就被浏览器广泛支持,但是直到 ES6,才将其写入了标准。

    var f = function () {};
    
    // ES5
    f.name // ""
    
    // ES6
    f.name // "f"
    

    4. 箭头函数

    我们先看个例子:

    // 不使用箭头函数
    function greet () {
        console.log(`hello ${this.name}`)
    }
    
    var name = 'yan' //即 window.name = 'yan'
    person = {
        'name': 'bing',
        'greet': greet
    }
    greet()  // hello yan
    person.greet()  // hello bing
    
    //使用箭头函数
    var greet = () => console.log(`hello ${this.name}`)
    person = {
        'name': 'bing',
        'greet': greet
    }
    greet()  // hello yan
    person.greet()  // hello yan
    greet.call(person) // hello yan
    

    通过上面的例子我们可以得知箭头函数的this的指向是不会改变的,它在哪里定义,哪里就是它的执行上下文,由此我们可以得到使用箭头函数的注意点:

    (1)函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。

    (2)不可以当作构造函数,也就是说,不可以使用new命令,否则会抛出一个错误。

    (3)不可以使用arguments对象,该对象在函数体内不存在。如果要用,可以用 rest 参数代替。

    (4)不可以使用yield命令,因此箭头函数不能用作 Generator 函数。

    出现上述情况的根本原因是箭头函数没有自己的this,它的this指向外层的this

    不适用的场合:

    1. 定义对象的方法:
    const cat = {
      lives: 9,
      jumps: () => {
        this.lives--;
      }
    }
    

    因为对象不构成单独的作用域,导致jumps箭头函数定义时的作用域就是全局作用域。

    1. 需要动态this的时候:
    var button = document.getElementById('press');
    button.addEventListener('click', () => {
      this.classList.toggle('on');
    });
    

    因为button的监听函数是一个箭头函数,导致里面的this就是全局对象。如果改成普通函数,this就会动态指向被点击的按钮对象。

    5. Function.prototype.toString()

    toString()方法返回函数代码本身,以前会省略注释和空格。

    function /* foo comment */ foo () {}
    
    foo.toString()
    // "function /* foo comment */ foo () {}"
    

    参考链接

    作者:阮一峰
    链接:http://es6.ruanyifeng.com/#docs/destructuring

    相关文章

      网友评论

          本文标题:阮一峰ES6教程读书笔记(三)数值、函数的扩展

          本文链接:https://www.haomeiwen.com/subject/vzisdctx.html