摘要:返回布爾值標簽模板可以緊跟一個函數名后邊,該函數將被調用來處理這個模板字符串。其它情況下返回值為在內部,整數和浮點數使用同樣的存儲方法,所以和被視為同一個值。
簡介
ES6目標,讓JavaScript變成一個企業級的開發語言,不僅僅限制與前端頁面的腳本語言。
標準(Standard): 用于定義與其他事物區別的一套規則 實現(Implementation): 某個標準的具體實施/真實實踐編譯
服務端使用 babel 編譯
構建工具fis使用
插件:fis-parser-babel2 編譯ES6
fis.match("**.ts", { parser: fis.plugin("babel2"), rExt: ".js" });
TypeScript與babel編譯有區別:
例如:對一個類的編譯TypeScript編譯的結果是一個閉包類,babel編譯的結果是一個安全類.
function _classCallCheck( instance, Constructor ) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var Person = function Person() { _classCallCheck(this, Person); };
瀏覽器端編譯traceur庫來編譯.
traceur庫,引入兩個庫:traceur.js 和 treaceur-bootstrap.js
寫 let&const let
let聲明一個塊級作用域,并且可以初始化該變量。用法與var類似。只能在所在的代碼塊內有效。
塊作用域:{}構成的一個代碼庫處的作用域叫做塊作用域。
let 允許吧變量的作用域限制在塊級域中。與var 不同的是:var聲明變量要么是全局的,要么是函數級的,而無法是塊級的。
典型應用:for循環中的計數器
let定義的變量,會在塊作用域保留下來,訪問的就是當前循環的變量值。
for ( let j=0; j<4; j++ ) { } console.log(j); // error 報錯。
for(let i = 0; i < 5; i++){ setTimeout(function(){ console.log(i); }, i * 100); }
作用域規則
用let定義的變量的作用域是定義它們的塊內,以及包含在這個塊中的子塊
與var區別,是否可以有塊級概念,是否有存在變量提升
function test () { let a = 10; if ( true ) { let a = 20; console.log(a); // 20 } console.log(a); // 10 } test();
不存在變量提升
console.log(b); // undefined console.log(a); // 報錯ReferenceError let a = 10; var b = 10;
function foo(){ a = 20; // 報錯 ReferenceError console.log(a); let a = 10; console.log(a); } foo();
let 的暫存死區
在同一個函數或同一個作用域中的let重復定義一個變量將引入TypeError
注意:switch中聲明的變量。
if ( true ) { let a; let a; // TypeError thrown. }
如果塊中存在let 和const。 凡是在聲明之前就使用這些變量,就會報錯。
let聲明之前,該變量都是不可用的。稱之為:暫時性死區(temproal dead zone簡稱 TDZ)
if (true) { // TDZ開始 a = 10; // ReferenceError console.log(tmp); // ReferenceError let a; // TDZ結束 console.log(tmp); // undefined a = 100; console.log(tmp); // 100 }
ES6中的TDZ 像是運行時的檢查而不是語法聲明上的概念.
if(true){ setTimeout(function(){ console.log(x); // 1 }); let x = 1; }
let特點:
具有塊級作用域
沒有變量提升
不允許重復聲明
具有暫時性死亡區
為了不出發死亡區,必須,變量先聲明,后使用的規定。
let聲明的全局變量并不是全局對象的屬性
let聲明的變量知道控制流到達該變量被定義的代碼行時才會被裝載,所以在到達之前會使用該變量會觸發錯誤,也就是所說的暫時性死亡區。沒有分為預解析階段。
塊級作用域的意義:
ES5中,只有全局作用域和函數作用域,沒有塊級作用域。
缺點:
1:內存變量可能會覆蓋外層變量。
2:用來計數的循環變量泄漏為全局變量。(循環內變量過度共享)
const聲明一個只讀的常量。 一旦聲明,常量的值就不可更改。
意味著,const一旦聲明常量,就必須立即初始化,不可留到以后賦值。
語法:const key = value;
在定義常量的前面,不能訪問到常量,因此一般都將常量定義在文件的最前面。
例如:require() 模塊的時候使用。
const http = require("http");
常量不能被重復定義,為了保證常量定義使用的時候的安全性。
const 和 let 的區別:
const具有let的所有特性,比let更加嚴格,不可以被重復定義(賦值)。
const 只是保證變量名指向的地址不變,并不保證該地址的數據不變。所以將一個對象聲明為常量必須非常小心。
const foo = {}; foo.k1 = 100; console.log( foo.k1 ); foo = {}; // TypeError: "foo" is read-only // 常量foo 儲存的是一個地址,這個地址指向一個對象。不可變的只是這個地址,即不能把foo指向另一個地址,但是對象本身是可變的,所以依然可以為其添加新的屬性。
Objcet.freeze()
作用:凍結對象(不可為對象添加屬性和和方法)
參數:被凍結的對象
const foo = {} Object.freeze(foo); // 常規模式時,下面一行不起作用, 忽略添加 // 嚴格模式時,該行會報錯 foo.k1 = 100; console.log(foo);解構賦值
結構的作用是可以快速取得數組或對象當中的元素或屬性,而無需使用arr[x]或者obj[key]等傳統方式進行賦值.
數組的結構賦值解構:從數組和對象中提取值,對變量進行賦值。
基本用法:
let [a, b, c] = [1, 2, 3]; // 從數組中提取值,按照對應位置,對變量賦值。 // 這種寫法,本質上屬于“模式匹配”。 只要等號兩邊的模式相同,左右的變量就會賦予對應的值。 let [foo, [[bar], baz]] = [1, [[10], 100]]; let [x, y, ...z] = ["a", "b", "c", "d"]; console.log(x,y,z); // a b ["c", "d"]
如果解構不成功,變量值為undefiend。
let [d] = []; // undefined let [e, f] = [1]; // 1 undefined
不完全解構: 等號左邊的模式,只匹配一部分等號右邊的數組。
let [x, y] = [1, 2, 3]; console.log(x, y); // 1 2 let [a, [b], c] = [1, [2, 3], 4]; console.log(a, b, c); // 1 2 4
等號的右邊不是數組,(嚴格的說,不是可遍歷的結構),會報錯。
// 報錯 let [foo] = 1; let [foo] = false; let [foo] = NaN; let [foo] = undefined; let [foo] = null; let [foo] = {}; // 轉為對象以后不具備Iterator接口(前面5個表達式), 本身就不具備Iterator接口(最后一個表達式)
只要某種數據結構具有Iterator接口,都可以采用數組形式的解構賦值。 (例如:函數執行之后的返回值)
默認值
解構賦值允許設置默認值
let [a = 100] = []; console.log(a); // 100
ES6內部使用嚴格相等 運算符(===),判斷一個位置是否有值。如果一個數組成員不嚴格等于undefeind,默認值不會生效。
let [a = 1] = [undefined]; let [b = 10] = [null]; let [c = 100] = [NaN]; console.log(a, b, c); // 1 null NaN
如果默認值是一個表達式,這個表達式是惰性求值(只有在用到的時候才會求值)。
function fn () { console.log("a"); } let [x = fn()] = [1]; console.log(x); // 1 // fn(); 函數不會執行
默認值可以引用其它解構賦值的變量
默認值可以引用解構賦值的其它變量,但該變量必須已經聲明,如果不聲明,將會報錯。
let [x = 1, y = x] = []; // x = 1 y = 1 let [x = y, y = 1] = []; // ReferenceError // x用到默認值y時,y還沒有聲明。
最為常用的地方是:函數參數的arguments類數組對象的解構賦值。
對象的解構賦值對象的解構與數組有一個重要的不同之處,數組的元素是按次序排序,變量的取值由它的位置決定的,而對象的屬性沒有次序,變量必須與屬性同名,才能取到正確的值。
let {a, b} = {b: 10, a: 100}; console.log(a, b); // 10 100 let { c } = {d: 1000, e: 100 } console.log(c); // undefined
對象的解構賦值的內部機制:先找到同名屬性,然后再賦值給對應的變量。真被賦值的是后者,而不是前者。
let {foo: baz} = {foo: "aaa", bar: "bbb"} console.log(baz); // aaa console.log(foo); // ReferenceError // 被賦值的是變量baz,而不是模式foo
默認值
默認值生效的條件:對象的屬性值嚴格等于undefined。
let {x = 3} = {x: undefined} // x = 3; let {y = 10} = {y: null} console.log(x, y); // 3 null
如果解構失敗,變量值等于undefined
字符串的解構賦值字符串的解構賦值本質:字符串被轉為類數組對象,類數組對象中有length屬性,可以有Iterator接口,可以被遍歷。
const [a, b, c, d, e] = "hello"; console.log(a, b, c, d, e); // h e l l o數值和布爾值的解構賦值
需要指定包裝對象toString
解構規則:等號右邊的值不是對象,就先將其轉為對象。 undefind 和 null 無法轉為對象,對其二者解構賦值,都會報錯。
let {toString: s} = 10; console.log( s === Number.prototype.toString ); // true console.log(s); // function toString() { [native code] } let { toString: t } = true; console.log(t); // function toString() { [native code] }函數參數解構賦值
可以解構賦值,可以使用默認值
[[1, 2], [3, 4]].map( ([a, b]) => a + b ); // 3 7 [[, 2], [, 4]].map(function ( [a = 10, b] ) { return a + b; });圓括號問題
對于編譯器來說,一個式子到底是模式,,還是表達式,沒有辦法從一開始就知道,必須解析到(或解析不到)等號才能知道。
問題:模式中出現的圓括號怎么處理。
解析規則:只要有可能導致解構的歧義,就不得使用圓括號。
建議:只要有可能,就不要再模式中放置圓括號。能不使用,就不使用圓括號。
不能使用圓括號的情況
變量聲明語句中,不能帶有圓括號
// 全部報錯 var [(a)] = [1]; var {x: (c)} = {}; var ({x: c}) = {}; var {(x: c)} = {}; var {(x): c} = {};} var { o: ({ p: p }) } = { o: { p: 2 } }; // 都是變量聲明語句,模式不能使用圓括號。
函數參數中,模式不能帶有圓括號。
// 報錯 function f([(z)]) { return z; }
賦值語句中,不能將整個模式,或嵌套模式中一層,放在圓括號之中。
// 全部報錯 ({ p: a }) = { p: 42 }; ([a]) = [5];
可以使用圓括號的情況:
賦值語句的非模式部分,可以使用圓括號。
[(b)] = [3]; // 正確 // 模式是取數組的第一個成員,跟圓括號無關 ({ p: (d) } = {}); // 正確 // 模式是p,而不是d; [(parseInt.prop)] = [3]; // 正確 // 模式是取數組的第一個成員,跟圓括號無關用途
交換變量的值
[x, y] = [y, x]
從函數返回多個值
函數只能返回一個值,如果要返回多個值,只能將它們放在數組或對象里返回。
// 返回一個數組 function example() { return [1, 2, 3]; } var [a, b, c] = example(); // 返回一個對象 function example() { return { foo: 1, bar: 2 }; } var { foo, bar } = example();
函數參數定義
將一組參數與變量名對應。
// 參數是一組有次序的值 function f([x, y, z]) { ... } f([1, 2, 3]); // 參數是一組無次序的值 function f({x, y, z}) { ... } f({z: 3, y: 2, x: 1});
提取JSON數據
提取JSON對象中的數據
函數參數的默認值遍歷Map結構
輸入模塊的指定方法
加載模塊時,往往需要指定輸入那些方法。
const { SourceMapConsumer, SourceNode } = require("source-map");String的擴展 satrtsWith
str.starsWith(str1, num);
判斷字符串以指定參數開頭
參數1:開頭的字符
參數2:可選,指定開始查找的索引位置
返回布爾值
str.endWith(str1, num);
判斷字符串以指定參數結束
參數1:結束的字符子串
參數2:可選,指定開始查找的索引位置
返回布爾值
str.includes(str1, num);
判斷指定的參數字符串是否包含在字符串中。
參數1:被查詢的字符串
參數2:可選,從那邊開始查找索引位置。
返回布爾值
``可以緊跟一個函數名后邊,該函數將被調用來處理這個模板字符串。 稱之為:“標簽模板”(tagged template);
alert`123`; alert(123); // 等同
標簽模板實質并不是模板,而是函數的調用的一種特殊形式。“標簽”指的就是函數,緊跟在后面的模板字符串就是它的參數。
如果模板字符里面有變量,就不在是簡單的調用,而是會將模板字符串先處理成多個參數,再調用函數。
模板字符串語法:${表達式}
需要通過``來聲明字符串,才會生效。
如果使用""單引號聲明沒有效果。并不會解析模板中的表達式。
//let num = `十`; let num = `5`; let intro = `repeat笑一笑,${5 * 2}年少
`;
重復字符串
參數:指定重復的次數
let num = "10"; let intro = `raw笑一笑,${num * 2}年少
`; let introT = intro.repeat(2); console.log(intro); console.log(introT);
返回原始字符串,該方法不會將字符串中的轉義字符轉義
這個方法特殊,不需要使用()調用.
是String底下的一個方法,直接通過String調用即可。
console.log("success 1"); let str = String.raw`success 1`; console.log(str);Number的擴展
ES6在Number對象上,提供了Number.isFinite() 和 Number.isNaN();兩個方法。
用來檢查Infinite和NaN這兩個特殊值。
檢查是否是作為數字存在,對于不存在或者是NaN返回值為:false,對于數字返回值為:true
Number.isFinite(15); // true 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
// 實現方式 (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);isNaN();
當參數是NaN的時候,才返回true,其它情況都返回false。
Number.isNaN(NaN) // true Number.isNaN(15) // false Number.isNaN("15") // false Number.isNaN(true) // false Number.isNaN(9/NaN) // true Number.isNaN("true"/0) // true Number.isNaN("true"/"true") // true
// 實現方式 (function (global) { var global_isNaN = global.isNaN; Object.defineProperty(Number, "isNaN", { value: function isNaN(value) { return typeof value === "number" && global_isNaN(value); }, configurable: true, enumerable: false, writable: true }); })(this);
與傳統的全局方法isFinte()方法isNaN()的區別:
傳統方法先調用Number()將非數值的值轉為數值,再進行判斷。
Number.isFinte()和isNaN();只對數值有效,非數值一律返回false.
ES6將全局方法parseInt();和parseFloat();方法 移植到Number對象上,行為完全保持不變。
目的:逐漸減少全局性方法,是的語言逐步模塊化。
isInteger當參數是正整數,負整數,0,-0,的時候返回值為:true。其它情況下返回值為:false
在JavaScript內部,整數和浮點數使用同樣的存儲方法,所以3和3.0被視為同一個值。
Math.sign();用來判斷一個數到底是整數,負數,還是零。
返回值:
參數為整數,返回+1
參數為負數,返回-1
參數為0 ,返回0
參數為-0,返回-0
參數為其它值,返回NaN
Math.sign(""); // NaN
Math.sign("a"); // NaN
Math.sign(NaN); // NaN
Math.sign(false); // NaN
Math.sign(undefined); // NaN
Math.sign(0); // 0
Math.sign(-0); // -0
Math.sign(-2); // -1
Math.sign(+2); // 1
// 模擬 Math.sign(); 方法 Math.sign = Math.sign || function ( x ) { x = +x; if ( x === 0 || isNaN(x) ) { return x; } return x > 0 ? 1 : 1; }trunc
Math.trunc(); 用于去除一個數的小數部分,返回 整數部分。
非數值,Math.trunc()內部先使用Number();將其轉為數值。
Math.trunc(123.456); // 123
對于空值和無法截取整數的值,返回NaN
Math.trunc(NaN); // NaN Math.trunc("hello"); // NaN // 模擬Math.trunc(); Math.trunc = Math.trunc || function ( x ) { return x < 0 ? Math.ceil(x) : Math.floor(x); }Array的擴展 form
作用:將類數組對象轉化成數組對象
參數1:類數組對象
參數2:可選,處理的回調函數。可以處理類對象每個成員
該回調函數中參數1:value值,(類數組的成員), 參數2:索引值。返回值會保留最終結果,如果沒有返回值,那么from函數會返回由undefined構成的一個新數組
如果是轉一個真正的數組,Array.from();會返回一個一模一樣的新數組。
更經常的使用時:arguments 和 獲取的DOM元素。
// 類數組對象 let arrayLike = { 0: "a", 1: "b", length: 2 } // ES5的寫法 let arr1 = [].slice.call(arrayLike); // ES6的寫法 let arr2 = Array.from(arrayLike); console.log(arr1, arr2); // ["a", "b"]["a", "b"]
let arrayLike = { 0: "a", 1: "b", length: 2 } // ES6的寫法 let arr2 = Array.from(arrayLike, function ( value, index ) { return value + "-xixi"; });of
Array.of();將一組值,轉為數組。
返回值:返回參數值組成的數組,如果沒有參數,就返回一個空數組。
作用:彌補數組構造函數Array()的不足。因為參數個數的不同,會導致Array()的行為有所差異。
Array()如果是一個參數,則表示當前數組的長度。如果是二個或二個以上,則表示數組成員。
Array.of(1, 2, 3, "xixi");
Array.of();基本上可以用來替代Array()或new Array(); 并且不存在由于參數不同而導致的重載問題。
Array.of() // [] Array.of(undefined) // [undefined] Array.of(1) // [1]
// 模擬Arraay.of();方法 function ArrayOf () { return [].slice.call(arguments); }find
find();方法和findIndex();方法
find();
作用:獲取第一個符合條件的數組成員。
參數:回調函數。所有的數組成員一次執行該回調函數。知道找出一個返回值為true的成員。如果沒有符合條件的成員,則返回false。
回調函數中的參數: 當前value值,當前索引值index(可選), 原數組arr(可選)
[1, 2, 23, 10].find( (n) => n > 10 ); // 23 [1, 2, 3].find( ( value, index, arr ) => value < 2 ) // 1
findIndex();
作用:返回第一個符合條件的數組成員的索引值,如果所有成員都不符合條件,則返回 -1.
參數:和find();參數一樣是一個回調函數。
["cyan", "pink", "tan", "red"].findIndex( (value, index, arr) => Object.is("tan", value) );
提出的解決問題:
indexOf();無法檢測出成員中是否有NaN
[NaN].indexOf(NaN); // -1
findIndex();搭配 Object.is(); 可以檢測出來
[10,NaN].findIndex( (value) => Object.is(NaN, value) ); // 1fill
使用給定值,填充一個數組。
參數1:填充的值
參數2:可選,填充的起始位置。
參數3: 可選,填充的結束位置。
keys() & values(); & entrices();
keys();
獲取數組所有成員的索引值。
返回的是一個迭代器對象。
需要通過 for-of遍歷數組迭代器
let colors = ["cyan", "pink", "tan", "red"]; let Idx = colors.keys(); console.log( Idx ); // ArrayIterator {} for ( let index of Idx ) { console.log(index); }
values();
獲取所有成員的值,返回一個數組迭代器對象。
let colors = ["cyan", "pink", "tan", "red"]; let vals = colors.values(); console.log( vals ); // ArrayIterator {} for ( let val of vals ) { console.log(val); }
entries();
對鍵值對的遍歷。
將數組成員的索引值以及數組成員的值,保留在一個新數組中。
let colors = ["cyan", "pink", "tan", "red"]; let entri = colors.entries(); console.log(entri); for ( let entris of entri ) { console.log( entris ); } // 輸出: // [0, "cyan"] // [1, "pink"] // [2, "tan"] // [3, "red"]
如果不使用for-of遍歷,可以手動調用遍歷器對象的next();方法,進行遍歷。
let colors = ["cyan", "pink", "tan", "red"]; let entri = colors.entries(); console.log( entri.next().value ); // [0, "cyan"] console.log( entri.next().value ); // [1, "pink"] console.log( entri.next().value ); // [2, "tan"] console.log( entri.next().value ); // [3, "red"] // next();方法返回:Object {value: Array[2], done: false}空位
數組的空位:數組的某一個位置沒有任何值。
例如:Array構造函數返回的數組都是空位。
空位不是undefined,一個位置的值等于undefined,依然是有值的。空位是沒有任何值
注意:空位不是undefiend,一個位置的值等于undefeind。依然是有值。空位是沒有任何值。
0 in [undefined, undefined, undefined] // true 0 in [, , ,] // false // 第一個數組的0號位置是有值的,第二個數組的 0 號位置沒有值。
ES5中對于空位的不一致:
forEach(), filter(), every() ,some()都會跳過空位。
map()會跳過空位,但會保留這個值
join()和toString()會將空位視為undefined,而undefined和null會被處理成空字符串。
// forEach();
[,"a"].forEach((x,i) => console.log(i)); // 1
// filter();
["a",,"b"].filter(x => true) // ["a","b"]
// every();
[,"a"].every(x => x==="a") // true
// some();
[,"a"].some(x => x !== "a") // false
// map();
[,"a"].map(x => 1) // [,1]
// join();
[,"a",undefined,null].join("#") // "#a##"
// toString();
[,"a",undefined,null].toString() // ",a,,"
ES6則明確將空位轉成undeinfed
由于空位的處理規則非常不統一,所以應該盡量避免出現空位。
ES6允許直接寫入變量和函數,作為對象的屬性和方法。
在定義一個對象的時候,如果對象的屬性與屬性值對應的變量名同名,可以簡寫屬性名。
let objName = "bar"; let obj1 = {objName} console.log( obj1 ); // Object {objName: "bar"} // 等同于 let obj2 = {objName: objName} // Object {objName: "bar"} console.log(obj2); // 在對象之中,只寫屬性名,不寫屬性值,這時候,屬性值等于屬性名所代表的變量。
// 參數簡寫 function Person ( name, age ) { return {name, age} // ES6 允許直接引入變量,不用對對象添加同名屬性 } console.log( Person("cyan", 22) ); // Object {name: "cyan", age: 22} // 等同于 function Person(name , age) { return {name: name, age: age}; }
// 方法的簡寫 // 如果對象屬性名與對象屬性名對應的方法名稱相同,可以省略屬性以及 function關鍵字, 像定義類中的方法一樣定義這個屬性方法。 let obj1 = { name: "cyan", age: 22, sayAge: function () { console.log(this.age); // 22 }, sayName () { // ES6允許定義方法的時候,可以省略方法對應的屬性名,和 fucntion 關鍵字 console.log(this.name); // cyan } } obj1.sayName(); obj1.sayAge(); // sayName等同于 var obj1 = { sayName: function() { console.log(this.name); } }
// 這種屬性簡寫用于函數返回非常方便 function getPoint() { var x = 1; var y = 10; return {x, y}; } getPoint(); // {x:1, y:10}屬性名表達式
JavaScript語言定義對象的屬性,有兩種方式。
// 方法一 obj.foo = true; // 標識符作為屬性名 // 方法二 obj["a" + "bc"] = 123; // 表達式作為屬性名
// 允許定義對象時候,對對象屬性應用表達式 let a = "num"; let b = "ber"; let obj = { [a + b]: 100 } console.log( obj );
// 表達式也可以用于方法名 let obj = { ["h"+"ello"]() { return "hi"; } }; obj.hello() // hi
注意:屬性名表達式不能與簡潔表示法同時使用,會報錯。
// 報錯 let a = "b"; let b = "pink"; let color = { [a] } console.log(color); // 正確 let a = "cyan"; let color = { [a]: "pink" } console.log(color);方法的name屬性
函數的name屬性,返回函數名。
對象方法也是函數,,因此也有name屬性。
let obj = { name: "cyan", sayName() { return this.name; } } console.log( obj.sayName.name ); // sayName
特殊:
bind();方法創造的函數,name屬性返回"bound"加上原函數的名字。
Function構造函數創造的函數,name屬性返回"anonymous".
console.log( (new Function()).name ); // anonymous let doSomething = function () {} console.log( doSomething.bind().name ); //bound doSomethingis
Object.is();
作用判斷兩個參數是否相等。
可以判斷0與-0 ,是否相等。
ES5中,比較兩個值是否相等使用:==或者===
==缺點:自動轉換數據類型
===缺點:NaN不等于自身,以及0等于-0
ES6提出“Same-value equality”(同值相等)算法.
用來比較兩個值是否嚴格相等,與嚴格比較運算符(===)的行為基本一致。
+0 === -0 //true NaN === NaN // false Object.is(+0, -0) // false Object.is(NaN, NaN) // trueassgin
Object.assgin();
作用:用于對象合并。
將源對象(source)的所有可枚舉屬性,復制到目標對象(target)。
參數1:目標對象。
后面的參數都是源對象。
只拷貝源對象的自身屬性(不拷貝繼承屬性),也不拷貝不可枚舉的屬性(enumerable: false)。
如果目標對象與源對象有同名屬性,或多個源對象有同名屬性,則后面的屬性會覆蓋前面的屬性。
如果只有一個參數對象,則返回該參數對象。
let obj1 = { x: 1, y: 2 } let obj2 = { a: "a", b: "b" } console.log( Object.assign(obj1, obj2) );
如果參數不是對象,則會先轉成對象然后返回。
undefeind和null無法轉成對象,作為參數,會報錯。
Object.assign(2); // Number {[[PrimitiveValue]]: 2} Object.assign(undefined) // 報錯 Object.assign(null) // 報錯
Object.assign();是淺拷貝,而不是深拷貝。如果源對象某個屬性的值是對象,那么目標對象拷貝得到的是這個對象的引用。
let obj1 = { x: 1, y: 2 } let obj2 = { a: "a", b: "b" } let resObj = Object.assign(obj1, obj2); console.log( resObj ); console.log( obj1 ); console.log( resObj == obj1 ); // true console.log( obj2 );
assign用途
為對象添加屬性
class Point { constructor (x, y) { Object.assign(this,{x, y}); } } let p = new Point(10, 20); console.log(p); // Point {x: 10, y: 20} // 通過Object.assign();將x屬性和y屬性添加到Point類的對象實例。
為對象添加方法
Object.assign(Some.prototype, { someMethod1 ( arg1, arg2 ) { // .... } someMethod2 ( arg1, arg2 ) { // .... } }) // 等價 Some.prototype.someMethod1 = function ( arg1, arg2 ) { // .... } Some.prototype.someMethod2 = function ( arg1, arg2 ) { // .... }
克隆對象
function clone(origin) { return Object.assign({}, origin); } // 將原對象拷貝到一個空對象,得到了原始對象的克隆。
合并多個對象
將多個對象合并到某個對象,可以是空對象中(合并之后返回新的對象)
const merge = (target, ...sources) => Object.assign({}, ...sources);屬性遍歷
ES6中共有5中可以遍歷屬性的方法:
for-in
for-in循環遍歷對象吱聲和繼承的可枚舉的屬性(不含Symbol屬性)。
Object.keys(obj)
Object.keys返回一個數組,包括對象自身的(不含繼承)所有可枚舉屬性(不含Symbol屬性)
Oject.getOwnPropertyNames(obj);
Object.getOwnPropertyNames(); 返回一個數組,包含對象自身的所有屬性(不含Symbol屬性,但是包括不可枚舉屬性)
Object.getOwnPropertySymbols(obj);
Object.getOwnPropertySymbols();返回一個數組,包含對象自身的所有Symbol屬性。
Reflect.ownKeys(obj);
Reflect.ownKeys(obj);返回一個數組,包含對象自身的所有屬性,不管是屬性名Symbol或字符串,也不管是否可枚舉
遍歷規則:
首先遍歷所有屬性為數值的屬性,按照數字排序。
其次遍歷所有屬性名為字符串的屬性,按照生成時間排序。
最后遍歷所有屬性名為Symbol值的屬性,按照生成時間排序。
keys&values&entriesObject.keys();
作用: 參數對象自身的(不含繼承的) 所有可遍歷屬性的鍵名。
返回數組
Object.values();
作用:參數對象自身的(不含繼承的) 所有可遍歷屬性的鍵值。
返回數組
Object.entries();
作用:參數對象自身的(不含繼承的) 所有可遍歷屬性的鍵值對數組。
返回數組
let obj = { a: 1, b: 2 }; console.log( Object.entries(obj) ); [ ["a", 1], ["b", 2] ]Symbol
ES6引入Symbol的原因:根本上防止屬性名的沖突。
ES6引入一種新的原始數據類型Symbol,表示獨一無二的值。
它是JavaScript語言的第七種中數據類型。前六中是:Undefiend,Null,布爾值(Boolean),字符串(String),數值(Number),對象(Object)
let s = Symbol(); typeof s // symbol
Symbol值通過Symbol函數生成。
對象的屬性名可以有兩種類型,一種是:string。 一種是:Symbol。(凡是屬性名屬于Symbol類型,就是獨一無二,可以保證不會與其它屬性名產生沖突)
注意:Symbol函數前不能使用new關鍵字,否則會報錯。因為:Symbol是一個原始類型的值,不是對象。由于Symbol值不是對象,所以不能添加屬性。基本上,它是一種類似于字符串的數據類型。
Symbol函數可以接收一個字符串作為參數,表示對Symbol實例的描述,
作用:控制臺顯示或轉為字符串時,比較容易區分。
返回值不相等
注意:Symbol函數的參數只是表示對當前Symbol值的描述,因此相同參數的Symbol函數的返回值是不相等的。
// 沒有參數 let s1 = Symbol(); let s2 = Symbol(); console.log(s1 == s2); // false // 參數 let s3 = Symbol("a"); let s4 = Symbol("b"); console.log(s3 == s4); // false
不能與其它類型值進行運算
let sym = Symbol("My Symbol"); console.log("your symbol is" + sym); // TypeError: Cannot convert a Symbol value to a string console.log(`your symbol is` + sym); // TypeError: Cannot convert a Symbol value to a string
可以顯示轉換為String,Boolean。(注意:不能夠轉為數值類型)
let sym = Symbol("My Symbol"); console.log(String(sym)); console.log(sym.toString()); console.log(Boolean(sym)); console.log(!sym); console.log(Number(sym)); // TypeError: Cannot convert a Symbol value to a number console.log(sym + 2); // // TypeError: Cannot convert a Symbol value to a number
Symbol與字符串的區別:
獨一無二
值不相等
不可與其它類型值進行運算
作為屬性名Symbol由于每個Symbol值都是不相等的,意味著Symbol值可以作為標識符。
用于對象的屬性名,就能保證不會出現同名的屬性。 對于一個對象由多個模塊構成的模塊情況,能防止某一個鍵被不小心改寫或覆蓋。
Symbol值作為屬性名時,是公開屬性,而不是私有屬性。
let mySymbol = Symbol("a"); // 第一種方法 let a = {}; a[mySymbol] = "hello"; // 第二種寫法 let b = { [mySymbol]: "hell2" }; // 在對象的內部,使用Symbol值定義屬性時,Symbol值必須放在方括號之中。 // 第三種寫法 let c = {}; Object.defineProperty(c, mySymbol, { value: "hello3" }); console.log(a[mySymbol], b[mySymbol], c[mySymbol]);
注意:Symbol值作為對象的屬性名,不能使用點語法。
let mySymbol = Symbol("a"); let obj = {}; obj[mySymbol] = "hello"; console.log( obj[mySymbol] ); // hello console.log( obj.mySymbol ); // undefind
定義一組常量,保證這組常量值是不相等。
log.levels = { DEBUG: Symbol("debug"), INFO: Symbol("info"), WARN: Symbol("warn") }; log(log.levels.DEBUG, "debug message"); log(log.levels.INFO, "info message");消除魔術字符串
魔術字符串:在代碼之中出現多次,與代碼形成強耦合的某一個具體的字符串或者數值。
缺點:不利于將來的維護和修改。
消除魔術字符串的方法:把它寫成一個變量,或者常量。
變量或者常量等于那個值并不重要,只要確保不會跟其它的屬性沖突即可。
const shapeType = { triangle: Symbol(), triangleTow: Symbol() } function getArea ( shape, options ) { let area = 0; switch ( shape ) { case shapeType.triangle: area = .5 * options.width * options.height; break; case shapeType.triangleTow: area = .7 * options.width * options.height; break; } return area; } let a = getArea( shapeType.triangleTow, { width: 20, height: 20 } ); let b = getArea( shapeType.triangle, { width: 10, height: 10 } ); console.log( a,b ); // 280 , 50屬性名的遍歷
Symbol作為屬性名,該屬性不會出現在for-in, for-of循環中,也不會被Object.kes();. Object.getOwnProperTyNames();返回。
Symbol并不是私有屬性,ES6中提供Object.getOwnPropertySymbols();
Object.getOwnPropertySymbols();
作用:獲取指定對象的所有Symbol屬性名。
返回一個數組。成員是當前對象的所有用作屬性名的Symbol值。
let obj1 = {}; let c = Symbol("c"); let d = Symbol("d"); obj1[c] = "cyan"; obj1[d] = "tan"; let objectSymbol = Object.getOwnPropertySymbols(obj1); console.log( objectSymbol ); // [Symbol(c), Symbol(d)]
Reflect.ownKeys();
作用:返回所有類型的鍵名,包括常規鍵名和Symbol鍵名。
let obj = { [Symbol("my_key")]: 1, enum: 2, nonEnum: 3 }; console.log( Reflect.ownKeys(obj) ); // ["enum", "nonEnum", Symbol(my_key)]
以Symbol值作為名稱的屬性,不會被常規方法遍歷得到。可以為對象定義一些非私有的,但又希望只用于內部的方法。造成一種非私有話的內部的效果。
Symbol.for(),Symbol.keyFor()Symbol.for();
作用:使用同一個Symbol的值。
參數:一個字符串。
過程:接收一個字符串后,搜索有沒有以該參數作為名稱的Symbol值。如果有,就返回這個Symbol值,否則就新建并返回一個以該字符串為名稱的Symbol值。(具有登記機制,被登記的會在全局環境中供搜索)
let s1 = Symbol.for("a"); let s2 = Symbol.for("a"); console.log( s1 === s2 ); // true
Symbol.keFor();
返回:一個已經登記的Symbol類型值的key。
let s1 = Symbol.for("a"); let s2 = Symbol.for("b"); let s3 = Symbol("b"); console.log( Symbol.keyFor(s1), Symbol.keyFor(s2), Symbol.keyFor(s3) ); // a b undefined模塊的Singleton模式
Signleton模式:指的是調用一個類,任何時候返回的都是同一個實例。
對于 Node 來說,模塊文件可以看成是一個類。怎么保證每次執行這個模塊文件,返回的都是同一個實例呢?
很容易想到,可以把實例放到頂層對象global。
對于Node來說,模塊文件可以看成是一個類。怎么保證每次執行這個模塊文件,都是返回同一個實例呢? 可以把實例掛載到頂層對象global。
// mod.js function A () { this.foo = "hello"; } if ( !global._foo ) { global._foo = new A(); } module.exprots = global._foo; // 加載mod.js let mod = require("./mod.js"); console.log( mod.foo );
存在問題: 掛載到global的屬性,是可寫的,任何文件都可以修改。
防止這種情況,可以使用Symbol.
// mod.js const FOO_KEY = Symbol.foo("foo"); function A () { this.foo = "hello"; } if ( !global[FOO_KEY] ) { global[FOO_KEY] = new A(); } module.exprots = global[FOO_KEY];Proxy&Reflect Proxy概述
Proxy作用:修改某些操作的默認行為。 在目標對象之前設置屏障,外界對該對象的訪問,都必須先通過這層攔截,來保護該對象(內部的屬性,狀態,方法,行為等等)。(Proxy的操作等同于在語言層面做出修改,所以屬性一種“元編程”,即對編程語言進行編程)
提供一種機制,可以對外界的訪問進行過濾和改寫。
Proxy這個詞的原意是代理,這里表達hi由它來“代理”某些操作,可以譯為“代理器”。
語法: new Proxy(代理對象, 屏蔽對象);
屏障的方法,比較特殊,并不是任意定,只能通過get來獲取代理對象的信息,set來設置原始對象的額信息。但是它們不是直接處理對象的,在處理之前會過濾一些人為操作。屏蔽對象配置攔截行為(對于每一個被代理的操作,需要提供一個對應的處理函數,該函數將攔截對應的操作。)
get();
參數:target,key,receiver
set();
參數:target,key,value,receiver
let Start = { count: 0 } let obj1 = new Proxy(Start, { get: function ( target, key ) { console.log(`getting ${key}!`); return target[key]; }, set: function ( target, key, value ) { console.log(`setting ${key}!`); return target[key] = value; } }); console.log( obj1.count ); obj1.count = 1; console.log( obj1.count );
注意:是的Proxy起作用,必須針對Proxy實例進行操作, 而不是針對目標對象進行操作。
如果屏蔽對象沒有進行任何攔截操作,那就等同于直接通過原對象,操作原對象
let a = {}; let b = {}; let proxy = new Proxy(a, b); proxy.tag = "javascript"; console.log(a.tag); // javascript
將Proxy對象,可以設置到Object.proxy屬性上,從而達到可以從Object對象上調用。
Proxy實例可以作為其他對象的原型對象。
// 放在object.proxy 屬性上 let object = { proxy: new Proxy(target, handler) } // 其它對象的原型上 let proxy = new Proxy({}, { get: function ( target, property ) { console.log( target[property] ); return 10; } }); let obj = Object.create(proxy); console.log(obj.time); // 10
同一個攔截器函數,可以設置多個操作。
var handler = { get: function(target, name) { if (name === "prototype") { return Object.prototype; } return "Hello, " + name; }, apply: function(target, thisBinding, args) { return args[0]; }, construct: function(target, args) { return {value: args[1]}; } }; var fproxy = new Proxy(function(x, y) { return x + y; }, handler); fproxy(1, 2) // 1 new fproxy(1,2) // {value: 2} fproxy.prototype === Object.prototype // true fproxy.foo // "Hello, foo"Proxy攔截器操作函數
對于可以設置,但沒有設置攔截的操作,則直接落在目標對象上,按照原先的方式產生結果。
get()
攔截對象屬性的讀取
set()
攔截對象屬性的設置
has(target, propKey)
攔截propKey in proxy的操作,以及對象的hasOwnProperty();
返回值:布爾值。
判斷對象是否具有某個屬性時,方法才會生效。典型的操作就是in運算符。
使用has方法隱藏某些屬性,不被in運算符發現
let headler = { has ( target, key ) { if ( key[0] === "_" ) { return false; } return key in target; } } let target = { _prop: "aaa", prop: "bbb" } let proxy = new Proxy(target, headler); console.log( "_prop" in proxy ); // false
deleteProperty(target, propKey)
攔截delete proxy[propKey]的操作。
返回值:布爾值。 表示是否刪除成功。
apply(target, object, args)
參數args:目標對象的參數數組。
攔截Proxy實例作為函數調用的操作,比如proxy(...ars) ,proxy.call(object, ...args), proxy,apply(...)
let target = function () { return "target"; } let handler = { apply: function () { console.log("apply"); return "handler"; } } let p1 = new Proxy(target, handler); console.log(p1()); // hanlder
construct(target, args)
攔截作為構造函數調用的操作。 比如: new Proxy(...args)
攔截函數和PHP中的魔術方法有相同的原理,當一定條件下,會觸發該聲明的函數。
Reflect概述Reflect對象與Proxy對象一樣
為了操作對象而提供的新API
設計目的:
將Object對象的一些明顯屬性語言內部的方法(比如Object.defiendProerty),放到Reflect對象上,現階段,某些方法同時在Object和Reflect對象上部署,未來的新方法將只部署在Reflect對象上。
修改某Object方法的返回結果,讓其變得更合理。
讓Object操作都變成函數行為
比如: delete obj[name] 和 name in obj 的指令式操作
Reflect對象的方法與Proxy對象的方法一一對應,只要是Proxy對象的方法,就能在Reflect對象上找到對應的方法。
函數的擴展 參數的默認值ES5的默認參數做法
如果沒有if判斷,參數是否被賦值,如果沒有,再等于默認值
原因:如果參數賦值了,但是對應值是false,則該賦值就不應該起作用。 例如:調用的時候log("hello", ""); 如果參數是空字符串,就不應該被改為默認值。
function log ( x, y ) { if ( typeof y === undefined ) { y = y || "world"; } console.log(x, y); } log("Hello"); // Hello world log("Hello", "hi"); // Hello hi log("Hello", ""); // Hello
ES6使用默認參數,直接寫在參數定義的后邊。
好處:
1:易閱讀代碼
2:利于未來的代碼優化。未來的版本在對外接口中,測底拿掉這個參數,也不會導致以前的代碼無法執行。
特點:參數變量時默認聲明的,不能用let或const再次聲明
function Point ( x, y=10 ) { this.x = x; this.y = y; } let p = new Point(100); console.log(p); // Point {x: 100, y: 10}
參數默認的位置
一般的,定義默認值的參數,應該是函數的尾參數。這樣可以比較容易看出來,到底省略了哪些參數。
如果非尾參數設置默認值,這個參數是沒法省略。(想使用默認值,設置為undefeind)
function fn ( x = 1, y ) { return [x ,y]; } console.log( fn() ); // [1, undefined] console.log( fn(2) ); // [2, undefined] console.log( fn(undefined, 1) ); // [1, 1] // 如果傳入undefeind,將觸發該參數等于默認值, console.log( fn(null, 1) ); // [null, 1] // 如果傳入null,則會輸出。 console.log( fn( , 10) ); // error
function fn2 ( x, y = 1, z ) { return [x, y, z]; } console.log( fn2() ); // [undefined, 1, undefined] console.log( fn2(2) ); // [2, 1, undefiend] console.log( fn2(2, undefined, 3) ); // [2, 1, 3] console.log( fn2(2, , 3) ); // error
函數的length屬性
length屬性的含義:該函數預期傳入的參數個數。
指定了默認值后,length屬性將失真。將返回沒有指定默認參數的個數。
某個參數指定了默認值以后,預期傳入的參數個數就不包括這個參數了。rest參數不會計入length屬性個數中。
let len1 = (function (a) {}).length; let len2 = (function (a = 5) {}).length; let len3 = (function ( a, b, c = 1 ) {}).length; let len4 = (fucntion (...args) {}).length; console.log(len1); // 1 console.log(len2); // 0 console.log( len3 ); // 2 console.log( len4 ); // 0
作用域
如果參數默認值是一個變量,則該變量所處的作用域,與其它變量的作用規則是一樣的,即先是當前函數的作用域,然后才是全局作用域。
var x = 1; function fn ( x, y = x ) { console.log(y); } fn(2); // 2
如果參數默認值是一個函數
該函數的作用域是其聲明時所在的作用域
let foo = "outer"; function bar ( func = x => foo ) { let foo = "inner"; console.log( func() ); // outer } bar(); // 函數bar的參數func的默認值是一個匿名函數,返回值為變量foo。這個匿名函數聲明時,bar函數的作用域還沒有形成,所以匿名函數里面的foo指向外層作用域foo
應用
利用參數默認值,可以指定某一個參數不得省略,如果省略就拋出一個錯誤。
function throwMsg () { throw new Error("error"); } function foo ( pro1 = throwMsg() ) { return pro1; } foo(); // 拋出錯誤
可以將參數默認值設置為unfeined,表明這個參數是可以省略的。
function foo ( options = undefined ) { }rest參數
語法:...變量名
作用:把逗號隔開的值序列組合成一個數組
獲取過來的是數組,將制定的多余變量放入數組中。
使用rest參數就不需要使用arugments對象
function add ( ...nums ) { let sum = 0; for ( let val of nums ) { sum += val; } return sum; } console.log( add(1, 2, 3) ); // 6
注意:rest參數之后不能再有其它參數(即只能是最后一個參數)
擴展運算符擴展運算符...。
好比rest參數的逆運算。
作用:把數組或類數組對象展開成一系列用逗號隔開的值(將一個數組轉為用逗號分隔的參數序列).
console.log(...[1, 2, 3]); // 1 2 3 console.log(1, ...[2, 3, 4], 5 ); // 1 2 3 4 5
一般的,主要用于函數調用:
function add ( x, y ) { return x + y; } let nums = [10, 20]; console.log( add(...nums) ); // 30
擴展運算符與正常的函數參數,rest參數可以配合使用,體現靈活性。
function fn ( x, y, z, n, m, ...nums ) { console.log(...nums); // 5 ,6 } let args = [1, 2]; fn(-1, ...args, 3, ...[4, 5], 6);
擴展運算符的應用
合并數組
let moreArr = [3,4]; let arr1 = [1, 2].concat(moreArr); console.log(arr1); console.log( [1, 2, ...moreArr] ); let arr2 = ["a", "b"]; let arr3 = ["c", "d"]; let arr4 = ["e", "f"]; // ES5的寫法 console.log( arr2.concat(arr3, arr4) ); // ES6的寫法 console.log( [...arr2, ...arr3, ...arr4] );
與解構賦值結合
擴展運算符與解構賦值結合起來使用,用于生成數組。
注意:擴展運算符只能放在參數的最后一位。(否則將會報錯)
const [first, ...rest] = [1, 2, 3, 4, 5]; const [...butLast, last] = [1, 2, 3, 4, 5]; // SyntaxError: Rest element must be last element in array const [first, ...middle, last] = [1, 2, 3, 4, 5]; // SyntaxError: Rest element must be last element in array
函數返回值
JavaScript的函數只能返回一個值,如果需要返回多個值,只能返回數組或對象。擴展運算符提供變通方法。
字符串
擴展運算符可以將字符串轉為數組
console.log( [..."hello"] ); // ["h", "e", "l", "l", "o"]
實現了Iterator接口的對象
任何Iterator接口對象,都可以使用擴展運算符轉為真正的數組
例如:arguments對象,nodeList對象
let arrList = { "0": "a", "1": "b", "2": "c", length: 3 } console.log( [...Array.from(arrList)] ); // ["a", "b", "c"] console.log( [...arrList] ); // TypeError: arrList[Symbol.iterator] is not a functionname屬性
函數的name屬性,返回該函數的屬性名。
屬性被瀏覽器支持,到ES6才寫入規范中。
ES5中匿名函數,name屬性會返回空字符串。
ES6中匿名函數,name屬性會返回實際的函數名
ES5,ES6中,有名的匿名函數,name屬性返回具有函數名的名字
Function 構造函數返回函數實例, name 屬性值為:anonymouse
let fn1 = function () {}; // ES5 console.log( fn1.name ); // "" // ES6 console.log( fn1.name ); // fn1 const fn = function fn2 () {} // ES5 console.log( fn.name ); // fn2 // ES6 console.log( fn.name ); // fn2 // Function 構造函數 console.log( (new Function).name ); // "anonymouse";箭頭函數
基本用法
使用 => 定義函數
() => 表達式
參數 => 表達式
var f = v => v; // 等價 var f = function ( v ) { return v; }
如果箭頭函數需要多個參數或者不需要參數,可以使用()代表參數。
() => { 函數體 }
var fn = () => 5; // 等同于 var fn = function () { return 5; } var sum = (num1, num2) => num1 + num2; // 等同于 var sum = function ( num1, num2 ) { return num1 + num2 }
箭頭函數返回對象,需要在對象外加()
var getTemp = id => ({ id: id, name: "cyan" });
箭頭函數與變量解構賦值,rest參數搭配使用
let fn = (...s) => { console.log(...s); } const full = ( first, last ) => { return first+ " " + last; } // 等同于 function full( first, last ) { return first+ " " + last; }
箭頭函數使用注意點
函數體內的this對象,就是定義時所在的對象,而不是使用時所在的對象。
不可以當作構造函數,也就是不可以使用new關鍵字。否則會拋出一個錯誤。(names is not a constrtucor)
不可以使用arguments對象,因為arguments對象不存在函數體內
不可以使用yield命令, 因此箭頭函數不能作用Generator函數。
this固定
this對象在箭頭函數中,是固定的。
this的指向固定化,有利于封裝回調函數。
this指向固定化,并不是因為箭頭函數內部有綁定this的機制,實際原因是箭頭函數根本沒有自己的this。導致內部的this就是外層代碼塊的this。正是因為它沒有this,所以也就不能夠作為構造函數。
箭頭函數的this永遠指向定義的時候的作用域,在各對象中定義,就指向那個對象。
當使用call()和apply()方法更改作用于時候,箭頭函數this是沒有更改成功。
function foo () { setTimeout(() => { console.log( "id:", this.id ); // 42 },100); setTimeout(function () { console.log("id: " ,this.id); //41 }, 1000); } var id = 21; foo.call({ id: 42 }); // 箭頭函數導致this總是只想函數定義生效時所在的對象
對比箭頭函數的setTimeout和ES5版本的普通回調函數:
// ES6 function foo () { setTimeout( () => { console.log("id:", this.id); }, 100 ); } foo(); //ES5 function fn2 () { let _this = this; setTimeout(function () { console.log("id:", _this.id); }, 100); } fn2(); // fn2中清楚的說明了,箭頭函數里面根本沒有自己的this,而是引用外層的this。
function foo() { return () => { console.log(this); // Object {id: 1} return () => { console.log(this); // Object {id: 1} return () => { console.log(this); // Object {id: 1} console.log("id:", this.id); // id: 1 }; }; }; } var f = foo.call( {id: 1} ); f()()();
箭頭函數中不存在:this,argumetns, super,new.target.相應的指向外層函數的對應 this,argumetns, super,new.target
function foo () { console.log("args:", arguments); // args: [1, 2, 3, 4] setTimeout( () => { console.log("args:", arguments); // args: [1, 2, 3, 4] },100 ); } foo(...[1, 2, 3, 4]);
箭頭函數嵌套
箭頭函數的嵌套:具有管道機制(前一個函數的輸出是后一個函數的輸入)。
let insert = (value) => ({ into: (array) => ({ after: (afterValue) => { array.splice( array.indexOf(afterValue) + 1, 0, value ); return array; } }) }); console.log( insert(2).into([1, 3]).after(1) ); //[1, 2, 3]尾調用優化
尾調用
尾調用:指某個函數的最后一步是調用另一函數。
尾調用是函數式編程的重要概念。
function fn (x) { return g(x); // 函數的最后一步調用函數 g() ,就是指的尾調用 } // 不屬于為調用的情形 function f1 ( x ) { let y = g(x); return y; } // 調用函數 g(x) 之后,還有賦值操作。 function f2 (x) { return g(x) + 1; } // 調用之后還有其它操作 function f3 ( x ) { g(x); } // f3 最后是返回 undefined // 尾調用不一定要出現在函數的尾部 function f ( x ) { if ( x > 0 ) { return m(x); } return n(x); }
尾調用優化
尾調用特殊之處:與其它調用不同的,在于它特殊的調用位置。
函數調用會在內存形成一個“調用記錄”,又稱“調用幀” (call frame),保存調用位置和內部變量等信息。
如果在函數A的內部調用函數B,那么在A的調用幀上方,還會形成一個B的調用幀。等到B運行結束,將結果返回到A,B的調用幀才會消失。如果函數B內部還調用函數C,那就還有一個C的調用幀,以此推類。所有的調用幀,就形成一個“調用棧”(call stack)
尾調用由于是函數的最后一步操作,所以不需要保留外層函數的調用幀,因為調用位置,內部變量等信息都不會再使用到。只要直接用內層函數的調用幀,取代外層函數的調用幀就可以。
尾調用優化:只保留內層函數的調用幀
function fn () { let m = 1; let n = 2; return g(m+n); // 執行到這一步,完全可以刪除fn()的調用幀,只保留 g(3) 的調用幀 } fn(); // 等同于 function fn () { return g(3); } fn(); // 等同于 g(3); // “尾調用優化”: 只保留內層函數的調用幀。
如果所有函數都是尾調用,那么完全可以做到每次執行時,調用幀只有一項,效果就大大節省內存。
注意:只有不再用到外層函數的內部變量,內層函數的調用幀才會取代外層函數的調用幀,否則就無法進行“尾調用優化”
尾遞歸
函數調用自身,稱為遞歸,如果尾調用自身,就稱之為:尾遞歸。
遞歸非常消耗內存,因為需要同時保存成千上百個調用幀,很容易發生"棧溢出"錯誤(stack voerflow).但是對于尾遞歸來說,由于只存在一個調用幀,所以不會發生"棧溢出"錯誤。
// 對比階乘 使用尾遞歸 和 不使用尾遞歸的情形 function factorial ( n ) { if ( n === 1 ) { return 1; } return n * factorial(n-1); } // 尾遞歸 function factorial2 ( n, total ) { if ( n === 1 ) { return total; } return factorial2( n-1, n * total ); } factorial2( 5, 1 ); // 120
遞歸函數的改寫
尾遞歸的實現,往往需要改寫遞歸函數,確保最后一步只調用自身。做到這一點的方法,就是把所有用到的內部變量改寫成函數的參數。
方法1:是在尾遞歸函數之外,再提供一個正常形式的函數。
function tailFactorial(n, total) { if (n === 1) return total; return tailFactorial(n - 1, n * total); } function factorial(n) { return tailFactorial(n, 1); } factorial(5) // 120
方法2:函數柯里化
柯里化:將多參數的函數轉換成單參數的形式。(函數式編程的重要概念)
方法3:ES6的函數默認值
function factorial ( n, total = 1 ) { if ( n === 1 ) return total; return factorial(n-1, n * total); } console.log( factorial(5) ); // 120;
遞歸本質上是一種循環操作。純粹的函數式編程語言沒有循環操作命令,所有的循環都是使用遞歸實現。
對于支持“尾調用優化”的語言,只需要知道循環可以用遞歸代替。
如果一旦使用遞歸,就最好使用尾遞歸。
尾遞歸只在嚴格模式下有效果
正常模式下:函數內部有兩個變量,可以跟蹤函數的調用棧。
func.argumetns : 返回調用時函數的參數
func.caller: 返回調用當前函數的那個函數。
尾遞歸優化生效的時候,函數的調用棧會改寫,因此argumetns,caller會失真。嚴格模式下禁用這兩個變量。 所以尾遞歸模式僅在嚴格模式下生效。
Set和Map數據結構 Set基本用法
Set對象是ES6中新增的一種表示集合的數據結構。它類似于數組,但是成員的值都是唯一的。沒有重復的值。
Set本身是一個構造函數,需要通過new關鍵字來生成。
Set函數可以接受一個數組(或類似數組的對象)作為參數,用來初始化。
得到的實例化對象是去重復的Set對象。
let s2 = new Set([123, 23, , 345, 345, 23]); console.log(s2); // Set {123, 23, undefined, 345} console.log(...s2); // 123 23 undefined 345 console.log( s2.size ); // 4
Set實例的屬性和方法
Set結構的實例屬性:
Set.prototype.constructor:構造函數,默認就是Set函數
Set.prototype.size: 返回Set實例的成員總數
Set實例方法分為:操作方法(用于操作數據) 和遍歷方法(用于遍歷成員)
操作方法
add(value): 添加某個值,返回Set結構本身。
delete(value):刪除某個值,返回一個布爾值,表示是否刪除成功。
has(value):返回一個布爾值,表示該值是否為Set成員。
cleart(): 清除所有成員,沒有返回值。
Array.form()可以將Set結構轉為數組。
var items = new Set([1, 2, 3, 5, 6, 3, 4]); console.log( Array.from(items) ); // [1, 2, 3, 5, 6, 4]
遍歷操作
keys(); 返回鍵名的迭代器。
values(); 返回鍵值的迭代器。
entries(); 返回鍵值對的迭代器。
forEach(); 使用回調函數遍歷每個成員。
Set結構的遍歷順序是插入順序。 使用場景比如:使用Set結構保存一個回調函數列表,調用的時候,就能夠保證按照添加順序調用。
由于Set結構沒有鍵名,只有鍵值(或者說鍵名和鍵值是同一個值)。 所以keys() 和values()是相同的。
let set = new Set(["pink", "cyan", "tan"]); for (let item of set.keys()) { console.log(item); } // pink // cyan // tan for (let item of set.values()) { console.log(item); } // pink // cyan // tan for (let item of set.entries()) { console.log(item); } // ["pink", "pink"] // ["cyan", "cyan"] // ["tan", "tan"]
Set結構的實例默認可遍歷,它的默認遍歷器生成函數就是它values方法。
console.log( Set.prototype[Symbol.iterator] === Set.prototype.values ); // true
遍歷的應用
數組去重
擴展運算符(...) 內部使用 for -of循環
實現交集,并集,差集。
與Array.from()搭配使用
let a = new Set([1, 2, 3]); let b = new Set([4, 3, 2]); // 并集 let union = new Set([...a, ...b]); // Set {1, 2, 3, 4} // 交集 let intersect = new Set([...a].filter(x => b.has(x))); // set {2, 3} // 差集 let difference = new Set([...a].filter(x => !b.has(x))); // Set {1}
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/86474.html
摘要:是制定的的規范,代表發表的新版本,等以此類推。持續優化更新變量聲明用于聲明常量,變量建議使用命令,建議不再使用命令,聲明的是全局變量,而則為局部變量。聲明的是全局變量也是頂層對象的屬性,則沒有綁定到頂層對象。 ECMAScript6 是 ECMA 制定的 JavaScript 的規范,ES6代表2016發表的新版本,ES7、ES5等以此類推。 (持續優化更新) 變量聲明 cons...
摘要:基本用法所聲明的變量,只在命令所在的代碼塊內有效。在循環中適合使用不存在變量提升不像那樣會發生變量提升現象暫時性死區只要塊級作用域內存在命令,它所聲明的變量就綁定這個區域,不再受外部的影響。塊級作用域實際上為新增了塊級作用域。 1 let 基本用法 所聲明的變量,只在let命令所在的代碼塊內有效。 { let b = 100; console.log(b); //100...
摘要:改變發布了最新版本,最新版本的模塊名稱都改成前綴,具體可前往英文官網查看,中文網站文檔尚未更新插件包都已更換名稱,以官網為準,不然會報錯構造函數完全更改,一臉懵逼原先個參數改為一個對象參數,參數字段具體如下圖具體查看介紹新的寫法根據最 改變 babel發布了最新版本,npm最新版本的模塊名稱都改成@babel前綴,具體可前往babel英文官網查看,中文網站文檔尚未更新 插件 plug...
摘要:正則表達式擴展構造函數支持傳入正則得到拷貝,同時可以用第二參修改修飾符引入新的修飾符中的修飾符有個加上的修飾符,一共個修飾符描述描述多行模式忽略大小寫模式全局匹配模式模式粘連模式模式為了兼容自己我們需要在一下情況使用該模式情況很明顯這個是不 正則表達式擴展 構造函數支持傳入正則得到拷貝,同時可以用第二參修改修飾符 var reg = /^abc/ig; var newReg_ig = ...
摘要:筆記和和是塊作用域的,是聲明常量用的。一個對象如果要有可被循環調用的接口,就必須在的屬性上部署遍歷器生成方法原型鏈上的對象具有該方法也可。這種方式會訪問注冊表,其中存儲了已經存在的一系列。這種方式與通過定義的獨立不同,注冊表中的是共享的。 ECMAScript6 筆記 let 和 const let和const是塊作用域的 ,const是聲明常量用的。 {let a = 10;} a ...
閱讀 1989·2021-11-22 14:45
閱讀 2606·2021-10-12 10:11
閱讀 773·2021-09-22 10:02
閱讀 1216·2019-08-30 15:55
閱讀 1145·2019-08-30 15:54
閱讀 3256·2019-08-30 15:54
閱讀 1190·2019-08-29 17:16
閱讀 3088·2019-08-28 17:55