文章出處

前面的話

  本文將詳細介紹ES6數字擴展

 

指數運算符

  ES2016引入的唯一一個JS語法變化是求冪運算符,它是一種將指數應用于基數的數學運算。JS已有的Math.pow()方法可以執行求冪運算,但它也是為數不多的需要通過方法而不是正式的運算符來進行求冪

  求冪運算符是兩個星號(**)左操作數是基數,右操作數是指數

let result = 5 ** 2;
console.log(result) // 25
console.log(result === Math.pow(5,2) ) // true

  指數運算符可以與等號結合,形成一個新的賦值運算符(**=

let a = 1.5;
a **= 2;
// 等同于 a = a * a;

let b = 4;
b **= 3;
// 等同于 b = b * b * b;

  [注意]在 V8 引擎中,指數運算符與Math.pow的實現不相同,對于特別大的運算結果,兩者會有細微的差異

Math.pow(99, 99) // 3.697296376497263e+197

99 ** 99 // 3.697296376497268e+197

【運算順序】

  求冪運算符具有JS中所有二進制運算符的優先級(一元運算符的優先級高于**),這意味著它首先應用于所有復合操作

let result = 2 * 5 ** 2
console.log(result) // 50

  先計算52,然后將得到的值乘以2,最終結果為50

【運算限制】

  取冪運算符確實有其他運算符沒有的一些不尋常的限制,它左側的一元表達式只能使用++或--

//語法錯誤
let result =-5 ** 2

  此示例中的-5的寫法是一個語法錯誤,因為運算的順序是不明確的。-是只適用于5呢,還是適用于表達式5**2的結果?禁用求冪運算符左側的二元表達式可以消除歧義。要明確指明意圖,需要用括號包裹-5或5**2

//可以包裹5**2
let result1 =-(5 ** 2) //-25

//也可以包裹-5
let result2 = (-5) ** 2 // 等于25

  如果在表達式兩端放置括號,則-將應用于整個表達式;如果在-5兩端放置括號,則表明想計算-5的二次幕

  在求幕運算符左側無須用括號就可以使用++和--,因為這兩個運算符都明確定義了作用于操作數的行為。前綴++或--會在其他所有操作發生之前更改操作數,而后綴版本直到整個表達式被計算過后才會進行改變。這兩個用法在運算付左側都是安全的

let num1 = 2,
    num2 = 2;
console.log(++num1 ** 2) // 9
console.log(num1) // 3
console.log(num2--** 2) // 4
console.log(num2) // 1

  在這個示例中,num1在應用取冪運算符之前先加1,所以num1變為3,運算結果為9;而num2取冪運算的值保持為2,之后再減1

 

不同進制

  ES6 提供了二進制和八進制數值的新的寫法,分別用前綴0b(或0B)和0o(或0O)表示

0b111110111 === 503 // true
0o767 === 503 // true

  從 ES5 開始,在嚴格模式之中,八進制就不再允許使用前綴0表示,ES6 進一步明確,要使用前綴0o表示

// 非嚴格模式
(function(){
  console.log(0o11 === 011);
})() // true

// 嚴格模式
(function(){
  'use strict';
  console.log(0o11 === 011);
})() // Uncaught SyntaxError: Octal literals are not allowed in strict mode.

  如果要將0b0o前綴的字符串數值轉為十進制,要使用Number方法

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

 

Number方法

  ES6 在Number對象上,新提供了Number.isFinite()Number.isNaN()兩個方法

Number.isFinite()

  Number.isFinite()用來檢查一個數值是否為有限的(finite)

console.log( Number.isFinite(15)); // true
console.log( Number.isFinite(0.8)); // true
console.log( Number.isFinite(NaN)); // false
console.log( Number.isFinite(Infinity)); // false
console.log( Number.isFinite(-Infinity)); // false
console.log( Number.isFinite('foo')); // false
console.log( Number.isFinite('15')); // false
console.log( Number.isFinite(true)); // false

  與原有的isFinite()方法的不同之處在于,Number.isFinite()方法沒有隱式的Number()類型轉換,對于非數值一律返回false

console.log(isFinite(15)); // true
console.log(isFinite(0.8)); // true
console.log(isFinite(NaN)); // false
console.log(isFinite(Infinity)); // false
console.log(isFinite(-Infinity)); // false
console.log(isFinite('foo')); // false
console.log(isFinite('15')); // true
console.log(isFinite(true)); // true

  ES5 可以通過下面的代碼,部署Number.isFinite方法

(function (global) {
  var global_isFinite = global.isFinite;

  Object.defineProperty(Number, 'isFinite', {
    value: function isFinite(value) {
      return typeof value === 'number' && global_isFinite(value);
    },
    configurable: true,
    enumerable: false,
    writable: true
  });
})(this);

Number.isNaN()

  Number.isNaN()用來檢查一個值是否為NaN

console.log(Number.isNaN('true')); //false
console.log(Number.isNaN('hello')); //false
console.log(Number.isNaN(NaN)); // true
console.log(Number.isNaN(15)); // false
console.log(Number.isNaN('15')); // false
console.log(Number.isNaN(true)); // false
console.log(Number.isNaN('true'/0)); // true

  與原有的isNaN()方法不同,不存在隱式的Number()類型轉換,非NaN一律返回false

console.log(isNaN('true')); //true
console.log(isNaN('hello')); //true
console.log(isNaN(NaN)); // true
console.log(isNaN(15)); // false
console.log(isNaN('15')); // false
console.log(isNaN(true)); // false
console.log(isNaN('true'/0)); // true

  ES6 將全局方法parseInt()parseFloat(),移植到Number對象上面,行為完全保持不變

【parseInt()】

// ES5的寫法
parseInt('12.34') // 12
parseFloat('123.45#') // 123.45

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

  這樣做的目的,是逐步減少全局性方法,使得語言逐步模塊化

Number.parseInt === parseInt // true
Number.parseFloat === parseFloat // true

Number.isInteger()

  Number.isInteger()用來判斷一個值是否為整數。需要注意的是,在JS內部,整數和浮點數是同樣的儲存方法,所以3和3.0被視為同一個值

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

  ES5 可以通過下面的代碼,部署Number.isInteger()

(function (global) {
  var floor = Math.floor,
    isFinite = global.isFinite;

  Object.defineProperty(Number, 'isInteger', {
    value: function isInteger(value) {
      return typeof value === 'number' &&
        isFinite(value) &&
        floor(value) === value;
    },
    configurable: true,
    enumerable: false,
    writable: true
  });
})(this);

 

Number常量

Number.EPSILON】 

  ES6在Number對象上面,新增一個極小的常量Number.EPSILON

Number.EPSILON// 2.220446049250313e-16
Number.EPSILON.toFixed(20)// '0.00000000000000022204'

  引入一個這么小的量的目的,在于為浮點數計算,設置一個誤差范圍

0.1 + 0.2// 0.30000000000000004

0.1 + 0.2 - 0.3// 5.551115123125783e-17

5.551115123125783e-17.toFixed(20)// '0.00000000000000005551'

  但是如果這個誤差能夠小于Number.EPSILON,我們就可以認為得到了正確結果

5.551115123125783e-17 < Number.EPSILON // true

  因此,Number.EPSILON的實質是一個可以接受的誤差范圍

function withinErrorMargin (left, right) {
  return Math.abs(left - right) < Number.EPSILON;
}
withinErrorMargin(0.1 + 0.2, 0.3)// true
withinErrorMargin(0.2 + 0.2, 0.3)// false

  上面的代碼為浮點數運算,部署了一個誤差檢查函數

【安全整數】

  JS能夠準確表示的整數范圍在-2^532^53之間(不含兩個端點),超過這個范圍,無法精確表示這個值

Math.pow(2, 53) // 9007199254740992

9007199254740992  // 9007199254740992
9007199254740993  // 9007199254740992

Math.pow(2, 53) === Math.pow(2, 53) + 1 // true

  上面代碼中,超出2的53次方之后,一個數就不精確了

【Number.MAX_SAFE_INTEGER、Number.MIN_SAFE_INTEGER】

  ES6引入了Number.MAX_SAFE_INTEGERNumber.MIN_SAFE_INTEGER這兩個常量,用來表示這個范圍的上下限

Number.MAX_SAFE_INTEGER === Math.pow(2, 53) - 1 // true
Number.MAX_SAFE_INTEGER === 9007199254740991 // true

Number.MIN_SAFE_INTEGER === -Number.MAX_SAFE_INTEGER // true
Number.MIN_SAFE_INTEGER === -9007199254740991 // true

  上面代碼中,可以看到JS能夠精確表示的極限

Number.isSafeInteger()

  Number.isSafeInteger()則是用來判斷一個整數是否落在這個范圍之內

Number.isSafeInteger('a') // false
Number.isSafeInteger(null) // false
Number.isSafeInteger(NaN) // false
Number.isSafeInteger(Infinity) // false
Number.isSafeInteger(-Infinity) // false

Number.isSafeInteger(3) // true
Number.isSafeInteger(1.2) // false
Number.isSafeInteger(9007199254740990) // true
Number.isSafeInteger(9007199254740992) // false

Number.isSafeInteger(Number.MIN_SAFE_INTEGER - 1) // false
Number.isSafeInteger(Number.MIN_SAFE_INTEGER) // true
Number.isSafeInteger(Number.MAX_SAFE_INTEGER) // true
Number.isSafeInteger(Number.MAX_SAFE_INTEGER + 1) // false

  這個函數的實現很簡單,就是跟安全整數的兩個邊界值比較一下

Number.isSafeInteger = function (n) {
  return (typeof n === 'number' &&
    Math.round(n) === n &&
    Number.MIN_SAFE_INTEGER <= n &&
    n <= Number.MAX_SAFE_INTEGER);
}

  實際使用這個函數時,需要注意驗證運算結果是否落在安全整數的范圍內,不要只驗證運算結果,而要同時驗證參與運算的每個值

Number.isSafeInteger(9007199254740993) // false
Number.isSafeInteger(990) // true
Number.isSafeInteger(9007199254740993 - 990)  // true

9007199254740993 - 990
// 返回結果 9007199254740002
// 正確答案應該是 9007199254740003

  上面代碼中,9007199254740993不是一個安全整數,但是Number.isSafeInteger會返回結果,顯示計算結果是安全的。這是因為,這個數超出了精度范圍,導致在計算機內部,以9007199254740992的形式儲存

9007199254740993 === 9007199254740992 // true

  所以,如果只驗證運算結果是否為安全整數,很可能得到錯誤結果。下面的函數可以同時驗證兩個運算數和運算結果

function trusty (left, right, result) {
  if (
    Number.isSafeInteger(left) &&
    Number.isSafeInteger(right) &&
    Number.isSafeInteger(result)
  ) {
    return result;
  }
  throw new RangeError('Operation cannot be trusted!');
}
// RangeError: Operation cannot be trusted!
trusty(9007199254740993, 990, 9007199254740993 - 990)

trusty(1, 2, 3)// 3

 

Math對象

  ES6在Math對象上新增了17個與數學相關的方法。所有這些方法都是靜態方法,只能在Math對象上調用

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

  對于非數值,Math.trunc內部使用Number方法將其先轉為數值

Math.trunc('123.456')// 123

  對于空值和無法截取整數的值,返回NaN

Math.trunc(NaN);      // NaN
Math.trunc('foo');    // NaN
Math.trunc();         // NaN

  對于沒有部署這個方法的環境,可以用下面的代碼模擬

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

Math.sign

  Math.sign方法用來判斷一個數到底是正數、負數、還是零。對于非數值,會先將其轉換為數值

  它會返回以下五種值

參數為正數,返回+1;
參數為負數,返回-1;
參數為0,返回0;
參數為-0,返回-0;
其他值,返回NaN。
Math.sign(-5) // -1
Math.sign(5) // +1
Math.sign(0) // +0
Math.sign(-0) // -0
Math.sign(NaN) // NaN
Math.sign('9'); // +1
Math.sign('foo'); // NaN
Math.sign();      // NaN

  對于沒有部署這個方法的環境,可以用下面的代碼模擬

Math.sign = Math.sign || function(x) {
  x = +x; // convert to a number
  if (x === 0 || isNaN(x)) {
    return x;
  }
  return x > 0 ? 1 : -1;
};

Math.cbrt

  Math.cbrt方法用于計算一個數的立方根

Math.cbrt(-1) // -1
Math.cbrt(0)  // 0
Math.cbrt(1)  // 1
Math.cbrt(2)  // 1.2599210498948734

  對于非數值,Math.cbrt方法內部也是先使用Number方法將其轉為數值

Math.cbrt('8') // 2
Math.cbrt('hello') // NaN

  對于沒有部署這個方法的環境,可以用下面的代碼模擬

Math.cbrt = Math.cbrt || function(x) {
  var y = Math.pow(Math.abs(x), 1/3);
  return x < 0 ? -y : y;
};

Math.clz32

  JS的整數使用32位二進制形式表示,Math.clz32方法返回一個數的32位無符號整數形式有多少個前導0

Math.clz32(0) // 32
Math.clz32(1) // 31
Math.clz32(1000) // 22
Math.clz32(0b01000000000000000000000000000000) // 1
Math.clz32(0b00100000000000000000000000000000) // 2

  上面代碼中,0的二進制形式全為0,所以有32個前導0;1的二進制形式是0b1,只占1位,所以32位之中有31個前導0;1000的二進制形式是0b1111101000,一共有10位,所以32位之中有22個前導0

  左移運算符(<<)與Math.clz32方法直接相關

Math.clz32(0) // 32
Math.clz32(1) // 31
Math.clz32(1 << 1) // 30
Math.clz32(1 << 2) // 29
Math.clz32(1 << 29) // 2

  對于小數,Math.clz32方法只考慮整數部分

Math.clz32(3.2) // 30
Math.clz32(3.9) // 30

  對于空值或其他類型的值,Math.clz32方法會將它們先轉為數值,然后再計算

Math.clz32() // 32
Math.clz32(NaN) // 32
Math.clz32(Infinity) // 32
Math.clz32(null) // 32
Math.clz32('foo') // 32
Math.clz32([]) // 32
Math.clz32({}) // 32
Math.clz32(true) // 31

Math.imul

  Math.imul方法返回兩個數以32位帶符號整數形式相乘的結果,返回的也是一個32位的帶符號整數

Math.imul(2, 4)   // 8
Math.imul(-1, 8)  // -8
Math.imul(-2, -2) // 4

  如果只考慮最后32位,大多數情況下,Math.imul(a, b)a * b的結果是相同的,即該方法等同于(a * b)|0的效果(超過32位的部分溢出)。之所以需要部署這個方法,是因為JS有精度限制,超過2的53次方的值無法精確表示。這就是說,對于那些很大的數的乘法,低位數值往往都是不精確的,Math.imul方法可以返回正確的低位數值

(0x7fffffff * 0x7fffffff)|0 // 0

  上面這個乘法算式,返回結果為0。但是由于這兩個二進制數的最低位都是1,所以這個結果肯定是不正確的,因為根據二進制乘法,計算結果的二進制最低位應該也是1。這個錯誤就是因為它們的乘積超過了2的53次方,JS無法保存額外的精度,就把低位的值都變成了0。Math.imul方法可以返回正確的值1

Math.imul(0x7fffffff, 0x7fffffff) // 1

【Math.fround】

  Math.fround方法返回一個數的單精度浮點數形式

Math.fround(0)     // 0
Math.fround(1)     // 1
Math.fround(1.337) // 1.3370000123977661
Math.fround(1.5)   // 1.5
Math.fround(NaN)   // NaN

  對于整數來說,Math.fround方法返回結果不會有任何不同,區別主要是那些無法用64個二進制位精確表示的小數。這時,Math.fround方法會返回最接近這個小數的單精度浮點數

  對于沒有部署這個方法的環境,可以用下面的代碼模擬

Math.fround = Math.fround || function(x) {
  return new Float32Array([x])[0];
};

Math.hypot

  Math.hypot方法返回所有參數的平方和的平方根

Math.hypot(3, 4);        // 5
Math.hypot(3, 4, 5);     // 7.0710678118654755
Math.hypot();            // 0
Math.hypot(NaN);         // NaN
Math.hypot(3, 4, 'foo'); // NaN
Math.hypot(3, 4, '5');   // 7.0710678118654755
Math.hypot(-3);          // 3

  上面代碼中,3的平方加上4的平方,等于5的平方

  如果參數不是數值,Math.hypot方法會將其轉為數值。只要有一個參數無法轉為數值,就會返回NaN

  ES6新增了4個對數相關方法

Math.expm1

  Math.expm1(x)返回ex - 1,即Math.exp(x) - 1

Math.expm1(-1) // -0.6321205588285577
Math.expm1(0)  // 0
Math.expm1(1)  // 1.718281828459045

  對于沒有部署這個方法的環境,可以用下面的代碼模擬

Math.expm1 = Math.expm1 || function(x) {
  return Math.exp(x) - 1;
};

Math.log1p(x)

  Math.log1p(x)方法返回1 + x的自然對數,即Math.log(1 + x)。如果x小于-1,返回NaN

Math.log1p(1)  // 0.6931471805599453
Math.log1p(0)  // 0
Math.log1p(-1) // -Infinity
Math.log1p(-2) // NaN

  對于沒有部署這個方法的環境,可以用下面的代碼模擬

Math.log1p = Math.log1p || function(x) {
  return Math.log(1 + x);
};

Math.log10(x)

  Math.log10(x)返回以10為底的x的對數。如果x小于0,則返回NaN

Math.log10(2)      // 0.3010299956639812
Math.log10(1)      // 0
Math.log10(0)      // -Infinity
Math.log10(-2)     // NaN
Math.log10(100000) // 5

  對于沒有部署這個方法的環境,可以用下面的代碼模擬

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

【Math.log2(x)】

  Math.log2(x)返回以2為底的x的對數。如果x小于0,則返回NaN

Math.log2(3)       // 1.584962500721156
Math.log2(2)       // 1
Math.log2(1)       // 0
Math.log2(0)       // -Infinity
Math.log2(-2)      // NaN
Math.log2(1024)    // 10
Math.log2(1 << 29) // 29

  對于沒有部署這個方法的環境,可以用下面的代碼模擬

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

  ES6新增了6個雙曲函數方法

Math.sinh(x) 返回x的雙曲正弦(hyperbolic sine)
Math.cosh(x) 返回x的雙曲余弦(hyperbolic cosine)
Math.tanh(x) 返回x的雙曲正切(hyperbolic tangent)
Math.asinh(x) 返回x的反雙曲正弦(inverse hyperbolic sine)
Math.acosh(x) 返回x的反雙曲余弦(inverse hyperbolic cosine)
Math.atanh(x) 返回x的反雙曲正切(inverse hyperbolic tangent)

 


文章列表


不含病毒。www.avast.com
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()