摘要:情況一情況二這兩種情況,根據的規定都是非法的。的作用域與命令相同只在聲明所在的塊級作用域內有效。因此,將一個對象聲明為常量必須非常小心。頂層對象的屬性與全局變量掛鉤,被認為時語言最大的設計敗筆之一。
這是ES6的入門篇教程的筆記,網址:鏈接描述,以下內容中粗體+斜體表示大標題,粗體是小標題,還有一些重點;斜體表示對于自身,還需要下功夫學習的內容。這里面有一些自己的見解,所以若是發現問題,歡迎指出~
上一篇es5的到最后令人崩潰,看來深層的東西還是不太熟,希望這次不要這樣了!!!
ECMAScript 6簡介
Babel轉碼器
以前構建Vue-cli的時候一直不明白為什么要添加babel的依賴,現在才知道。。。。
Babel是一個廣泛使用的ES6轉碼器,可以將ES6代碼轉為ES5代碼,從而在現有環境執行。這意味著,你可以用ES6的方式編寫程序,又不用擔心現有環境是否支持。
let 和 const 命令
1.let命令
基本用法
ES6新增了let命令,用來聲明變量。它的用法類似于var,但是所聲明的變量,只在let命令所在的代碼塊內有效。
{ let a = 10; var b = 1; } a // ReferenceError: a is not defined. b // 1 // so for循環的計數器,就很合適使用let命令 for (let i = 0; i < 10; i++) { // ... } console.log(i); // ReferenceError: i is not defined
有一個重大發現!for循環還有一個特別之處,就是設置循環變量的那部分是一個父作用域,而循環體內部是一個多帶帶的子作用域。這是以前沒注意的!!
for (let i = 0; i < 3; i++) { let i = "abc"; console.log(i); } // abc // abc // abc 輸出3次abc,這表明函數內部的變量i與循環變量i不在同一個作用域,有各自多帶帶的作用域。
不存在變量提升
var命令會發生“變量提升”現象,即變量可以在聲明之前使用,值為undefined;而let命令糾正了這種現象,它所聲明的變量一定要在聲明后使用,否則報錯。
// var 的情況 console.log(foo); // 輸出undefined var foo = 2; // let 的情況 console.log(bar); // 報錯ReferenceError let bar = 2;
暫時性死區
只要塊級作用域內存在let命令,它所聲明的變量就“綁定”(binding)這個區域,不再受外部的影響。
ES6明確規定,如果區塊中存在let和const命令,這個區塊對這些命令聲明的變量,從一開始就形成了封閉作用域。
var tmp = 123; if (true) { tmp = "abc"; // ReferenceError let tmp; // 在塊級作用域內又聲明了一個局部變量tmp,tmp綁定這個塊級作用域,凡是在聲明之前就使用這些變量,就會報錯。 }
總之,在代碼塊內,使用let命令聲明變量之前,該變量都是不可用的,這在語法上,稱為“暫時性死區”(temporal dead zone, 簡稱TDZ)。
if (true) { // TDZ開始 tmp = "abc"; // ReferenceError console.log(tmp); // ReferenceError let tmp; // TDZ結束 console.log(tmp); // undefined tmp = 123; console.log(tmp); // 123 }
不允許重復聲明
let不允許在相同作用域內,重復聲明同一個變量。
// 報錯 function func() { let a = 10; var a = 1; } // 報錯 function func() { let a = 10; let a = 1; }
2.塊級作用域
為什么需要塊級作用域?
ES5只有全局作用域和函數作用域,沒有塊級作用域,這帶來很多不合理的場景。
第一種場景,內層變量可能會覆蓋外層變量。
var tmp = new Date(); function f() { console.log(tmp); // 原意是調用外層的tmp變量 if (false) { var tmp = "hello world"; } } f(); // undefined 變量提升,導致內層的tmp變量覆蓋了外層的tmp變量
第二種場景,用來計數的循環變量泄露為全局變量。
var s = "hello"; for (var i = 0; i < s.length; i++) { console.log(s[i]); } console.log(i); // 5 變臉i只用來控制循環,但是循環結束后,它并沒有消失,泄露成了全局變量。
ES6的塊級作用域
let實際上為JavaScript新增了塊級作用域。
function f1() { let n = 5; if (true) { let n = 10; } console.log(n); // 5 }
塊級作用域與函數聲明
函數能不能在塊級作用域之中聲明?這是一個相當令人混淆的問題。
ES5規定,函數只能在頂層作用域和函數作用域之中聲明,不能在塊級作用域聲明。
// 情況一 if (true) { function f() {} } // 情況二 try { function f() {} } catch(e) { // ... } // 這兩種情況,根據ES5的規定都是非法的。
3.const命令
基本用法
const聲明一個只讀的常量。一旦聲明,常量的值就不能改變。
const聲明的變量不得改變值,這意味著,const一旦聲明變量,就必須立即初始化,不能留到以后賦值。
const foo; // SyntaxError: Missing initializer in const declaration 對于const來說,只聲明不賦值,就會報錯。
const的作用域與let命令相同:只在聲明所在的塊級作用域內有效。
const實際上保證的,并不是變量的值不得改動,而是變量指向的那個內存地址所保存的數據不得改動。對于簡單類型的數據(數值、字符串、布爾值),值就保存在變量指向的那個內存地址,因此等同于常量。但對于復合雷公的數據(主要是對象和數組),變量指向的內存地址,保存的只是一個指向實際數據的指針,const只能保證這個指針是固定的(即總是指向另一個固定的地址),至于它指向的數據結構是不是可變的,就完全不能控制了。因此,將一個對象聲明為常量必須非常小心。
const foo = {}; // foo儲存的是一個地址,這個地址指向一個對象,不可以變得只是這個地址,即不能把foo指向另一個地址,但對象本身是可變得,所以可以為其添加新屬性。 // 為foo添加一個屬性,可以成功 foo.prop = 123; foo.prop // 123 // 將foo指向另一個對象,就會報錯 foo = {}; // TypeError: "foo" is ready-only
如果真的想將對象凍結,應該使用Object.freeze方法。
const foo = Object.freeze({}); // 常量foo指向一個凍結得對象,所以下面得添加新屬性不起作用,嚴格模式時還會報錯。 // 常規模式時,下面一行不起作用; // 嚴格模式時,該行會報錯 foo.prop = 123; foo.prop // undefined
ES6聲明變量得六種方法
ES5只有兩種聲明變量得方法:var命令和function命令。ES6除了添加let和const命令,還有另外兩種聲明變量的方法:import命令和class命令。所以ES6一共有6種聲明變量的方法。
4.頂層對象的屬性
頂層對象,在瀏覽器環境指的是window對象,在Node指的是global對象。ES5之中,頂層對象的屬性與全局變量時等價的。
頂層對象的屬性與全局變量掛鉤,被認為時JavaScript語言最大的設計敗筆之一。
window.a = 1; a // 1 a = 2; window.a // 2 // 以上代碼表示頂層對象的屬性賦值與全局變量的賦值,是同一件事。會帶來以下三個問題: // 1)沒法再編譯時就報出變量未聲明的錯誤,只有運行時才能知道(因為全局變量可能是頂層兌現的屬性創造的,而屬性的創造是動態的)。 // 2)程序員很容易不知不覺地就創建了全局變量 // 3)頂層對象的屬性是到處可以讀寫的,這非常不利于模塊化編程 // 而且window對象有實體含義,指的是瀏覽器的窗口對象,頂層對象是一個有尸體含義的對象。
ES6為了保持兼容性,規定,var命令和function命令聲明的全局變量,依舊是頂層對象的屬性;另一方面規定,let命令、const命令、class命令聲明的全局變量,不屬于頂層對象的屬性。也就是說,從ES6開始,全局變量將逐步與頂層對象的熟悉那個脫鉤。
var a = 1; window.a // 1 let b = 1; window.b // undefined
變量的解構賦值
1.數組的解構賦值
基本用法
ES6允許按照一定模式,從數組和對象中提取值,對變量進行賦值,這被成為解構(Destructuring)。
let a = 1; let b = 2; let c = 3; // 這是以前為變量賦值,只能直接指定值 // ES6允許寫成下面這樣 let [a, b, c] = [1, 2, 3]; // 表示可以從數組中提取值,按照對應位置,對變量賦值。
上面這種寫法,本質上,是屬于“模式匹配”,只要等號兩邊的模式相同,左邊的變量就會被賦予對應的值。下面是一些使用嵌套數組進行解構的例子。
let [foo, [[bar], baz]] = [1, [[2], 3]]; foo // 1 bar // 2 baz // 3 let [,, third] = ["foo", "bar", "baz"]; third // "baz" let [head, ...tail] = [1, 2, 3, 4]; head // 1 tail // [2, 3, 4] let [x, y, ...z] = ["a"]; x // "a" y // undefined z // [] // 如果解構不成功,變量的值就等于undefined let [foo] = []; let [bar, foo] = [1]; // 兩種情況都屬于解構不成功,foo的值都會等于undefined
默認值
解構賦值允許指定默認值。
let [foo = true] = []; foo // true let [x, y = "b"] = ["a"]; // x="a"", y="b" let [x, y = "b"] = ["a", undefined]; // x="a", y="b"
ES6內部使用嚴格相等運算符(===),判斷一個位置是否有值,所以,只有當一個數組成員嚴格等于undefined。默認值才會生效,如下:
let [x = 1] = [undefined]; x // 1 let [x = 1] = [null]; x // null 默認值沒有生效,因為null不嚴格等于undefined
如果默認值是一個表達式,那么這個表達式是惰性求值的,即只有在用到的時候,才會求值。
function f() { console.log("aaa"); } let [x = f()] = [1]; // 因為x能取到值,所以函數f根本不會執行,等價于下面的代碼 let x; if ([1][0] === undefined) { x = f(); } else { x = [1][0]; }
默認值可以應用解構賦值的其他變量,但該變量必須已經聲明。
let [x = 1, y = x] = []; // x=1; y=1 let [x = 1, y = x] = [2]; // x=2; y=2 x=2是因為[2][0]不是undefined let [x = 1, y = x] = [1, 2]; // x=1;y=2 let [x = y, y = 1] = []; // ReferenceError: y is not defined 因為x用y做默認值時,y還沒有聲明
2.對象的解構賦值
簡介
解構不僅可以用于數組,還可以用于對象。
對象的解構與數組有一個重要的不同。數組的元素是按次序排列的,變量的取值由它的位置決定;而對象的屬性沒有次序,變量必須與屬性同名,才能取到正確的值。
let { bar, foo } = { foo: "aaa", bar: "bbb" }; foo // "aaa" bar // "bbb" let { baz } = { foo: "aaa", bar: "bbb" }; baz // undefined
對象的解構賦值,可以很方便地將現有對象的方法,賦值到某個變量。
let { log, sin, cos } = Math; // 將Math對象的對數、正弦、余弦三個方法,賦值到對應的變量上,使用起來就會方便很多 const { log } = console; // 將console.log賦值到log變量 log("hello") // hello // 如果變量名與屬性名不一致,必須寫成下面這樣 let { foo: baz } = { foo: "aaa", bar: "bbb" }; baz // "aaa" // 以上說明,對象的解構賦值是下面形式的簡寫。也就是說,對象的解構賦值的內部機制,是先找到同名屬性,然后再賦給對應的變量。**真正被賦值的是后者,而不是前者。** let { foo: foo, bar: bar} = { foo: "aaa", bar: "bbb"}; let { foo: baz } = { foo: "aaa", bar: "bbb" }; baz // "aaa" foo是匹配的模式,baz才是變量。真正被賦值的是變量baz,而不是模式foo。 foo // error: foo is not defined // 與數組一樣,解構也可以用于嵌套解構的對象。 let obj = { p: [ "Hello", { y: "World" } ] }; let { p: [x, { y }] } = obj; x // "Hello" y // "World" p // error: p is not defined 因為p是模式,不是變量,不會被賦值 let obj = { p: [ "Hello", { y: "World" } ] }; let { p } = obj; // 這時p是變量 p // ["Hello", {y: "World"}] x // error: x is not defined y // error: y is not defined // 如果想要p、x、y同時賦值 let obj = { p: [ "Hello", { y: "World" } ] }; let { p, p: [x, { y }] } = obj; // 第一個p是變量,可以將p賦值;第二個p是模式,將x、y賦值,因為p已經有值了,所以從p中獲取(一般情況下,冒號‘:’表示模式)
如果解構模式是嵌套的對象,而且子對象所在的父屬性不存在,那么將會報錯。
// 報錯 let {foo: {bar}} = {baz: "baz"} // 等號左邊對象的foo屬性,對應一個子對象。該子對象的bar屬性,解構時會報錯。這是因為foo此時等于undefined,再取子屬性就會報錯
注意點
(1)如果要將一個已經聲明的變量用于解構賦值,必須非常小心。
// 錯誤的寫法 let x; {x} = {x: 1} // SyntaxError: syntex error
代碼的寫法報錯,時因為JavaScript引擎會將{x}理解成一個代碼塊,從而發生語法錯誤。只有不將大括號寫在行首,避免JavaScript將其解釋為代碼塊,才能解決這個問題。
// 正確的寫法 let x; ({x} = {x: 1});
(2)解構賦值允許等號左邊的模式之中,不防止任何變量名。因此,可以寫出非常古怪的賦值表達式。
({} = [true, false]); ({} = "abc"); ({} = []); // 上面的表達式雖然毫無意義,但是語法是合法的,可以執行。
3.字符串的解構賦值
字符串也可以解構賦值,這是因為此時,字符串被轉換成了一個類似數組的對象。
const [a, b, c, d, e] = "hello"; a // "h" b // "e" c // "l" d // "l" e // "o"
類似數組的對象都有一個length屬性,因此還可以對這個屬性解構賦值。
let {length: len} = "hello"; len // 5
4.數值和布爾值的解構賦值
解構賦值時,如果等號右邊事數值和布爾值,則會先轉為對象。
解構賦值的規則是,只要等號右邊的值不是對象或數組,就先將其傳為對象。由于undefined和null無法轉為對象,所以對它們進行解構賦值,都會報錯。
let {toString: s} = 123; s === Number.prototype.toString // true *那s可以干什么嗎?表示疑惑* let { prop: x } = undefined; // TypeError let { prop: y } = null; // TypeError
5.函數參數的解構賦值
函數的參數也可以使用解構賦值。
function add([x, y]) { // 表面上,該函數的參數是一個數組 return x + y; } add([1, 2]); // 3 傳入參數的那一刻,數組參數就被解構成變量x和y // so on [[1, 2], [3, 4]].map(([a, b]) => a + b); // [3, 7] // 函數參數的解構也可以使用默認值 function move({x = 0, y = 0} = {}) { // 后面加"={}",就是防止什么都沒傳時,默認為{} return [x, y]; } move({x: 3}); // [3, 0] move({}); // [0, 0] move(); // [0, 0] // 不信看下面的,哈哈哈 function move({x, y} = {x: 0, y: 0}) { // 這是給move的參數指定默認值,而不是為變量x和y指定默認值 return [x, y]; } move({x: 3, y: 8}); // [3, 8] move({x: 3}); // [3, undefined] move({}); // [undefined, undefined] move(); // [0, 0] // undefined會觸發函數參數的默認值 [1, undefined, 3].map((x = "yes") => x); // [1, "yes", 3]
6.圓括號問題
解構賦值雖然很方便,但是解析起來并不容易。對于編譯器來說,一個式子到底是模式,還是表達式,沒有辦法從一開始就知道,必須解析到(或解析不到)等號才能知道。
由此帶來的問題市,如果模式中出現圓括號怎么處理。ES6的規則是,只要有可能導致解構的歧義,就不得使用圓括號。
但是,這條規則實際上不那么容易辨別,處理起來相當麻煩。因此, 建議只要有可能,就不要在模式中放置圓括號。
不能使用圓括號的情況
1)變量聲明語句
// 全部報錯 它們都是變量聲明語句,模式不能使用圓括號。 let [(a)] = [1]; let {x: (c)} = {}; let ({x: c}) = {}; let {(x: c)} = {}; let {(x): c} = {};
2)函數參數
函數參數也屬于變量聲明,因此不能帶有圓括號。
// 報錯 function f([(z)]) { return z; } function f([z, (x)]) { return x; }
3)賦值語句的模式
// 報錯 ({ p: a}) = {p: 42}; ([a]) = [5]; [({ p: a }), { x: c }] = [{}, {}]; // 但是這樣是可以的 ({p: a} = {p: 42});
可以使用圓括號的情況只有一種:賦值語句的非模式部分,可以使用圓括號。
下面三行語句都可以正確執行,因為首先它們都是賦值語句,而不是聲明語句;其次它們的圓括號都不熟模式的一部分。第一行語句中,模式是取數組的第一個成員,跟圓括號無關;第二行語句中,模式是p,而不是d;第三行語句與第一行語句的性質一致。
[(b)] = [3]; // 正確 ({p: (d)} = {}); // 正確 ({(parseInt.prop)] = [3]; // 正確
7.用途
變量的解構賦值用途很多。
1)交換變量的值
let x = 1; let y = 2; [x, y] = [y, x]; // 這個是數組賦值,不像對象賦值一樣去找對應的名字
2)從函數返回多個值
函數只能返回一個值,如果要返回多個值,只能將它們放在數組或對象里返回。
// 返回一個數組 function example() { return [1, 2, 3]; } let [a, b , c] = example(); // 返回一個對象 function example() { return { foo: 1, bar: 2 }; } let { foo, bar } = example();
3)函數參數的定義
解構賦值可以方便地將一組參數與變量名對應起來
// 參數是一組有次序的值 function f([x, y, z]) {...} f([1, 2, 3]); // 參數是一組無次序的值 function f({x, y, z}) {...} f({z: 3, y: 2, x: 1});
4)提取JSON數據
解構賦值對提取JSON對象中的數據,尤其有用。
let jsonData = { id: 42, status: "OK", data: [867, 5309] }; let { id, status, data: number } = jsonData; console.log(id, status, number); // 42, "OK", [867, 5309]
5)函數參數的默認值
jQuery.ajax = function (url, { async = true, beforeSend = function () {}, cache = true, complete = function () {}, crossDomain = false, global = true, // ... more config } = {}) { // ... do stuff };
6)遍歷Map結構
之前知道有map遍歷,現在又出來了Map結構,要好好區分,不然就暈了。
任何部署了Interator接口的對象,都可以用for...of循環遍歷。Map結構原生支持Interator接口,配合變量的解構賦值,獲取鍵名和鍵值就非常方便。
百度了一下,map與其他鍵值對集合的區別,發現map的“key”范圍不僅限于字符串,而是各種類型的值都可以當作key。也就是說,object提供了“字符串-值”的對應結構,map則提供的是“值-值”的對應,是一種更加完善的hash結構。
const map = new Map(); map.set("first", "hello"); map.set("second", "world"); for (let [key, value] of map) { console.log(key + " is " + value); } // first is hello // second is world // 如果只想獲取鍵名,或者只想獲取鍵值,可以寫成下面這樣 // 獲取鍵名 for (let [key] of map) { // ... } for (let [, value] of map) { // ... }
7)輸入模塊的指定方法
加載模塊時,往往需要指定輸入哪些方法。結構賦值使得輸入語句非常清晰。
const { SourceMapConsumer, SourceNode } = require("source-map");
字符串的擴展
1.字符串的Unicode表示法
emm感覺用Unicode表示一個字符的用處不大,所以跳過這節吧~
2.字符串的遍歷器接口
字符串可以被for...of循環遍歷,這個遍歷器最大的優點是可以識別大于0xFFFF的碼點,傳統的for循環無法識別這樣的碼點。
for (let codePoint of "foo") { console.log(codePoint) } // "f" // "o" // "o"
5.模板字符串
模板字符串(template string)是增強版的字符串,用反引號(`)表示。它可以當作普通字符串使用,也可以用來定義多行字符串,或者在字符串中嵌入變量。模板字符串中嵌入變量,需要將變量名寫在${}之中。
大括號內部可以放入任意的JavaScript表達式,可以進行運算,以及引用對象屬性。模板字符串之中還能調用函數。
let x = 1; let y = 2; `${x} + ${y} = ${x + y}` // "1 + 2 = 3" let obj = {x: 1, y: 2}; `${obj.x + obj.y}` // "3" function fn() { return "Hello World"; } `foo ${fn()} bar` // foo Hello World bar
如果需要引用模板字符串本身,在需要時執行,可以寫成函數。模板字符串寫成了一個函數的返回值。執行這個函數,就相當于執行這個模板字符串了。
let func = (name) => `Hello ${name}!`; func("Jack") // "Hello Jack!"
emmm 下面的看不懂了,也沒在代碼中看到過,看來后期的學習還有很長一段路呢。
字符串的新增方法
前面的那幾種方法感覺并不常用,只是粗略看了一遍,并沒有細看。
6.實例方法:repeat()
repeat方法返回一個新字符串,表示將原字符串重復n次。
"hello".repeat(2) // "hellohello" "na".repeat(0) // "" "na".repeat(2.9) // "nana" 小數會被取整,向下取整
正則的擴展
1、RegExp構造函數
在ES5中,RegExp構造函數的參數有兩種情況。
第一種情況是,參數是字符串,這時第二個參數表示正則表達式的修飾符(flag)。
第二種情況是,參數是一個正則表達式,這時會返回一個原有正則表達式的拷貝。
// 第一種 let regex = new RegExp("xyz", "i"); // 等價于 let regex = /xyz/i; // 第二種 let regex = new RegExp(/xyz/i); // 等價于 let regex = /xyz/i; // 但是不允許此時使用第二個參數添加修飾符,否則會報錯。 let regex = new RegExp(/xyz/, "i"); // Uncaught TypeError: Cannot supply flags when constructing one RegExp from another.
2、字符串的正則方法
字符串對象共有4個方法,可以使用正則表達式:match()、replace()、search()、split()。
ES6將這4個方法,在語言內部全部調用RegExp的實例方法,從而做到所有與正則相關的方法,全部定義在RegExp對象上。
- String.prototype.match調用RegExp.prototype[Symbol.match] - String.prototype.replace調用RegExp.prototype[Symbol.replace] - String.prototype.search調用RegExp.prototype[Symbol.search] - String.prototype.split調用RegExp.prototype[Symbol.split]
數值的擴展
2、Number.isFinite(),Number.isNaN()
ES6在Number對象上,新提供了Number.isFinite()和Number.isNaN()兩個方法。
Number.isFinite()用來檢查一個數值是否為有限的(finite),即不是Infinity。
注:如果參數類型不是數值,Number.isFinite一律返回false。
Number.isFinite(0.8); // true Number.isFinite(NaN); // false Number.isFinite(Infinity); // false Number.isFinite(-Infinity); // false Number.isFinite("foo"); // false Number.isFinite("15"); // false Number.isFinite(true); // false
Number.isNaN()用來檢查一個值是否為NaN。
注:如果參數類型不是數值,Number.isFinite一律返回false。
Number.isNaN(NaN); // true Number.isNaN(15); // true Number.isNaN("15"); // false Number.isNaN(true); // false Number.isNaN(9 / NaN); // true Number.isNaN("true" / 0); // true Number.isNaN("true" / "true"); // true
它們與傳統的全局方法isFinite()和isNaN()的區別在于,傳統方法先調用Number()將非數值的值轉為數值,在進行判斷,而這兩個新方法只對數值有效,Number.isFinite()對于非數值一律返回false,Number.isNaN()只有對于NaN才返回true,非NaN一律返回false。
isFinite("25"); // true Number.isFinite("25"); // false isNaN("NaN"); // false Number.isNaN("NaN"); // true
3、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("12.345#") // 123.45
4、Number.isInteger()
Number.isInteger()用來判斷一個數值是否為整數,也就是說,是直接判斷的。
Number.isInteger(25); // true Number.isInteger(25.1); // false Number.isInteger(); // false Number.isInteger(null); // false Number.isInteger("25"); // false Number.isInteger(true); // false
5、Number.EPSILON
ES6在Number對象上面,新增一個極小的常量Number.EPSILON。根據規格,它表示1與大于1的最小浮點數之間的差。(相當于2的-52次方。)
Number.EPSILON實際上是JavaScript能夠表示的最小精度。誤差如果小于這個值,就可以認為已經沒有意義了,即不存在誤差了。
Number.EPSILON可以用來設置“能夠接受的誤差范圍”。比如。誤差范圍設為2的-50次方(即Number.EPSILON * Math.pow(2, 2)),即如果兩個浮點數的差小于這個值,我們就認為這兩個浮點數相等。
function withinErrorMargin (left, right) { return Math.abs(left - right) < Number.EPSILON * Math.pow(2, 2); } 0.1 + 0.2 === 0.3; // false withinErrorMargin(0.1 + 0.2, 0.3); // true
7、Math對象的擴展
ES6在Math對象上新增了17個與數學相關的方法。所有這些方法都靜態方法,只能在Math對象上調用。
Math.trunc()
Math.trunc方法用于去除一個數的小數部分,返回整數部分。
對于非數值,Math.trunc內部使用Number方法將其先轉為數值。
Math.trunc(4.1); // 4 Math.trunc(4.9); // 4 Math.trunc(-4.9); // -4 Math.trunc("123.456"); // 123 Math.trunc(true); // 1 Math.trunc(null); // 0 Math.trunc(NaN); // NaN Math.trunc("foo"); // NaN Math.trunc(undefined); // NaN // 實際意義 Math.trunc = Math.trunc || function(x) { return x < 0 ? Math.ceil(x) : Math.floor(x); }
Math.sign()
Math.sign()方法用來判斷一個數到底是正數、負數、還是零。對于非數值,會先將其轉換為數值。
它會返回五種值:
參數為正數,返回+1; Math.sign(5) // +1
參數為負數,返回-1; Math.sign(-5) // -1
參數為0, 返回0; Math.sign(0) // +0
參數為-0,返回-0; Math.sign(-0) // -0
其他值,返回NaN。 Math.sign(NaN) // NaN
如果參數是非數值,會自動轉為數值。對于那些無法轉為數值的值,會返回NaN。
Math.sign("") // 0 Math.sign(true) // +1 Math.sign(false) // 0 Math.sign(null) // 0 Math.sign("foo") // NaN Math.sign("9") // +1 Math.sign() // NaN Math.sign(undefined) // NaN
Math.cbrt()
Math.cbrt方法用于計算一個數的立方根。
對于非數值,Math.cbrt方法內部也是先使用Number方法將其轉為數值。
Math.cbrt(-1) // -1 Math.cbrt(2) // 1.2599210498948734 Math.cbrt("8") // 2 Math.cbrt("hello") // NaN // 實質 Math.cbrt = Math.cbrt || function(x) { let y = Math.power(Math.abs(x), 1/3); return x < 0 ? -y : y; }
8、指數運算符
ES2016新增了一個指數運算符(**)。該運算符是右結合,而不是常見的左結合。多個指數運算符連用時,是從最右邊開始計算的。
2 ** 3 // 8 2 ** 3 ** 2 // 512 相當于 2**(3**2)
指數運算符可以與等號結合,形成一個新的賦值運算符(**=)。
a **= 2 // 等同于 a = a * a b **= 3 // 等同于 b = b * b * b
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/105438.html
摘要:使用新特性開發微信小程序國際化與本地化新特性國際化與本地化新增了很多對于國際化的支持,比如時間格式,貨幣格式,數字格式等。 ECMAScript 6(簡稱ES6)是JavaScript語言的最新標準。因為當前版本的ES6是在2015年發布的,所以又稱ECMAScript 2015。 微信小程序支持絕大部分ES6的新增特性。 使用ES6新特性開發微信小程序(1) ES6新特性:Cons...
摘要:更新了個版本,最新正式版是語言的下一代標準,早已在年月正式發布。基本不支持移動端瀏覽器對的支持情況版起便可以支持的新特性。比較通用的工具方案有,,,等。 1、ECMAScript是什么? 和 JavaScript 有著怎樣的關系? 1996 年 11 月,Netscape 創造了javascript并將其提交給了標準化組織 ECMA,次年,ECMA 發布 262 號標準文件(ECMA-...
摘要:但是在中,可以通過關鍵字來實現類的繼承的使用可以使得繼承意義更加明確并且值得一提的是,如果你使用來定義的組件,那么可以在類的構造器里面,用簡單的的聲明方式來替代方法。 原文:The 10 min ES6 course for the beginner React Developer譯者:Jim Xiao 著名的80/20定律可以用來解釋React和ES6的關系。因為ES6增加了超過75...
摘要:,正式名稱是,但是這個名稱更加簡潔。已經不再是最新的標準,但是它已經廣泛用于編程實踐中。而制定了模塊功能。自從年雙十一正式上線,累計處理了億錯誤事件,得到了金山軟件等眾多知名用戶的認可。 譯者按: 人生苦短,我用ES6。 原文: Top 10 ES6 Features Every Busy JavaScript Developer Must Know 譯者: Fundebug 為了保...
摘要:今天閑來無事,看見幾行小字。又說所有對象,繼承終是。強行押韻一波這首詩的意思就是說的我今天沒有什么事情,然后無意中又在網上看到了任何對象都是從對象繼承而來的這句話。一時興起,便去驗證這句話。 今天閑來無事,看見幾行小字。又說所有對象,繼承終是Obj。—— 強行押韻一波 這首詩的意思就是說的我今天沒有什么事情,然后無意中又在網上看到了任何對象都是從Object對象繼承而來的這句話。一時興...
閱讀 3267·2021-11-18 10:02
閱讀 3443·2021-10-11 10:58
閱讀 3376·2021-09-24 09:47
閱讀 1120·2021-09-22 15:21
閱讀 3915·2021-09-10 11:10
閱讀 3277·2021-09-03 10:28
閱讀 1748·2019-08-30 15:45
閱讀 2136·2019-08-30 14:22