[总结]ES6到ES12各类方法和新特性(建议收藏)
~ 此文纯粹手动敲打,如要转载请征求我的同意。禁止私自转载,谢谢🙏
ES6(ES2015)
Es6是简称,全称是ECMAScript 6.0。由于es6是2015年6月份发布的标准。又可以称之为ECMAScript 2015,或es2015。它的目标,是使得JavaScript语言可以用来编写复杂的大型应用程序,成为企业级开发语言。
关于let和const
在现代JavaScript(ES6以及之后),变量声明都是采用的let和const;
1. 首先来看let
例如:
let i;
let m;
也可以是:
let i,m;
let i=0,j=0,k=0;
let x=2,y=x*x;//初始化的语句可以使用前面的声明变量;
以上用法和var好像没区别,
咱们来看一下实际的案例:
var my_name="Mike";
function fn1(){
console.log(my_name);
//此时my_name的结果是什么?
let my_name="JavaScript";
};
fn1();
这时控制台会报错 :
Uncaught ReferenceError: can't access lexical declaration 'my_name' before initialization;
为什么my_name不是function外部的"Mike"呢?
因为let声明会在当前的{ }内部形成一个块级作用域,let并不能完全的说不存在变量提升,let存在一个隐式变量提升,在let声明之后形成的当前块级作用域会自动检测当前块级作用域环境,如果检测有let声明的变量,会进行隐式提升,这种提升会被浏览器的V8引擎给限制掉,所以就出现了报错,这就是暂时性死区;
2. 再来看一下const
const和let同样存在暂时性死区,和let不同的是,const专门用于声明一个常量;
来看看以下代码:
const obj={
name:"Mike",
age:19,
hobby:"FootBall"
}
obj.name="John";
console.log(obj);//此时会报错吗?
不会报错; const表示声明的是一个常量,顾名思义,常量的值是不能改变的,当用const声明一个原始值类型(基本数据类型)的时候,该值如果发生变化,控制台会报错; 但如果const声明的是一个(对象类型)引用数据类型,只要这个对象的空间地址不发生改变,里面的键值对发生变化,是可以使用const声明的;
总结:
let和const存在隐式变量提升,都可能会形成暂时性死区;- 在相同作用域下
let和const不允许重复声明,在嵌套作用域或不同作用域中是允许同名变量声明的; let和const声明所在的{ }都会形成一个块级作用域;let主要用于声明变量,const主要用于声明常量;
3. 来看看解构赋值
先来看个对象的解构例子:
let obj1={
name:"Mike",
age:19,
hobby:"FootBall"
};
let {
age,
hobby
}=obj1; console.log(age,hobby);//19,"FootBall"
此时{age,hobby}分别是两个变量名,并不是对象属性名,这点尤其注意;
这时候age和hobby会去匹配obj1对象中的属性名,找到与age和hobby相同变量名的属性名,则会进行匹配;
再来看个数组解构的例子:
let arr1=[0,"1",NaN,undefined,"hello"];
let [a,b,c]=arr1;
console.log(a,b,c);//0,"1",NaN
这时候[a,b,c]里的a b c分别对应的是arr1中的前三个数值,不是索引
4. 关于扩展运算符...
我看网上有将其分为展开运算符和剩余运算符的,我的理解就是一种,先来看个例子:
let arr1=[1,2,3,4,5,6];
let arr2 = [...arr1];// [1,2,3,4,5,6]
我们可以把...arr1看作是1,2,3,4,5,6,这不是字符串,不可以单独打印出来,外层必须要套一层[]或{},要么就作为形参,放在形参的最右侧使用;
所以...arr中,arr必须是一个数组或对象; 并且...arr只能放在最右边,无论是作为形参还是数组,都只能将其放在最右边;
我们再来看一个例子:
let obj1={
aa:1,
bb:2,
cc:3,
dd:4,
ee:5
};
let obj2={
name:"Mike",
age:19,
...obj1
}
此时的...obj1就相当于是把外层的衣服剥开,只露出里层的内容,所以obj2={name:"Mike", age:19,aa:1, bb:2, cc:3, dd:4, ee:5};
总结:
-
扩展运算符
...只能放在数组或对象的最右(后)侧; -
...的右边紧跟的是数组或对象,例如...[1,2,3]或...{aa:11,bb:22};
5. 模板字符串${}
模板字符串解决了传统的""或''不允许换行的问题,我们再使用${}的时候能随意换行,并且${}让解构更清晰,来看个例子:
let str`你好${变量1},再见${变量2},aaaaa`;
模板字符串中允许使用表达式,这是非常好用的地方;
6. 数组的迭代方法for of
说是数组的迭代方法,其实严谨来说for of是用来迭代iterable对象的,而数组是典型的iterable对象;
-
for of与for in之间的区别
for of可迭代iterable对象中的所有私有属性或方法,不可迭代出该对象的原型上的属性或方法;
for in可迭代iterable对象或非iterable对象,迭代的是对象中的私有属性或方法,以及后期添加在该对象原型中的属性或方法;
所以一般情况下,我们要的仅仅只是对象中的私有属性或方法, 如果这个对象是一个iterable对象,for of是最好不过的了; 当然,for of只能迭代属性值,并不能迭代属性名, ES6给我们提供了一个方法可以仅迭代对象的私有属性名,叫Object.keys();
7. 对象的迭代属性方法Object.keys()
先来看个简单的例子:
function Person(name,age)(
this.name=name;
this.age=age;
);
Person.prototype.hobby='FootBall';
let p = new Person("Mike",19);
let aa = Object.keys(p);
console.log(aa);
此时打印的结果是["name", "age"],Object.keys()会把迭代到的属性名组成一个数组给返回出来;
我们可以在了解一下Object.keys()与Object.getOwnPropertyNames的区别.
Object.getOwnPropertyNames与Object.keys()十分类似, 唯一的区别是: Object.keys()在迭代对象的属性名的时候,只会迭代出可枚举的属性名, 而Object.getOwnPropertyNames不仅仅会把所有的私有可枚举属性名迭代出来,还会迭代出私有的不可枚举属性名,例如数组的length属性.
8. Array.prototype.keys( )
和对象方法Object.keys()不一样,这里要尤其注意!
对象中的keys方法是直接作为Object的静态属性,而数组中的keys是添加到数组的原型上,意思就是说供普通数组来调用使用,来看代码:
let arr =['一','二','三'];
let bbb = arr.keys();
console.log(bbb);
for(i of bbb){
console.log(i);//👉 0 1 2
}
这时候的控制台打印出来的bbb显示的是Array Iterator,并且展开之后什么也没有,有一个next(),所以我猜测这个api是基于generator生成器实现的方法(如有错误请纠正);
这时候要想获取到bbb中的key值,还需要对其进行迭代,这时候利用for of迭代出来值也就是我们期望的key值了;
注意: Array.prototype.keys( )括号中没有参数.
9. Array.prototype.values( )
和Array.prototype.values( )类似,不过一个是keys,一个是values,两者是搭配的,使用方法一样,同样的括号中没有参数存在;
10. 来看看箭头函数=>
我们来看看以前写函数的方式:
function fn(){
console.log('你好');
};
箭头函数的方式:
let fn=(name,age)=>{
console.log(`你好`,name,age);
};
//如果`{}`中只有一行代码,可以更加省略,甚至只有一个参数的时候 `()`也可以省略掉
let fn=name=>console.log(`你好`,name);
//如果函数没有参数的话,`()`是不能省略的:
let fn=()=>console.log(`你好`);
关于箭头函数与普通函数的区别:
-
箭头函数没有自己的
this,函数中的this相当于是上级作用域中的this,上级作用域中的this指向谁,那么这个this就指向谁; -
因为没有自己的
this,所以箭头函数也不能new; -
箭头函数没有
arguments;- 箭头函数要想获取实参信息,可以通过在最后一个形参中传
...arg的方式获取实参
- 箭头函数要想获取实参信息,可以通过在最后一个形参中传
-
箭头函数没有原型对象;
11. Symbol原始类型
Symbol是ES6中新增的一种原始类型(基本数据类型),Symbol类型没有字面量语法; 要获取一个Symbol值,需要调用Symbol()方法,这个函数永远不会返回相同的值,即使每次传入的参数都一模一样,例如:
let aa=Symbol(1);
let bb=Symbol(1);
console.log(aa==bb);//false
这意味着可以将调用Symbol()取得的符号安全地用于为对象添加新属性,无需担心可能重写已有的同名属性;
但是,Symbol作为对象的属性名时,是迭代不到的,例如:
let aa = Symbol(1);
let obj = {
name: "mike",
age: 19,
aa:123
};
for(let i in obj){
console.log(i);//name,age,aa
};
console.log(Object.keys(obj));//["name", "age", "aa"]
但是Symbol作为属性值的时候,却是可以被迭代的:
let arr=[aa,123,321];
for(let i of arr){
console.log(i);//Symbol(1), 123, 321
}
12. 默认参数
例如:
function fn1(name="Mike",age=19){};
function fn2(name,age){
name='Mike'||name;
age=19||age;
}
fn1与fn2是等价的,只不过fn1的方式是ES6新提供的一种简写方式;
13. Map对象和Set对象
Es6内置的Set(集合)类和Map(映射)类;
1) 我们先来看看Map
let map=new Map();
console.log(map);
map.set("jian1","值1");
map.set("jian2","值2");
map.set("jian3","值3");
map.set("jian4","值4");
map.set("jian5","值5");
map.set("1","字符串1");
map.set(1,"数字1");
console.log(map);
console.log(map.get("jian3"));//获取到键叫"jian3"对应的值
console.log(map.get(1));//获取到键叫1对应的值
console.log(map.get("1"));//获取到键叫"1"对应的值
console.log(map.size);//获取map对象的长度;
从中我们可以看出,Map类创造出的实例对象中的属性名是能够区分基本数据类型的,例如把属性名设置为1和"1"是不一样的,而这个在普通对象中却是不能实现的,因为普通对象中的键名只能是字符串或Symbol类型,当给普通对象的键名设置为number类型的时候,会默认的被调用toString()方法,将其转化成字符串的number;
那么,Map的键名可以传递原始值类型,那么引用数据类型可以吗?
我们来看看:
let obj={ name:'Mike' };
let map2=new Map();
map2.set(obj,'值1');
console.log(map2);
/*
map2的打印结果:
0: {Object => "值1"}
1: {1 => 1}
2: {2 => 2}
3: {3 => 3}
*/
console.log(map2.get(obj)); //值1
由此我们可以看出,Map类的实例对象的键名是可以是各种类型的;包括本文中未提及的null undefined bigint object Array 等等;
Map还有一个特点: 可以链式给对象设置属性,比如:
//Map,set每次调用的时候都会返回自身,所以可以进行链式调用
map2.set(1,1).set(2,2).set(3,3);
除此之外,Map 有内置的 forEach 方法,与 Array 类似;
2) 我们再来看看Set
Set 是一个特殊的类型集合 —— “值的集合”(没有键),它的每一个值只能出现一次。
它的主要方法如下:
new Set(iterable)—— 创建一个set,如果提供了一个iterable对象(通常是数组),将会从数组里面复制值到set中。set.add(value)—— 添加一个值,返回 set 本身set.delete(value)—— 删除值,如果value在这个方法调用的时候存在则返回true,否则返回false。set.has(value)—— 如果value在 set 中,返回true,否则返回false。set.clear()—— 清空 set。set.size—— 返回元素个数。
面试中我们经常被问到数组去重的方法,这时候
Set便是一个最简洁的方法,比如:
let arr = [1,2,0,9,0,9,0,[1,2,0,0,0],0,6,4,5,67,43,4,'你好','hello',"你好",0,0];
let set = new Set(arr);//括号里放的是一个iterable对象(通常是数组);
console.log(set); //[1,2,0,9...];
这时候就实现了数组去重;
14. class类
class声明会创建一个新类并为其赋予一个名字,类意味着一组对象从同一个原型对象继承属性;
新增的class关键字并未改变JavaScript原型的本质;
class abc{
consturctor(name,age){
this.name=name;
thia.age=age;
}
hobby='FootBall';
my_public(){
console.log("这是my_public");
}
static my_static(){
console.log('这是我的static');
}
}
从示例可以看出:
class关键字可以声明一个类,后面紧跟着类名和花括号中的类体;- 关键字
constructor用于定义类的构造函数,但实际上定义的函数并不叫'constructor',class语句会定义一个新变量Range,并将这个特殊的构造函数的值赋给该变量; - 如果类不需要任何初始化,可以省略
consturctor关键字及其方法体,解释器将隐式的为其创建一个空的构造函数;
1) 静态方法
在class体中,把static放在方法声明前用于声明一个静态方法,该静态方法是把类当作是一个对象,以键值对的方式存储在该对象当中,只要该类(该对象)能访问,类的实例对象无法访问到.
2) 获取方法、设置方法以及其他形式的方法
在class类中的,可以像在对象字面量中一样定义获取属性方法和设置方法;唯一的区别是类的获取方法和设置方法后面不加括号;
一般来说,对象字面量支持所有简写的方法定义语法都可以在类体中使用,这包括生成器方法(带*)和名字为方括号表达式的方法;
15. try catch
当编译运行程序出错的时候,编译器就会抛出异常。同时程序下面的代码不能正常执行,这对开发很不友好,但是有一种更合理的es6语法 try..catch,它会在捕捉到异常,同时不会打断代码执行,把出现的异常通过catch捕捉到,避免出现红色报错;
例如:
try{
console.log(num);
let num=`学前端的格斯`;
}catch(err){
console.log(err);
}
这时候try当中就出现了暂时性死区,按理说会出现红色报错,但由于使用了try...catch,这时候的报错会被catch捕获到,由catch接收,所以会在catch内打印出具体的错误信息;
16. Object.is(value1,value2)
-
value1: 被比较的第一个值。 -
value2: 被比较的第二个值。
ES5 比较两个值是否相等,只有两个运算符:相等运算符(==)和严格相等运算符(===)。它们都有缺点,前者会自动转换数据类型,后者的NaN不等于自身,以及+0等于-0。JavaScript 缺乏一种运算,在所有环境中,只要两个值是一样的,它们就应该相等。
ES6 提出“Same-value equality”(同值相等)算法,用来解决这个问题。Object.is就是部署这个算法的新方法。它用来比较两个值是否严格相等,与严格比较运算符(===)的行为基本一致。
Object.is('one','one');//true;
Object.is({},{});//false;
不同之处只有两个:一是+0不等于-0,二是NaN等于自身。
-0===+0; 👉true;
NaN===NaN; 👉false;
来Object.is()的表现:
Object.is(NaN,NAN); 👉true;
Object.is(+0,-0); 👉false;
所以,Object.is更符合我们正常人的思维模式,它解决了===和==总是会存在一些历史遗留下来的常人难以理解的奇怪问题;
17. Object.assign(target,obj1,obj2,obj3,...)
基本用法
Object.assign()方法用于对象的合并,将源对象obj1``obj2``obj3的所有可枚举属性,复制到目标对象target上。
代码示例:
let target={
name:`写前端的格斯`,
};
let obj1={
aged:18
};
let obj2={
sex:`man`
};
let new_obj = Object.assign(target,obj1,obj2);
这时候我们打印出来的new_obj的结果是:
new_obj={
name:`写前端的格斯`,
aged:18,
sex:`man`
}
Object.assign()方法的第一个参数是目标对象,后面的参数都是源对象。
Object.assign()的返回值就是target;
注意:
- 如果目标对象与源对象有同名属性,或多个源对象有同名属性,则后面的属性会覆盖前面的属性;
Object.assign()方法实行的是浅拷贝,而不是深拷贝。- 如果源对象的属性名与
target对象中属性名重复,那么源对象中的属性将会覆盖第掉target对象中对应的属性;
18. Object.setPrototypeOf()和Object.getPrototypeOf()
我们先来看看MDN上关于__proto__的说明:
已废弃: 该特性已经从 Web 标准中删除,虽然一些浏览器目前仍然支持它,但也许会在未来的某个时间停止支持,请尽量不要使用该特性。
以上是MDN关于__proto__说明解释,已被废弃;
转而替代的设置和获取原型的API是Object.setPrototypeOf()和Object.getPrototypeOf();
先来看看Object.setPrototypeOf();
Object.setPrototypeOf方法的作用与__proto__相同,用来设置一个对象的原型对象(prototype),返回参数对象本身。它是 ES6 正式推荐的设置原型对象的方法。
Object.setPrototypeOf(obj,my_proto);
👉将obj对象的__proto__设置成my_proto;
再来看看Object.getPrototypeOf();
该方法与Object.setPrototypeOf方法配套,用于读取一个对象的原型对象。
let my_proto=Object.getPrototypeOf(obj);
👉获取到obj的原型,返回值是obj的原型;
19. String.raw
这是一个字符串的方法,该方法返回一个斜杠都被转义(即斜杠前面再加一个斜杠)的字符串,往往用于模板字符串的处理方法。
请看示例代码:
let str1 = String.raw`Hi\n\t,John!`
👉console.log(str1);
打印结果是: "Hi\n\t,John!"
要注意的是: String.raw后面直接跟的``,不是()
20. 函数的name属性
这个属性早就被浏览器广泛支持,但是直到 ES6,才将其写入了标准;
需要注意的是,ES6 对这个属性的行为做出了一些修改。如果将一个匿名函数赋值给一个变量,ES5 的name属性,会返回空字符串,而 ES6 的name属性会返回实际的函数名。
21. Array.from( )
主要用于将伪数组转换成一个真数组;
来看示例代码:
let lis=document.querySelectorAll("li");
let arr = Array.from(lis);
获取到所有<li>标签的集合;这就是一个伪数组,表面上和数组无疑,但是它的__proto__指向的并不是Array;
这时候通过Array.from()方法,将lis的__proto__更改为Array实现真数组的转变;
ES7(ES2016)
1. Array.prototype.includes( )
此方法数组原型上的方法,返回值是Boolean类型;
用于判断某个真数组中是否包含某个值,例如:
let arr = [11,22,33,44,66];
arr.includes(55);//返回结果是false;
arr.includes(66);//返回结果是true;
2. String.prototype.includes( )
字符串的原型上同样也有includes方法,使用上和数组一致,返回值依然是Boolean类型;
3. Object.values( )
此方法是用于迭代一个对象中的私有属性值,刚好和Object.keys相呼应,来看代码:
let obj={
name:'Mike',
aged:19,
sexual:'man'
};
Object.values(obj);👉'Mike' 19 'man'
4. Object.entries( )
此方法主要是讲一个普通对象转变成一个二维数组,请看代码:
let obj={
name:'Mike',
aged:19,
sexual:'man'
};
Object.entries(obj);
返回值是👇
[ ['name', 'Mike'],
['aged', 19],
['sexual', 'man']
]
该方法可以搭配Map内置类一起使用,因为Map括号中要求传递的就是一个二维数组对象;
5. Object.fromEntries( )
首先我先看一个示例:
const entries = new Map([
['foo', 'bar'],
['baz', 42]
]);
const obj = Object.fromEntries(entries);
console.log(obj);
// expected output: Object { foo: "bar", baz: 42 }
首先Object.fromEntries()接收一个iterable(通常是二维数组),里面的每一个数组有两个值,分别对应的是一个键值对;最后Object.fromEntries()返回的是一个对象,通常是搭配Map内置类一起使用。
未完,待续...
工作忙,此文长期维护更新...