ECMAScript6
目标
- 熟练使用ES6语法
- Symbol
- Promise对象
什么是ES6
ES6, 全称 ECMAScript 6.0 ,是 JavaScript 的下一个版本标准,2015.06 发版。
ES6 主要是为了解决 ES5 的先天不足,比如 JavaScript 里并没有类的概念,
Let、const和var的区别(重点)
ES6新增了let和const来声明变量,主要是解决var声明变量所造成的困扰和问题:
- var不能用于定义常量
- var可以重复声明变量
- var存在变量提升
- var不支持块级作用域
let和const解决了以上问题如下:
- 不可以重复声明变量
let site = 'itLike';
let site = 'itLike';
console.log(site);
- 不存在变量提升
console.log(site);
let site = 'itLike';
- 可以定义常量
const E = 2.718;
E = 2.71;
console.log(E);
// 引用类型
const LK = {
name:'itLike',
intro: '喜欢IT, 就上撩课(itLike.com)'
};
LK.name = '撩课';
console.log(LK);
- 块级作用域
如果用var定义变量,变量是通过函数或者闭包拥有作用域;但,现在用let定义变量,不仅仅可以通过函数/闭包隔离,还可以通过块级作用域隔离。
块级作用域用一组大括号定义一个块,使用 let 定义的变量在大括号的外部是访问不到的,此外,let声明的变量不会污染全局作用域。
{let site = 'itLike';}
console.log(site);
if(1){ let str = '04'; }
console.log(str);
解构赋值
解构赋值是对赋值运算符的扩展。
他是一种针对数组或者对象进行模式匹配,然后对其中的变量进行赋值。
在代码书写上简洁且易读,语义更加清晰明了;也方便了复杂对象中数据字段获取。
- 构的源,解构赋值表达式的右边部分。
- 解构的目标,解构赋值表达式的左边部分。
变量解构赋值(数组解构)
let nameArr = ['撩课', '小撩', '小煤球'];
let name1 = nameArr[0];
let name2 = nameArr[1];
let name3 = nameArr[2];
// 解构写法
let [name1, name2, name3] = nameArr;
console.log(name1, name2, name3);
变量解构赋值(对象解构)
// 写法1
let {name, age, sex}
= {name: '小煤球', age: 1, sex: '公'};
// 结果: 小煤球 1 公
console.log(name, age, sex);
// 写法2: 解构重命名
let {name: lkName, age: lkAge, sex: lkSex}
= {name: '小煤球', age: 1, sex: '公'};
// 结果: 小煤球 1 公
console.log(lkName, lkAge, lkSex);
// 写法3: 可以设置默认值
let {name, age, sex = '公'}
= {name: '小煤球', age: 1};
console.log(sex); // 公
// 写法4:省略解构
let [, , sex] = ['小煤球', 1, '公 '];
console.log(sex);
字符串、正则、数值、函数、数组、对象的扩展,箭头函数和普通函数区别
模板字符串
模板字符串用反引号()包含,变量用${}括起来; 在开发中使用是非常灵活的。
let name = '小煤球';
let sex = '公';
let result = `我叫 ${name} , 我是 ${sex} 的`;
console.log(result);
字符串扩展方法
- startsWith()
判断字符串是否以 XX 开头
let url = 'http://www.itlike.com';
console.log(url.startsWith('http')); // true
- endsWith()
判断字符串是否以 XX 结尾
let file = 'index.html';
console.log(file.endsWith('html')); // true
- includes
判断字符串中是否包含 XX
let str = 'liaoke';
console.log(str.includes('ao')); // true
- repeat()
拷贝n份
let title = '撩课在线';
console.log(title.repeat(100));
- padStart() / padEnd()
padStart()用于头部补全,
padEnd()用于尾部补全;
第一个参数用来指定字符串的最小长度,
第二个参数是用来补全的字符串。
// "2030111111"
let y1 = '2030'.padEnd(10, '1');
// "2030-11-22"
let y2 = '11-22'.padStart(10, '2030-MM-DD');
console.log(y1, y2);
延展操作符
- 延展数组
let arr1 = [ 'a', 'b', 'c'];
let arr2 = [1, 2, 3];
let result = [...arr1, ...arr2];
console.log(result);
// [ "a", "b", "c", 1, 2, 3 ]
- 延展对象
let smallDog = {name:'小煤球', age: 1};
let bigDog = {name: 'Python', age: 2};
let dog = {...smallDog, ...bigDog};
console.log(dog);
// {name: "Python", age: 2}
注意: 如果对象中的属性一致, 会被覆盖
- 开发应用场景
function getMinValue() {
console.log(Math.min(...arguments));
}
getMinValue(1, -99, 22, 10, 9); // -99
数值扩展
- 常量
Number.EPSILON ,属性表示 1 与大于 1 的最小浮点数之间的差。
它的值接近于 2.2204460492503130808472633361816E-16,或者 2-52。
测试数值是否在误差范围内:
0.1 + 0.2 === 0.3; // false
// 在误差范围内即视为相等
var equal = (Math.abs(0.1 - 0.3 + 0.2) < Number.EPSILON); // true
-
最大安全整数
安全整数:Number.MAX_SAFE_INTEGER
安全整数表示在 JavaScript 中能够精确表示的整数,安全整数的范围在 2 的 -53 次方到 2 的 53 次方之间(不包括两个端点),超过这个范围的整数无法精确表示。
最大安全整数
安全整数范围的上限,即 2 的 53 次方减 1 。 -
最小安全整数
安全整数范围的下限,即 2 的 53 次方减 1 的负数。
Number.MIN_SAFE_INTEGER
Number 对象新方法
- Number.isFinite()
用于检查一个数值是否为有限的( finite ),即不是 Infinity
console.log( Number.isFinite(1)); // true
console.log( Number.isFinite(0.1)); // true
// NaN 不是有限的
console.log( Number.isFinite(NaN)); // false
console.log( Number.isFinite(Infinity)); // false
console.log( Number.isFinite(-Infinity)); // false
// Number.isFinate 没有隐式的 Number() 类型转换,所有非数值都返回 false
console.log( Number.isFinite('foo')); // false
console.log( Number.isFinite('15')); // false
console.log( Number.isFinite(true)); // false
- Number.isNaN()
用于检查一个值是否为 NaN 。
console.log(Number.isNaN(NaN)); // true
console.log(Number.isNaN('true'/0)); // true
// 在全局的 isNaN() 中,以下皆返回 true,因为在判断前会将非数值向数值转换
// 而 Number.isNaN() 不存在隐式的 Number() 类型转换,非 NaN 全部返回 false
Number.isNaN("NaN"); // false
Number.isNaN(undefined); // false
Number.isNaN({}); // false
Number.isNaN("true"); // false
-
从全局移植到 Number 对象的方法
逐步减少全局方法,用于全局变量的模块化。
方法的行为没有发生改变。
Number.parseInt()
用于将给定字符串转化为指定进制的整数。
// 不指定进制时默认为 10 进制
Number.parseInt('12.34'); // 12
Number.parseInt(12.34); // 12
// 指定进制
Number.parseInt('0011',2); // 3
// 与全局的 parseInt() 函数是同一个函数
Number.parseInt === parseInt; // true
Number.parseFloat()
用于把一个字符串解析成浮点数。
Number.parseFloat('123.45') // 123.45
Number.parseFloat('123.45abc') // 123.45
// 无法被解析成浮点数,则返回 NaN
Number.parseFloat('abc') // NaN
// 与全局的 parseFloat() 方法是同一个方法
Number.parseFloat === parseFloat // true
Number.isInteger()
用于判断给定的参数是否为整数。
Number.isInteger(value)
Number.isInteger(0); // true
// JavaScript 内部,整数和浮点数采用的是同样的储存方法,因此 1 与 1.0 被视为相同的值
Number.isInteger(1); // true
Number.isInteger(1.0); // true
Number.isInteger(1.1); // false
Number.isInteger(Math.PI); // false
// NaN 和正负 Infinity 不是整数
Number.isInteger(NaN); // false
Number.isInteger(Infinity); // false
Number.isInteger(-Infinity); // false
Number.isInteger("10"); // false
Number.isInteger(true); // false
Number.isInteger(false); // false
Number.isInteger([1]); // false
// 数值的精度超过 53 个二进制位时,由于第 54 位及后面的位被丢弃,会产生误判
Number.isInteger(1.0000000000000001) // true
// 一个数值的绝对值小于 Number.MIN_VALUE(5E-324),即小于 JavaScript 能够分辨
// 的最小值,会被自动转为 0,也会产生误判
Number.isInteger(5E-324); // false
Number.isInteger(5E-325); // true
Number.isSafeInteger()
用于判断数值是否在安全范围内。
Number.isSafeInteger(Number.MIN_SAFE_INTEGER - 1); // false
Number.isSafeInteger(Number.MAX_SAFE_INTEGER + 1); // false
Math 对象的扩展
ES6 在 Math 对象上新增了 17 个数学相关的静态方法,这些方法只能在 Math 中调用。
- 普通计算
Math.cbrt
用于计算一个数的立方根。
Math.cbrt(1); // 1
Math.cbrt(0); // 0
Math.cbrt(-1); // -1
// 会对非数值进行转换
Math.cbrt('1'); // 1
// 非数值且无法转换为数值时返回 NaN
Math.cbrt('hhh'); // NaN
- Math.imul
两个数以 32 位带符号整数形式相乘的结果,返回的也是一个 32 位的带符号整数。
// 大多数情况下,结果与 a * b 相同
Math.imul(1, 2); // 2
// 用于正确返回大数乘法结果中的低位数值
Math.imul(0x7fffffff, 0x7fffffff); // 1
- Math.hypot
用于计算所有参数的平方和的平方根。
Math.hypot(3, 4); // 5
// 非数值会先被转换为数值后进行计算
Math.hypot(1, 2, '3'); // 3.741657386773941
Math.hypot(true); // 1
Math.hypot(false); // 0
// 空值会被转换为 0
Math.hypot(); // 0
Math.hypot([]); // 0
// 参数为 Infinity 或 -Infinity 返回 Infinity
Math.hypot(Infinity); // Infinity
Math.hypot(-Infinity); // Infinity
// 参数中存在无法转换为数值的参数时返回 NaN
Math.hypot(NaN); // NaN
Math.hypot(3, 4, 'foo'); // NaN
Math.hypot({}); // NaN
- Math.clz32
用于返回数字的32 位无符号整数形式的前导0的个数。
Math.clz32(0); // 32
Math.clz32(1); // 31
Math.clz32(0b01000000000100000000000000000000); // 1
// 当参数为小数时,只考虑整数部分
Math.clz32(0.5); // 32
// 对于空值或非数值,会转化为数值再进行计算
Math.clz32('1'); // 31
Math.clz32(); // 32
Math.clz32([]); // 32
Math.clz32({}); // 32
Math.clz32(NaN); // 32
Math.clz32(Infinity); // 32
Math.clz32(-Infinity); // 32
Math.clz32(undefined); // 32
Math.clz32('hhh'); // 32
- 数字处理
Math.trunc
用于返回数字的整数部分。
Math.trunc(12.3); // 12
Math.trunc(12); // 12
// 整数部分为 0 时也会判断符号
Math.trunc(-0.5); // -0
Math.trunc(0.5); // 0
// Math.trunc 会将非数值转为数值再进行处理
Math.trunc("12.3"); // 12
// 空值或无法转化为数值时时返回 NaN
Math.trunc(); // NaN
Math.trunc(NaN); // NaN
Math.trunc("hhh"); // NaN
Math.trunc("123.2hhh"); // NaN
Math.fround
用于获取数字的32位单精度浮点数形式。
// 对于 2 的 24 次方取负至 2 的 24 次方之间的整数(不含两个端点),返回结果与参数本身一致
Math.fround(-(2**24)+1); // -16777215
Math.fround(2 ** 24 - 1); // 16777215
// 用于将 64 位双精度浮点数转为 32 位单精度浮点数
Math.fround(1.234) // 1.125
// 当小数的精度超过 24 个二进制位,会丢失精度
Math.fround(0.3); // 0.30000001192092896
// 参数为 NaN 或 Infinity 时返回本身
Math.fround(NaN) // NaN
Math.fround(Infinity) // Infinity
// 参数为其他非数值类型时会将参数进行转换
Math.fround('5'); // 5
Math.fround(true); // 1
Math.fround(null); // 0
Math.fround([]); // 0
Math.fround({}); // NaN
判断
Math.sign
判断数字的符号(正、负、0)。
Math.sign(1); // 1
Math.sign(-1); // -1
// 参数为 0 时,不同符号的返回不同
Math.sign(0); // 0
Math.sign(-0); // -0
// 判断前会对非数值进行转换
Math.sign('1'); // 1
Math.sign('-1'); // -1
// 参数为非数值(无法转换为数值)时返回 NaN
Math.sign(NaN); // NaN
Math.sign('hhh'); // NaN
对数方法
Math.expm1()
用于计算 e 的 x 次方减 1 的结果,即 Math.exp(x) - 1 。
Math.expm1(1); // 1.718281828459045
Math.expm1(0); // 0
Math.expm1(-1); // -0.6321205588285577
// 会对非数值进行转换
Math.expm1('0'); //0
// 参数不为数值且无法转换为数值时返回 NaN
Math.expm1(NaN); // NaN
Math.log1p(x)
用于计算1 + x 的自然对数,即 Math.log(1 + x) 。
Math.log1p(1); // 0.6931471805599453
Math.log1p(0); // 0
Math.log1p(-1); // -Infinity
// 参数小于 -1 时返回 NaN
Math.log1p(-2); // NaN
Math.log10(x)
用于计算以 10 为底的 x 的对数。
Math.log1p(1); // 0.6931471805599453
Math.log1p(0); // 0
Math.log1p(-1); // -Infinity
// 参数小于 -1 时返回 NaN
Math.log1p(-2); // NaN
Math.log10(1); // 0
// 计算前对非数值进行转换
Math.log10('1'); // 0
// 参数为0时返回 -Infinity
Math.log10(0); // -Infinity
// 参数小于0或参数不为数值(且无法转换为数值)时返回 NaN
Math.log10(-1); // NaN
Math.log2()
用于计算 2 为底的 x 的对数。
Math.log2(1); // 0
// 计算前对非数值进行转换
Math.log2('1'); // 0
// 参数为0时返回 -Infinity
Math.log2(0); // -Infinity
// 参数小于0或参数不为数值(且无法转换为数值)时返回 NaN
Math.log2(-1); // NaN
-
双曲函数方法
Math.sinh(x): 用于计算双曲正弦。
Math.cosh(x): 用于计算双曲余弦。
Math.tanh(x): 用于计算双曲正切。
Math.asinh(x): 用于计算反双曲正弦。
Math.acosh(x): 用于计算反双曲余弦。
Math.atanh(x): 用于计算反双曲正切。 -
指数运算符
1 ** 2; // 1
// 右结合,从右至左计算
2 ** 2 ** 3; // 256
// **=
let exam = 2;
exam ** = 2; // 4
函数扩展
- 默认参数
基本用法
function fn(name,age=17){
console.log(name+","+age);
}
fn("Amy",18); // Amy,18
fn("Amy",""); // Amy,
fn("Amy"); // Amy,17
注意点:使用函数默认参数时,不允许有同名参数。
// 不报错
function fn(name,name){
console.log(name);
}
// 报错
//SyntaxError: Duplicate parameter name not allowed in this context
function fn(name,name,age=17){
console.log(name+","+age);
}
只有在未传递参数,或者参数为 undefined 时,才会使用默认参数,null 值被认为是有效的值传递。
function fn(name,age=17){
console.log(name+","+age);
}
fn("Amy",null); // Amy,null
函数参数默认值存在暂时性死区,在函数参数默认值表达式中,还未初始化赋值的参数值无法作为其他参数的默认值。
function f(x,y=x){
console.log(x,y);
}
f(1); // 1 1
function f(x=y){
console.log(x);
}
f(); // ReferenceError: y is not defined
- 不定参数
不定参数用来表示不确定参数个数,形如,...变量名,由...加上一个具名参数标识符组成。具名参数只能放在参数组的最后,并且有且只有一个不定参数。
基本用法
function f(...values){
console.log(values.length);
}
f(1,2); //2
f(1,2,3,4); //4
箭头函数
箭头函数提供了一种更加简洁的函数书写方式。基本语法是:
参数 => 函数体
function 函数名称(参数列表){
函数执行体
}
this指针的固化
var 函数名称 = (参数列表)=>{
函数执行体
}
基本用法:
var f = v => v;
//等价于
var f = function(a){
return a;
}
f(1); //1
当箭头函数没有参数或者有多个参数,要用 () 括起来。
var f = (a,b) => a+b;
f(6,2); //8
当箭头函数函数体有多行语句,用 {} 包裹起来,表示代码块,当只有一行语句,并且需要返回结果时,可以省略 {} , 结果会自动返回。
var f = (a,b) => {
let result = a+b;
return result;
}
f(6,2); // 8
- 单行语句返回形式
当箭头函数要返回对象的时候,为了区分于代码块,要用 () 将对象包裹起来
// 报错
var f = (id,name) => {id: id, name: name};
f(6,2); // SyntaxError: Unexpected token :
// 不报错
var f = (id,name) => ({id: id, name: name});
f(6,2); // {id: 6, name: 2}
注意点:没有 this、super、arguments 和 new.target 绑定。
var func = () => {
// 箭头函数里面没有 this 对象,
// 此时的 this 是外层的 this 对象,即 Window
console.log(this)
}
func(55) // Window
var func = () => {
console.log(arguments)
}
func(55); // ReferenceError: arguments is not defined
箭头函数体中的 this 对象,是定义函数时的对象,而不是使用函数时的对象。
function fn(){
setTimeout(()=>{
// 定义时,this 绑定的是 fn 中的 this 对象
console.log(this.a);
},0)
}
var a = 20;
// fn 的 this 对象为 {a: 19}
fn.call({a: 18}); // 18
var a = {
a: 18,
fn:function(){
setTimeout(()=>{
// 定义时,this 绑定的是 fn 中的 this 对象
console.log(this.a);
},0)
}
}
// call():
//将该函数的定义放到传入的对象下
//调用
不可以作为构造函数,也就是不能使用 new 命令,否则会报错
- 箭头函数适合使用的场景
ES6 之前,JavaScript 的 this 对象一直很令人头大,回调函数,经常看到 var self = this 这样的代码,为了将外部 this 传递到回调函数中,那么有了箭头函数,就不需要这样做了,直接使用 this 就行。
// 回调函数
var Person = {
'age': 18,
'sayHello': function () {
setTimeout(function () {
console.log(this.age);
});
}
};
var age = 20;
Person.sayHello(); // 20
var Person1 = {
'age': 18,
'sayHello': function () {
setTimeout(()=>{
console.log(this.age);
});
}
};
var age = 20;
Person1.sayHello(); // 18
所以,当我们需要维护一个 this 上下文的时候,就可以使用箭头函数。
- 不适合使用的场景
定义函数的方法,且该方法中包含 this
var Person = {
'age': 18,
'sayHello': ()=>{
console.log(this.age);
}
};
var age = 20;
Person.sayHello(); // 20
// 此时 this 指向的是全局对象
var Person1 = {
'age': 18,
'sayHello': function () {
console.log(this.age);
}
};
var age = 20;
Person1.sayHello(); // 18
// 此时的 this 指向 Person1 对象
需要动态 this 的时候
var button = document.getElementById('userClick');
button.addEventListener('click', () => {
this.classList.toggle('on');
});
button 的监听函数是箭头函数,所以监听函数里面的 this 指向的是定义的时候外层的 this 对象,即 Window,导致无法操作到被点击的按钮对象。
数组扩展
- 数组创建
Array.of()
将参数中所有值作为元素形成数组。
console.log(Array.of(1, 2, 3, 4)); // [1, 2, 3, 4]
// 参数值可为不同类型
console.log(Array.of(1, '2', true)); // [1, '2', true]
// 参数为空时返回空数组
console.log(Array.of()); // []
- Array.from()
将类数组对象或可迭代对象转化为数组。
// 参数为数组,返回与原数组一样的数组
console.log(Array.from([1, 2])); // [1, 2]
// 参数含空位
console.log(Array.from([1, , 3])); // [1, undefined, 3]
参数
Array.from(arrayLike[, mapFn[, thisArg]])
返回值为转换后的数组。
arrayLike
想要转换的类数组对象或可迭代对象。
console.log(Array.from([1, 2, 3])); // [1, 2, 3]
mapFn
可选,map 函数,用于对每个元素进行处理,放入数组的是处理后的元素。
console.log(Array.from([1, 2, 3], (n) => n * 2)); // [2, 4, 6]
thisArg
可选,用于指定 map 函数执行时的 this 对象。
let map = {
do: function(n) {
return n * 2;
}
}
let arrayLike = [1, 2, 3];
console.log(Array.from(arrayLike, function (n){
return this.do(n);
}),map); // [2, 4, 6]
- 类数组对象
一个类数组对象必须含有 length 属性,且元素属性名必须是数值或者可转换为数值的字符。
let arr = Array.from({
0: '1',
1: '2',
2: 3,
length: 3
});
console.log(); // ['1', '2', 3]
// 没有 length 属性,则返回空数组
let array = Array.from({
0: '1',
1: '2',
2: 3,
});
console.log(array); // []
// 元素属性名不为数值且无法转换为数值,返回长度为 length 元素值为 undefined 的数组
let array1 = Array.from({
a: 1,
b: 2,
length: 2
});
console.log(array1); // [undefined, undefined]
- 转换可迭代对象
转换 map
let map = new Map();
map.set('key0', 'value0');
map.set('key1', 'value1');
console.log(Array.from(map)); // [['key0', 'value0'],['key1',
// 'value1']]
转换 set
let arr = [1, 2, 3];
let set = new Set(arr);
console.log(Array.from(set)); // [1, 2, 3]
转换字符串
let str = 'abc';
console.log(Array.from(str)); // ["a", "b", "c"]
- 扩展的方法
查找
find()
查找数组中符合条件的元素,若有多个符合条件的元素,则返回第一个元素。
let arr = Array.of(1, 2, 3, 4);
console.log(arr.find(item => item > 2)); // 3
// 数组空位处理为 undefined
console.log([, 1].find(n => true)); // undefined
findIndex()
查找数组中符合条件的元素索引,若有多个符合条件的元素,则返回第一个元素索引。
let arr = Array.of(1, 2, 1, 3);
// 参数1:回调函数
// 参数2(可选):指定回调函数中的 this 值
console.log(arr.findIndex(item => item = 1)); // 0
// 数组空位处理为 undefined
console.log([, 1].findIndex(n => true)); //0
填充
fill()
将一定范围索引的数组元素内容填充为单个指定的值。
let arr = Array.of(1, 2, 3, 4);
// 参数1:用来填充的值
// 参数2:被填充的起始索引
// 参数3(可选):被填充的结束索引,默认为数组末尾
console.log(arr.fill(0,1,2)); // [1, 0, 3, 4]
copyWithin()
将一定范围索引的数组元素修改为此数组另一指定范围索引的元素。
// 参数1:被修改的起始索引
// 参数2:被用来覆盖的数据的起始索引
// 参数3(可选):被用来覆盖的数据的结束索引,默认为数组末尾
console.log([1, 2, 3, 4].copyWithin(0,2,)); // [3, 4, 3, 4]
// 参数1为负数表示倒数
console.log([1, 2, 3, 4].copyWithin(-2, 0)); // [1, 2, 1, 2]
console.log([1, 2, ,4].copyWithin(0, 2, 4)); // [, 4, , 4]
遍历
entries()
遍历键值对。
for(let [key, value] of ['a', 'b'].entries()){
console.log(key, value);
}
// 0 "a"
// 1 "b"
// 不使用 for... of 循环
let entries = ['a', 'b'].entries();
console.log(entries.next().value); // [0, "a"]
console.log(entries.next().value); // [1, "b"]
// 数组含空位
console.log([...[,-'a'].entries()]); // [[0, undefined], [1, "a"]]
keys()
遍历键名。
for(let key of ['a', 'b'].keys()){
console.log(key);
}
// 0
// 1
// 数组含空位
console.log([...[,'a'].keys()]); // [0, 1]
values()
遍历键值。
for(let value of ['a', 'b'].values()){
console.log(value);
}
// "a"
// "b"
// 数组含空位
console.log([...[,'a'].values()]); // [undefined, "a"]
包含
includes()
数组是否包含指定值。
注意:与 Set 和 Map 的 has 方法区分;Set 的 has 方法用于查找值;Map 的 has 方法用于查找键名。
// 参数1:包含的指定值
[1, 2, 3].includes(1); // true
// 参数2:可选,搜索的起始索引,默认为0
[1, 2, 3].includes(1, 2); // false
// NaN 的包含判断
[1, NaN, 3].includes(NaN); // true
嵌套数组转一维数组
flat()
console.log([1 ,[2, 3]].flat()); // [1, 2, 3]
// 指定转换的嵌套层数
console.log([1, [2, [3, [4, 5]]]].flat(2)); // [1, 2, 3, [4, 5]]
// 不管嵌套多少层
console.log([1, [2, [3, [4, 5]]]].flat(Infinity)); // [1, 2, 3, 4, 5]
// 自动跳过空位
console.log([1, [2, , 3]].flat()); // [1, 2, 3]
flatMap()
先对数组中每个元素进行了的处理,再对数组执行 flat() 方法。
// 参数1:遍历函数,该遍历函数可接受3个参数:当前元素、当前元素索引、原数组
// 参数2:指定遍历函数中 this 的指向
console.log([1, 2, 3].flatMap(n => [n * 2])); // [2, 4, 6]
对象的扩展
对象属性的简洁写法
- 属性简写
var foo = "bar";
var baz = { foo };
baz; // { foo: "baz" }
// 等价于
var baz = { foo: foo };
function f(x, y) {
return { x, y };
// 等价于: return { x: x, y: y };
}
f(1, 2); // {x: 1, y: 2}
- 方法简写
var obj = {
method() {
return "yuan is an animal";
}
}
// 等价于
var obj = {
method: function() {
return "yuan is an animal";
}
}
属性名表达式
在ES5中,定义对象的属性有两种方法
// 方法一:标识符作为属性名
obj.foo = true;
// 方法二:表达式作为属性名
obj["foo"] = true;
如果使用字面量方式定义对象,在ES5中只能使用方法一,而在ES6中可以使用方法二:
let yuan = "monkey";
let animal = {
"type": "animal",
[yuan]: "yuan is not monkey"
"monkey":"yuan is not monkey"
}
animal["type"]; // "animal"
animal[yuan]; // "yuan is not monkey"
animal["monkey"]; // "yuan is not monkey"
属性名定义方法:
let monkey = {
['yuan']() {
return "yuan is a monkey";
}
}
monkey.yuan(); // yuan is a monkey"
- 方法的name 属性
对象中的方法,也具有类似 function 的 name 属性:
let monkey = {
['yuan']() {
return "yuan is a monkey";
}
}
monkey.yuan.name; // "yuan"
- Object.is()
因在ES5中,并不能处理比较两个值是否严格相等,对于NaN,+0,-0等并不能做出严格相等来判断。
Object.is() 这个方法就是来弥补上述的缺陷的:
+0 === -0; // true
NaN === NaN; // false
Object.is(+0, -0); // false
Object.is(NaN, NaN); // true
- Object.assign()
基本用法
定义:将源对象(sourceN,不知一个源对象)的所有可枚举属性复制到目标对象上(target)。
使用方式:Object.assign(target, source1, source2, ..., sourceN)
let target = { x: 1};
let s1 = { y: 2 };
let s2 = { z: 3 };
Object.assign(target, s1, s2); // {x: 1, y: 2, z: 3}
注意点
1、如果目标对象与源对象用同名属性,或多个源对象具有同名属性,则后面的属性会覆盖前面的属性:
let target2 = { x: 1};
let s3 = { x: 3, y: 2 };
let s4 = { y: 4, z: 3 };
Object.assign(target2, s3, s4); // {x: 3, y: 4, z: 3}
2、如果参数不是对象,则会先转成对象,在返回:
typeof Object.assign(3); // "object"
3、若参数中有undefined 或者 null,这两个参数不能放在目标对象的位置,否则会报错:
Object.assign(undefined); // Cannot convert undefined or null to object at Function.assign (<anonymous>)
Object.assign(null); // Cannot convert undefined or null to object at Function.assign (<anonymous>)
4、除了字符串会以数组的形式复制到目标对象上,其他值都不会产生效果:
let a1 = 'yuan';
let a2 = true;
let a3 = 11;
let a4 = NaN;
Object.assign({}, a1, a2, a3, a4); // {0: "y", 1: "u", 2: "a", 3: "n"}
5、Object.assign()这个方法是对对象引用的复制,即是浅复制,而不是深复制。这里需要规避同名属性替换带来的问题:
var obj1 = { a: { b: 3, c: 4 } };
var obj2 = { a: { b: "yuan" } } ;
Object.assign(obj1, obj2); // { a: { b: "yuan" } }
obj1.a.b; // "yuan"
基本用途
给对象添加属性
class Geo {
constructor(x, y) {
Object.assign(this, x, y);
}
}
给对象添加方法
Object.assig(SomeClass.prototype, {
someMethod(arg1, arg2) { ... },
anotherMethod() { ... }
})
克隆对象
function clone(originObj) {
return Object.assign({}, originObj); // 将原始对象复制给空对象
}
合并多个对象
const mergeObjs = {
(target, ...sources) => Object.assign(target, ...sources);
}
为属性指定默认值
const DEFAULTS = {
logleve: 0,
outputFormat: 'html'
};
function processContent(options) {
retrun Object.assign({}, DEFAULTS, options);
}
属性的可枚举性(Enumerable)
ES5 中有3个操作会忽略 Enumerable 为 false 的属性:
1、for...in 循环:只遍历对象自身的和继承的可枚举性。
2、Object.keys():返回对象自身的所有可枚举属性的键名。
3、JSON.stringify():只串行化对象自身的可枚举属性。
在ES6 中,Object.assign() 操作会忽略 Enumerable 为false 的属性。
在上面四个操作中,只有 for...in 操作会返回继承的属性,为了不使问题复杂化,只关心对象自身的属性。所以,尽量不要使用 for...in 循环,用 Object.keys() 代替。
ES6还规定,所有 Class 的原型的方法都是不可枚举的。
属性的遍历
在ES6 中有五种方法来遍历对象的属性:
1、for...in
返回对象自身的和继承的可枚举属性(不含 Symbol 属性)。
2、Object.keys(ob)
返回一个数组,包括对象自身的(不含继承)所有可枚举属性(不含 Symbol 属性)。
3、Object.getOwnPropertyNames(obj)
返回一个数组,包括自身的所有属性(不含Symbol属性,但包括不可枚举属性)。
4、Object.getOwnPropertySymbols(obj)
返回一个数组。包含对象自身的所有 Symbol 属性。
5、Object.ownKeys(obj)
返回一个数组,包含对象所有的属性.。
以上五种方法都遵循同样的遍历顺序:
(1)、首先遍历所有属性名为数值的属性,按数字排序;
(2)、其次遍历所有属性名为字符串的属性,按照生成时间排序;
(3)、最后遍历所有属性名为 Symbol 的属性,按照生成时间排序。
八、Object.keys(),Object.values(),Object.entries()
1、Object.keys(obj)
返回一个数组,是由参数对象自身的(不含继承)所有可遍历属性的键名:
/* Array 对象 */
let arr = ["a", "b", "c"];
console.log(Object.keys(arr));
// ['0', '1', '2']
/* Object 对象 */
let obj = { foo: "bar", baz: 42 },
keys = Object.keys(obj);
// CCAC: Chrome Console Auto Copy
copy(keys);
// ["foo","baz"]
/* 类数组 对象 */
let obj = { 0 : "a", 1 : "b", 2 : "c"};
console.log(Object.keys(obj));
// ['0', '1', '2']
// 类数组 对象, 随机 key 排序 (遵循上述的属性的遍历顺序)
let anObj = { 100: 'a', 2: 'b', 7: 'c' };
console.log(Object.keys(anObj));
// ['2', '7', '100']
/* getFoo 是个不可枚举的属性 */
var my_obj = Object.create(
{},
{ getFoo : { value : function () { return this.foo } } }
);
my_obj.foo = 1;
console.log(Object.keys(my_obj));
// ['foo']
2、Object.values(obj)
返回一个数组,是由参数对象自身的(不含继承)所有可遍历属性的键值:
var obj = { foo: "bar", baz: 42 };
console.log(Object.values(obj)); // ['bar', 42]
// 类数组对象
var obj = { 0: 'a', 1: 'b', 2: 'c' };
console.log(Object.values(obj)); // ['a', 'b', 'c']
// 随机键值的类数组对象 (遵循上述的属性的遍历顺序)
var an_obj = { 100: 'a', 2: 'b', 7: 'c' };
console.log(Object.values(an_obj)); // ['b', 'c', 'a']
// getFoo 是不可枚举属性
var my_obj = Object.create({}, { getFoo: { value: function() { return this.foo; } } });
my_obj.foo = "bar";
console.log(Object.values(my_obj)); // ['bar']
// 参数是非对象会转变成对象
console.log(Object.values("foo")); // ['f', 'o', 'o']
Object.values() 会过滤属性名为 Symbol 值的属性:
Object.values({ [Symbol() ]: 123, "foo": "faa"}) // ["faa"]
3、Object.entries(obj)
返回一个数组,是由参数对象自身的(不含继承)所有可遍历属性的键值对数组:
const obj = { foo: 'bar', baz: 42 };
console.log(Object.entries(obj)); // [ ['foo', 'bar'], ['baz', 42] ]
// 类数组对象
const obj = { 0: 'a', 1: 'b', 2: 'c' };
console.log(Object.entries(obj)); // [ ['0', 'a'], ['1', 'b'], ['2', 'c'] ]
// 随机键值的类数组对象 (遵循上述的属性的遍历顺序)
const anObj = { 100: 'a', 2: 'b', 7: 'c' };
console.log(Object.entries(anObj)); // [ ['2', 'b'], ['7', 'c'], ['100', 'a'] ]
// getFoo 是不可枚举属性
const myObj = Object.create({}, { getFoo: { value() { return this.foo; } } });
myObj.foo = 'bar';
console.log(Object.entries(myObj)); // [ ['foo', 'bar'] ]
// 参数是非对象会转变成对象
console.log(Object.entries('foo')); // [ ['0', 'f'], ['1', 'o'], ['2', 'o'] ]
// 优雅地遍历键值
const obj = { a: 5, b: 7, c: 9 };
for (const [key, value] of Object.entries(obj)) {
console.log(`${key} ${value}`); // "a 5", "b 7", "c 9"
}
Object.entries() 的另一个用处:将对象转为真正的Map 数据结构:
var obj = { foo: "bar", baz: 42 };
var map = new Map(Object.entries(obj));
console.log(map); // Map { foo: "bar", baz: 42 }
iterator 和 for ...of 循环
Iterator 是 ES6 引入的一种新的遍历机制,迭代器有两个核心概念:
- 迭代器是一个统一的接口,它的作用是使各种数据结构可被便捷的访问,它是通过一个键为Symbol.iterator 的方法来实现。
- 迭代器是用于遍历数据结构元素的指针(如数据库中的游标)。
迭代器是带有特殊接口的对象。含有一个next()方法,调用返回一个包含两个属性的对象,分别是value和done,value表示当前位置的值,done表示是否迭代完,当为true的时候,调用next就无效了。
ES5中遍历集合通常都是 for循环,数组还有 forEach 方法,对象就是 for-in,ES6 中又添加了 Map 和 Set,而迭代器可以统一处理所有集合数据的方法。迭代器是一个接口,只要你这个数据结构暴露了一个iterator的接口,那就可以完成迭代。ES6创造了一种新的遍历命令for...of循环,Iterator接口主要供for...of消费。
for遍历
const fruits = ['apple','coconut','mango','durian'];
//for循环数组,通过下标取得每一项的值
for (let i = 0; i < fruits.length; i++) {
console.log(fruits[i]);
}
forEach遍历
//数组的forEach方法,相对for循环语法更简单
fruits.forEach(fruit => {
console.log(fruit);
})
//forEach有个问题是不能终止循环
fruits.forEach(fruit => {
if(fruit === 'mango' ){
break; //Illegal break statement
}
console.log(fruit);
})
forin 遍历
//for...in循环,遍历数组对象的属性,MDN不推荐使用for...in遍历数组
//for...in循环会打印出非数字属性
const fruits = ['apple','coconut','mango','durian'];
fruits.fav = 'my favorite fruit';
for(let index in fruits){
console.log(fruits[index]); //...my favorite fruit
}
forof遍历
const fruits = ['apple','coconut','mango','durian'];
fruits.fav = 'my favorite fruit';
//ES6中的for...of循环,遍历属性值
for(let fruit of fruits){
console.log(fruit);
}
//支持终止循环,也不会遍历非数字属性
for(let fruit of fruits){
if(fruit === 'mango' ){
break;
}
console.log(fruit); //apple coconut durian
}
应用场景
//arguments是个类数组对象,通常先要将其转换为数组才能遍历,但for...of可以直接遍历
function sum() {
let sum = 0
for(let num of arguments){
sum += num
}
console.log(sum); //15
}
sum(1,2,3,4,5)
//遍历字符串
let name = 'Asher';
for (let char of name){
console.log(char); //A s h e r
}
//遍历nodelists,效果如下图
<style type="text/css">
.completed {
text-decoration: line-through;
}
</style>
<body>
<ul>
<li>yoga</li>
<li>boxing</li>
<li>press</li>
</ul>
<script type="text/javascript">
const lis = document.querySelectorAll('li');
for(let li of lis){
li.addEventListener('click',function(){
this.classList.toggle('completed');
})
}
</script>
</body>
set 和 map 数据结构
基本用法
set 数据容器 能够存储无重复值数据的有序列表
- 通过 new set() 方法创建容器 通过add() 方法添加
- set.size获取存储的数据的数量
var set = new Set()
set.add(1);
set.add('1');
console.log(set)
console.log(set.size)
- Set内部使用Object.is()方法来判断两个数据项是否相等
- 利用数组来构造set 且set 构造器不会存在重复的数据
var set1 = new Set([1,2,3,3,3,3,3,2]) // 结果数组会去重
-
可以使用has()方法来判断某个值是否存在于Set中
-
使用delete()方法从Set中删除某个值,或者使用clear()方法从Set中删除所有值
set1.delete(1)
console.log(set1)
set1.clear()
console.log(set1)
- forEach 遍历set
set2.forEach(function(value,key){
console.log(value)
console.log(key)
})
- 将数组转换成set 直接将数组放在new Set()参数中
ES6中提供了Map数据结构,能够存放键值对,其中,键的去重是通过Object.is()方法进行比较,键的数据类型可以是基本类型数据也可以是对象,而值也可以是任意类型数据。
- 创建map
var map = new Map()
- 使用set()方法可以给Map添加键值对 ,能够自动去重,和set原理一样,添加相同的元素会进行去重处理
var map = new Map()
console.log(map)
map.set('title','baidu')
map.set('year','2018');
map.set('year','2018');
map.set('year','2018');
- 通过get()方法可以从Map中提取值,size方法同set一样,获取数组长度
console.log(map.get('year')) // 2018
console.log(map.size) // 2
- 同set一样,也有 has(),delete(),clear() 方法
map.delete('title');
map.clear();
console.log(map.has('year'));
- forEach 遍历
map.forEach(function(value,key){
console.log(value) // 值 2018
console.log(key) // 健 year
})
set和map 的区别
都是用来存储数据用的,但是存储的数据格式不同
set 直接存储 任意类型数据
map 存储数据的时候,必须以key,value的形式,
set 使用forEach 遍历的时候,key和value值是一样的
而map 遍历的时候,key就是存进去的对象的key,value就是存在的值