function detanx(x = 1) { ... }
// 搭配解構賦值
function detanx({name, age = 10} = {name: 'detanx', age: 10}) {...}
// 不徹底解構,
function detanx({name, age = 10} = {name: 'detanx'}) {...}
detanx({age: 12}) // undefined 12
複製代碼
undefined
。function detanx(x = null, y = 6) {
console.log(x, y);
}
detanx(undefined, null); // null null
複製代碼
length
屬性,將返回沒有指定默認值的參數個數。也就是說,指定了默認值後,length
屬性將失真。(function (a) {}).length // 1
(function (a = 5) {}).length // 0
(function (a, b, c = 5) {}).length // 2
複製代碼
(function (a = 0, b, c) {}).length // 0
(function (a, b = 1, c) {}).length // 1
複製代碼
length
爲0
.(function(...args) {}).length // 0
複製代碼
context
)。var x = 1;
function detanx(x, y = x) {
console.log(y);
}
f(2) // 2
function detanx(x = x, y = x) {
console.log(x, y);
}
f(undefined,2) // ReferenceError: Cannot access 'x' before initialization
複製代碼
function throwIfMissing() {
throw new Error('Missing parameter');
}
function foo(mustBeProvided = throwIfMissing()) {
return mustBeProvided;
}
foo()
// Error: Missing parameter
複製代碼
mustBeProvided
的默認值等於throwIfMissing
函數的運行結果(注意函數名throwIfMissing
以後有一對圓括號),這代表參數的默認值不是在定義時執行,而是在運行時執行。若是參數已經賦值,默認值中的函數就不會運行。ES5
開始,函數內部能夠設定爲嚴格模式。ES2016
作了一點修改,規定只要函數參數使用了默認值、解構賦值、或者擴展運算符,那麼函數內部就不能顯式設定爲嚴格模式,不然會報錯。// 報錯
function doSomething(a, b = a) {
'use strict';
// code
}
// 報錯
const doSomething = function ({a, b}) {
'use strict';
};
// 報錯
const doSomething = (...a) => {
'use strict';
};
const obj = {
// 報錯
doSomething({a, b}) {
'use strict';
}
};
複製代碼
// 第一種是設定全局性的嚴格模式,這是合法的。
'use strict';
function doSomething(a, b = a) {
// code
}
// 第二種是把函數包在一個無參數的當即執行函數裏面。
const doSomething = (function () {
'use strict';
return function(value = 42) {
return value;
};
}());
複製代碼
this
對象,就是定義時所在的對象,而不是使用時所在的對象。new
命令,不然會拋出一個錯誤。arguments
對象,該對象在函數體內不存在。若是要用,能夠用 rest
參數代替。yield
命令,所以箭頭函數不能用做 Generator
函數。// 柯里化
const curry = (fn, arr = []) => (...args) => (
arg => arg.length === fn.length
? fn(...arg)
: curry(fn, arg)
)([...arr, ...args]);
let curryTest = curry( (a, b) => a + b );
curryTest(1, 2) //返回3
curryTest(1)(2) //返回3
複製代碼
function add(...values) {
let sum = 0;
for (var val of values) {
sum += val;
}
return sum;
}
add(2, 5, 3) // 10
複製代碼
apply
方法fn.apply(this, arr)
。// ES5 的寫法
const args = [0, 1, 2];
function f(x, y, z) {
// ...
}
f.apply(null, args);
// ES6的寫法
function f(x, y, z) {
// ...
}
f(...args);
複製代碼
const a = [1]
const b = [2];
// 複製數組
const c = [...a]; // [1]
// 合併數組
[...a, ...b, ...c] // [1, 2, 1]
// 轉換字符串爲數組
[...'detanx'] // ['d', 'e', 't', 'a', 'n', 'x']
複製代碼
Iterator
接口的對象Iterator
)接口的對象(參閱 Iterator
一章),均可以用擴展運算符轉爲真正的數組。Number.prototype[Symbol.iterator] = function*() {
let i = 0;
let num = this.valueOf();
while (i < num) {
yield i++;
}
}
console.log([...5]) // [0, 1, 2, 3, 4]
複製代碼
arguments
對象是一個類數組;querySelectorAll
方法返回的是一個NodeList
對象。它也是一個相似數組的對象。let nodeList = document.querySelectorAll('div');
let array = [...nodeList];
複製代碼
Array.from
的做用和擴展運算符相似,均可以將類數組轉爲數組,但它還能夠接收第二參數,做用相似於數組的map方法,用來對每一個元素進行處理,將處理後的值放入返回的數組。// 生成0-99的數組
Array.from(new Array(100), (x, i) => i);
複製代碼
Array.prototype.copyWithin(target, start = 0, end = this.length)
複製代碼
- target(必需):從該位置開始替換數據。若是爲負值,表示倒數。
- start(可選):從該位置開始讀取數據,默認爲 0。若是爲負值,表示從末尾開始計算。
- end(可選):到該位置前中止讀取數據,默認等於數組長度。若是爲負值,表示從末尾開始計算。
複製代碼
// 將3號位複製到0號位
[1, 2, 3, 4, 5].copyWithin(0, 3, 4)
// [4, 2, 3, 4, 5]
// -2至關於3號位,-1至關於4號位
[1, 2, 3, 4, 5].copyWithin(0, -2, -1)
// [4, 2, 3, 4, 5]
// 將3號位複製到0號位
[].copyWithin.call({length: 5, 3: 1}, 0, 3)
// {0: 1, 3: 1, length: 5}
// 將2號位到數組結束,複製到0號位
let i32a = new Int32Array([1, 2, 3, 4, 5]);
i32a.copyWithin(0, 2);
// Int32Array [3, 4, 5, 4, 5]
// 對於沒有部署 TypedArray 的 copyWithin 方法的平臺
// 須要採用下面的寫法
[].copyWithin.call(new Int32Array([1, 2, 3, 4, 5]), 0, 3, 4);
// Int32Array [4, 2, 3, 4, 5]
複製代碼
this
。區別是find
沒有找到返回的是undefined
,findIndex
返回的是-1
。這兩個方法均可以發現NaN
,彌補了數組的indexOf
方法的不足。[1, 4, -5, 10].find((n) => n > 10) // undefined
[1, 5, 10, 15].findIndex(function(value, index, arr) {
return value > 15;
}) // -1
// 綁定this
function f(v){
return v > this.age;
}
let person = {name: 'John', age: 20};
[10, 12, 26, 15].find(f, person); // 26
// 找出NaN
[NaN].indexOf(NaN)
// -1
[NaN].findIndex(y => Object.is(NaN, y))
// 0
複製代碼
['a', 'b', 'c'].fill(7)
// [7, 7, 7]
new Array(3).fill(7)
// [7, 7, 7]
複製代碼
上面代碼代表,fill方法用於空數組的初始化很是方便。數組中已有的元素,會被所有抹去。node
fill方法還能夠接受第二個和第三個參數,用於指定填充的起始位置和結束位置。es6
- ['a', 'b', 'c'].fill(7, 1, 2)
// ['a', 7, 'c']
複製代碼
上面代碼表示,fill方法從 1 號位開始,向原數組填充 7,到 2 號位以前結束。數組
注意,若是填充的類型爲對象,那麼被賦值的是同一個內存地址的對象,而不是深拷貝對象。bash
let arr = new Array(3).fill({name: "Mike"});
arr[0].name = "Ben";
arr
// [{name: "Ben"}, {name: "Ben"}, {name: "Ben"}]
let arr = new Array(3).fill([]);
arr[0].push(5);
arr
// [[5], [5], [5]]
複製代碼
includes
方法相似。與indexOf
的區別是includes
直接返回一個布爾值,indexOf
須要再判斷返回值是否爲-1
。當咱們須要一個變量是否爲多個枚舉值之一時就可使用includes
。const name = 'detanx';
const nameArr = ['detanx', 'det', 'tanx']
// 使用includes作判斷
if(nameArr.indexOf(name) !== -1) { ... }
=>
if(nameArr.includes(name)) { ... }
// 判斷name是不是 detanx,tanx,det
if(name === 'detanx' || name === 'det' || name === 'tanx') {...}
=>
nameArr.includes(name);
複製代碼
undefined
,一個位置的值等於undefined
,依然是有值的。)。好比,Array
構造函數返回的數組都是空位。Array(3) // [, , ,]
複製代碼
forEach()
、 filter()
、 reduce()
、 every()
和some()
都會跳過空位。// forEach方法
[,'a'].forEach((x,i) => console.log(i)); // 1
// filter方法
['a',,'b'].filter(x => true) // ['a','b']
// every方法
[,'a'].every(x => x==='a') // true
// reduce方法
[1,,2].reduce((x,y) => x+y) // 3
// some方法
[,'a'].some(x => x !== 'a') // false
複製代碼
map()
會跳過空位,但會保留這個值。// map方法
[,'a'].map(x => 1) // [,1]
複製代碼
join()
和toString()
會將空位視爲undefined
,而undefined
和null
會被處理成空字符串。// join方法
[,'a',undefined,null].join('#') // "#a##"
// toString方法
[,'a',undefined,null].toString() // ",a,,"
複製代碼
Array.from()
、...
、entries()
、keys()
、values()
、find()
和findIndex()
會將空位處理成undefined
。Array.from(['a',,'b'])
// [ "a", undefined, "b" ]
[...['a',,'b']]
// [ "a", undefined, "b" ]
// entries()
[...[,'a'].entries()] // [[0,undefined], [1,"a"]]
// keys()
[...[,'a'].keys()] // [0,1]
// values()
[...[,'a'].values()] // [undefined,"a"]
// find()
[,'a'].find(x => true) // undefined
// findIndex()
[,'a'].findIndex(x => true) // 0
複製代碼
[,'a','b',,].copyWithin(2,0) // [,"a",,"a"]
new Array(3).fill('a') // ["a","a","a"]
let arr = [, ,];
for (let i of arr) {
console.log(1);
}
// 1
// 1
複製代碼
Descriptor
),用來控制該屬性的行爲。 Object.getOwnPropertyDescriptor
方法能夠獲取該屬性的描述對象。let obj = { foo: 123 };
Object.getOwnPropertyDescriptor(obj, 'foo')
// {
// value: 123,
// writable: true,
// enumerable: true,
// configurable: true
// }
複製代碼
描述對象的enumerable
屬性,稱爲「可枚舉性」,若是該屬性爲false
,就表示某些操做會忽略當前屬性。數據結構
目前,有四個操做會忽略enumerable
爲false
的屬性。app
for...in
循環:只遍歷對象自身的和繼承的可枚舉的屬性。Object.keys()
:返回對象自身的全部可枚舉的屬性的鍵名。JSON.stringify()
:只串行化對象自身的可枚舉的屬性。Object.assign()
: 忽略enumerable
爲false
的屬性,只拷貝對象自身的可枚舉的屬性。Symbol
類型的屬性只能經過getOwnPropertySymbols
方法獲取。ide
ES5
咱們判斷一個深層級的對象是否有某一個key
須要一層一層去判斷,如今咱們能夠經過?.
的方式去獲取。// es5
// 錯誤的寫法(當某一個key不存在undefined.key就會代碼報錯)
const firstName = message.body.user.firstName;
// 正確的寫法
const firstName = (message
&& message.body
&& message.body.user
&& message.body.user.firstName) || 'default';
// es6
const firstName = message?.body?.user?.firstName || 'default';
複製代碼
obj?.prop
// 對象屬性obj?.[expr]
// 對象屬性func?.(...args)
// 函數或對象方法的調用a?.b
// 等同於
a == null ? undefined : a.b
a?.[x]
// 等同於
a == null ? undefined : a[x]
a?.b()
// 等同於
a == null ? undefined : a.b()
a?.()
// 等同於
a == null ? undefined : a()
複製代碼
?.
運算符至關於一種短路機制,只要不知足條件,就再也不往下執行。// 當name不存在時,後面的就再也不執行
user?.name?.firstName
複製代碼
delete a?.b
// 等同於
a == null ? undefined : delete a.b
複製代碼
a
是undefined
或null
,會直接返回undefined
,而不會進行delete
運算。(a?.b).c
// 等價於
(a == null ? undefined : a.b).c
複製代碼
// 構造函數
new a?.()
new a?.b()
// 鏈判斷運算符的右側有模板字符串
a?.`{b}`
a?.b`{c}`
// 鏈判斷運算符的左側是 super
super?.()
super?.foo
// 鏈運算符用於賦值運算符左側
a?.b = c
複製代碼
foo?.3:0
被解析成foo ? .3 : 0
,所以規定若是?.
後面緊跟一個十進制數字,那麼?.
再也不被當作是一個完整的運算符,而會按照三元運算符進行處理,也就是說,那個小數點會歸屬於後面的十進制數字,造成一個小數。Null
運算判斷符??
null
或undefined
,有時候須要爲它們指定默認值。常見作法是經過||
運算符指定默認值。||
運算符當左邊爲空字符串或者false
的時候也會設置默認值,??
運算符只有當左邊是null
或undefined
纔會設置默認值。// || 運算
const a = '';
b = a || 1;
console.log(b) // 1
// ?? 運算
b = a ?? 1;
console.log(b) // ''
複製代碼
??
有一個運算優先級問題,它與&&
和||
的優先級孰高孰低,因此在一塊兒使用時必須用括號代表優先級,不然會報錯。Object.is('foo', 'foo')
// true
Object.is({}, {})
// false
複製代碼
+0 === -0 //true
NaN === NaN // false
Object.is(+0, -0) // false
Object.is(NaN, NaN) // true
複製代碼
source
)的全部自身屬性(不拷貝繼承屬性)、自身的Symbol
值屬性和可枚舉屬性(屬性的enumerable
爲true
),複製到目標對象(target
)。第一個參數爲目標對象,後面的都是源對象。const target = { a: 1, b: 1 };
const source1 = { b: 2, c: 2 };
const source2 = { c: 3 };
Object.assign(target, source1, source2);
target // {a:1, b:2, c:3}
// 拷貝Symbol值屬性
Object.assign({ a: 'b' }, { [Symbol('c')]: 'd' })
// { a: 'b', Symbol(c): 'd' }
複製代碼
null
和undefined
會報錯。Object.assign(undefined) // 報錯
Object.assign(null) // 報錯
複製代碼
Object.assign
方法實行的是淺拷貝,而不是深拷貝。Object.assign
的處理方法是替換,而不是添加。Object.assign
能夠用來處理數組,可是會把數組視爲對象。Object.assign([1, 2, 3], [4, 5])
// [4, 5, 3]
複製代碼
Object.assign
只能進行值的複製,若是要複製的值是一個取值函數,那麼將求值後再複製。const source = {
get foo() { return 1 }
};
const target = {};
Object.assign(target, source)
// { foo: 1 }
複製代碼
let obj = { a: 1, b: 2, c: 3 };
for (let key of Object.keys(obj)) {
console.log(key); // 'a', 'b', 'c'
console.log(obj[key]) // 1, 2, 3
}
複製代碼
let obj = { a: 1, b: 2, c: 3 };
for (let value of Object.values(obj)) {
console.log(value); // 1, 2, 3
}
// 數值的鍵名
const obj = { 100: 'a', 2: 'b', 7: 'c' };
Object.values(obj)
// ["b", "c", "a"]
複製代碼
b
、c
、a
。Object.values
會過濾屬性名爲 Symbol
值的屬性。Object.entries()
方法返回一個數,除了返回值類型不同以外,其餘的行爲和Object.values
基本一致。for (let [key, value] of Object.entries(obj)) {
console.log([key, value]); // ['a', 1], ['b', 2], ['c', 3]
}
複製代碼
Object.entries(obj).forEach([key, value] => {
console.log([key, value]); // ['a', 1], ['b', 2], ['c', 3]
})
複製代碼
Object.fromEntries([
['foo', 'bar'],
['baz', 42]
])
// { foo: "bar", baz: 42 }
複製代碼
// 例一
const entries = new Map([
['foo', 'bar'],
['baz', 42]
]);
Object.fromEntries(entries)
// { foo: "bar", baz: 42 }
// 例二
const map = new Map().set('foo', true).set('bar', false);
Object.fromEntries(map)
// { foo: true, bar: false }
複製代碼