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 }
复制代码