侧边栏壁纸
博主头像
江祎铭博主等级

不知江月待何人

  • 累计撰写 177 篇文章
  • 累计创建 3 个标签
  • 累计收到 119 条评论
标签搜索

目 录CONTENT

文章目录
ES6

ES6.md

江祎铭
2022-03-08 / 0 评论 / 1 点赞 / 402 阅读 / 26,568 字 / 正在检测是否收录...
温馨提示:
本文最后更新于 2022-03-08,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。
广告 广告

ECMAScript6

目标

  • 熟练使用ES6语法
  • 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

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; // { 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');
  • 通过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就是存在的值

参考资料

https://www.jianshu.com/p/3bb77516fa7e

Promise

Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。

Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。

Promise对象有以下两个特点

  • 对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:Pending(进行中)、Resolved(已完成,又称 Fulfilled)和Rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。

  • 一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从Pending变为Resolved和从Pending变为Rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果。就算改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。

有了Promise对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise对象提供统一的接口,使得控制异步操作更加容易。

使用场景

  • 主要用于异步计算
  • 可以将异步操作队列化,按照期望的顺序执行,返回符合预期的结果
  • 可以在对象之间传递和操作promise,帮助我们处理队列

在JavaScript中,所有代码都是单线程执行的。这样导致JavaScript的所有网络操作,浏览器事件,都必须是异步执行。异步执行可以用回调函数实现:

function callback() {
    console.log('Done');
}

console.log('before setTimeout()');
setTimeout(callback, 1000); // 1秒钟后调用callback函数
console.log('after setTimeout()');
=
//执行结果
// before setTimeout()
// after setTimeout()
// (等待1秒后)
// Done

异步操作会在将来的某个时间点触发一个函数调用。

异步操作的常见语法

  • 事件监听
document.getElementById('#start').addEventListener('click', start, false);
function start() {
  // 响应事件,进行相应的操作
}
// jquery on 监听
$('#start').on('click', start)
  • 回调
// 比较常见的有ajax
$.ajax('http://www.wyunfei.com/', {
 success (res) {
   // 这里可以监听res返回的数据做回调逻辑的处理
 }
})

// 或者在页面加载完毕后回调
$(function() {
 // 页面结构加载完成,做回调逻辑处理
})

Promise

  • 初始化Promise对象

//写法一
new Promise(
  function (resolve, reject) {
    // 一段耗时的异步操作
    resolve('成功') // 数据处理完成
    // reject('失败') // 数据处理出错
  }
).then(
  (res) => {console.log(res)},  // 成功
  (err) => {console.log(err)} // 失败
)

//写法二
var p1 = new Promise(function (resolve, reject) {
  // 一段耗时的异步操作
  resolve('成功') // 数据处理完成
  // reject('失败') // 数据处理出错
});

var p2 = p1.then(function (result) {
    console.log('成功:' + result);
});
var p3 = p2.catch(function (reason) {
    console.log('失败:' + reason);
});

参数:函数类型

  • resolve作用是,将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;

  • reject作用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。

  • promise有三个状态:
    1、pending[待定]初始状态
    2、resolve[实现]操作成功
    3、rejected[被否决]操作失败

  • 当promise状态发生改变,就会触发then()里的响应函数处理后续步骤;
    promise状态一经改变,不会再变。
    Promise对象的状态改变,只有两种可能:
    从pending变为fulfilled
    从pending变为rejected。
    这两种情况只要发生,状态就凝固了,不会再变了。

Promise方法

then()方法

then 方法就是把原来的回调写法分离出来,在异步操作执行完后,用链式调用的方式执行回调函数。
而 Promise 的优势就在于这个链式调用。我们可以在 then 方法中继续写 Promise 对象并返回,然后继续调用 then 来进行回调操作。
可有两个参数,第一个是成功 resolve 调用的方法,第二个是失败 reject 调用的方法

then方法注册 当resolve(成功)/reject(失败)的回调函数

// onFulfilled 是用来接收promise成功的值
// onRejected 是用来接收promise失败的原因
promise.then(onFulfilled, onRejected);

then方法是异步执行的

  • resolve(成功) onFulfilled会被调用
const promise = new Promise((resolve, reject) => {
   resolve('fulfilled'); // 状态由 pending => fulfilled
});
promise.then(result => { // onFulfilled
    console.log(result); // 'fulfilled'
}, reason => { // onRejected 不会被调用

})
  • reject(失败) onRejected会被调用
const promise = new Promise((resolve, reject) => {
   reject('rejected'); // 状态由 pending => rejected
});
promise.then(result => { // onFulfilled 不会被调用

}, reason => { // onRejected
    console.log(rejected); // 'rejected'
})
  • 案例:下一步的的操作需要使用上一部操作的结果。(这里使用 setTimeout 模拟异步操作),正式开发可以用 ajax 异步
        function buy(){
            console.log("开始买笔");
            var p = new Promise(function(resolve,reject){
                setTimeout(function(){
                    console.log("买了笔芯");
                    resolve("数学作业");
                },1000);
            });
            return p;
        }
        //写作业
        function work(data){
            console.log("开始写作业:"+data);
            var p = new Promise(function(resolve,reject){
                setTimeout(function(){
                    console.log("写完作业");
                    resolve("作业本");
                },1000);
            });
            return p;
        }

        function out(data){
            console.log("开始上交:"+data);
            var p = new Promise(function(resolve,reject){
                setTimeout(function(){
                    console.log("上交完毕");
                    resolve("得分:A");
                },1000);
            });
            return p;
        }
        /* 不建议使用这种方式
        buy().then(function(data){
            return work(data);
        }).then(function(data){
            return out(data);
        }).then(function(data){
            console.log(data);
        });*/

        //推荐这种简化的写法
        buy().then(work).then(out).then(function(data){
            console.log(data);
        });
  • ajax案例
// ajax函数将返回Promise对象:
function ajax(method, url, data) {
    var request = new XMLHttpRequest();
    return new Promise(function (resolve, reject) {
        request.onreadystatechange = function () {
            if (request.readyState === 4) {
                if (request.status === 200) {
                    resolve(request.responseText);
                } else {
                    reject(request.status);
                }
            }
        };
        request.open(method, url);
        request.send(data);
    });
}

var p = ajax('GET', '/api/categories');
p.then(function (text) { // 如果AJAX成功,获得响应内容
    log.innerText = text;
}).catch(function (status) { // 如果AJAX失败,获得响应代码
    log.innerText = 'ERROR: ' + status;
});

resolve()方法

reject()方法

resolve 方法把 Promise 的状态置为完成态(Resolved),这时 then 方法就能捕捉到变化,并执行“成功”情况的回调。
而 reject 方法就是把 Promise 的状态置为已失败(Rejected),这时 then 方法执行“失败”情况的回调(then 方法的第二参数)

catch()方法:

  • 它可以和 then 的第二个参数一样,用来指定 reject 的回调
  • 它的另一个作用是,当执行 resolve 的回调(也就是上面 then 中的第一个参数)时,如果抛出异常了(代码出错了),那么也不会报错卡死 js,而是会进到这个 catch 方法中。

all()方法:

Promise 的 all 方法提供了并行执行异步操作的能力,并且在所有异步操作执行完后才执行回调。
只有全部为resolve才会调用 通常会用来处理 多个并行异步操作



const p1 = new Promise((resolve, reject) => {
    resolve(1);
});

const p2 = new Promise((resolve, reject) => {
    resolve(2);
});

const p3 = new Promise((resolve, reject) => {
    reject(3);
});

Promise.all([p1, p2, p3]).then(data => {
    console.log(data); // [1, 2, 3] 结果顺序和promise实例数组顺序是一致的
}, err => {
    console.log(err);
});

race()方法:
race 按字面解释,就是赛跑的意思。race 的用法与 all 一样,只不过 all 是等所有异步操作都执行完毕后才执行 then 回调。而 race 的话只要有一个异步操作执行完毕,就立刻执行 then 回调。

注意:其它没有执行完毕的异步操作仍然会继续执行,而不是停止。

Promise.race 只要有一个promise对象进入 FulFilled 或者 Rejected 状态的话,就会继续进行后面的处理。

function timerPromisefy(delay) {
    return new Promise(function (resolve, reject) {
        setTimeout(function () {
            resolve(delay);
        }, delay);
    });
}
var startDate = Date.now();

Promise.race([
    timerPromisefy(10),
    timerPromisefy(20),
    timerPromisefy(30)
]).then(function (values) {
    console.log(values); // 10
});

race 使用场景很多。比如我们可以用 race 给某个异步请求设置超时时间,并且在超时后执行相应的操作。

function requestImg(){
            var p = new Promise(function(resolve, reject){
            var img = new Image();
            img.onload = function(){
               resolve(img);
            }
            img.src = 'xxxxxx';
            });
            return p;
        }

        //延时函数,用于给请求计时
        function timeout(){
            var p = new Promise(function(resolve, reject){
                setTimeout(function(){
                    reject('图片请求超时');
                }, 5000);
            });
            return p;
        }

        Promise.race([requestImg(), timeout()]).then(function(results){
            console.log(results);
        }).catch(function(reason){
            console.log(reason);
        });
        //上面代码 requestImg 函数异步请求一张图片,timeout 函数是一个延时 5 秒的异步操作。我们将它们一起放在 race 中赛跑。
        //如果 5 秒内图片请求成功那么便进入 then 方法,执行正常的流程。
        //如果 5 秒钟图片还未成功返回,那么则进入 catch,报“图片请求超时”的信息。
1
广告 广告

评论区