声明变量的扩展
在Es5中声明变量只有两种:var和function,在Es6中新增了两种常见的声明变量方式:let和const。
下面我们就来介绍一下Es6 let和const命令。
let
1、let声明变量与var类似,但是var不同的是let声明变量会形成块级作用域(所谓的块级作用域是可以简单理解为{块级作用域}就是两个花括号之间的叫做块级作用域。)变量只能在块级作用域中起效果在块级外不能访问。
if(true)
{
let a=1;
console.log(a);//1
}
console.log(a)//not define复制代码
我们知道在js中函数是划分全局变量和局部变量的标准,在函数内定义的变量为局部变量在函数外面定义变零为局部变量,而对于上面的代码没有函数按理说a是全局变量那为什么不能访问了,这就是let新特性了let形成块级作用域,只能在块级作用域有效。
对于let这个特性有一个地方会非常适合let那就是我们常见的for循环的计数器了。
在Es5中我们这样写:
var arr=[];
for(var i=0;i<5;i++)
{
arr[i]=function()
{
console.log(i);
}
}
arr[2]();//5复制代码
为什么是5那?主要的原因是我们是var定义的i是全局变量每次执行for循环时后面的i值会将前面的i值覆盖掉,最后整个执行完i就等于最后的值了,但为什么不是4了,哈哈哈别忘了后面还有执行一次i++。
在Es6中我们这样写:
var arr=[];
for(let i=0;i<5;i++)
{
arr[i]=function()
{
console.log(i);
}
}
arr[2]();//2
arr[3]();//3复制代码
我们这里使用let定义变量i,只能在块级作用域中使用没执行一次for循环,就形成一个块级作用所以就形成不同i所以最后输出的就是各自的i值。
2、let不允许在同一个作用域内进行重复声明,而var变量是可以的一最后面的为准。
function f() //报错
{
let i=1;
let i=1;
}
f();
function z()//报错
{
let a=1;
var a=1;
}
z();
function n()//报错
{
var b=1;
let b=1;
}
n();
function x()//不报错
{
var c=1;
var c=2
}
x();复制代码
3、let不允许在函数类重复的声明形参。
function ff(a)//报错
{
let a;
}
ff();
function zz(b)//不报错
{
var b;
}
zz();
function nn(c)//不报错
{
{
let c;
}
}
nn();复制代码
4、let在进行预编译时不会提升,var会提升,使用let时访问变量时一定要放到声明变量的后面。
console.log(b);//undefined
console.log(a);//not defined
let a=1;
var b=2;
console.log(a);//1
console.log(b);//2复制代码
5.使用let会形成封闭的作用域可以防止变量的污染。
var a=1;
if(true)
{
let a=2;
console.log(a);//2
}
console.log(a);//1
复制代码
6、暂时性死区问题。
Es6明确规定,若块级作用存在着let和const命令,则他们声明的变量一开始就形成了封闭的作用域,凡是在声明之前使用这些变量就会报错。
在代码块类内使用let之前是不能是使用变量的这叫作“暂时性死区”。
var a=3;
if(a<5)
{
a=4;
let a; //报错 not defined
}复制代码
暂时性死区使得typeof 变的不在百分之百安全,在没有let之前typeof是绝对安全的即使是变量没有定义也不会报错返回的只是undefined。但是如果我们在let之前使用typeof的话就会报错
console.log(typeof a); //不报错 undefined
if(true){ console.log(a);//报错 not defined let a;}复制代码
暂时性死区的本质:只要一进入当前的作用域,所有使用的当前变变量就存在了,但是不可获取,必须要等到声明对象的那句话执行之后才能对变量获取使用权。
Es6规定暂时性死区和let、const语句不出现变量提升,主要是为了减少运行时错误,防止变量在声明前就使用该变量,从而导致意料之外的错误。
const
下面我们来介绍下const命令:
const就是声明一个常量,比较书面的语言的话就是声明一个只读量,一旦声明一个常量那么它的的值就不能改变了,这就意味着我们在定义一个常量的同时就要初始化不能等到之后在进行初始化。也就是说如果我们只初始化常量或者我们给常量赋值的话就会报错。
1、const和let类似在声明变量时会形成块级作用域只在声明的块内有效。
if(true)
{
const PI=3.14;
}
console.log(PI);//报错 not defined复制代码
2、const也在预编译时不能进行变量的提升。
console.log(PI);//报错 not defined
const PI=3.14;
console.log(PI);//3.14复制代码
3、const也不能重复声明同时也存在暂时性死区,只能在声明之后访问。
let PI=3.141
var A=66;
const PI=3.14;//报错
const A=88;//报错
//暂时性死区
const PI=3.14;if(true){ console.log(PI); //报错not defined const PI=5; }
复制代码
4、const对于声明的引用数据类型的易错点。
在我们知道基本数据类型是在内存中进行存放时是在栈区的而引用数据类型的存储存储到堆区的,它的的内存地址存放在栈区中。怎么理解这句话了,我们先看下面的例子:
var obj=
{
"man":"fu",
"wife":"zhang",
}
复制代码
我们就拿对象来说下,上面的来自在 内存中存储是这样的会在栈区开辟一开空间存放变量名obj,再在堆区中开辟一块空间来存放对象体,同时将堆区的内存地址放到栈中开辟的obj中。也就是说变量obj里面存放的不是对象而是存放的地址。明白了这一点我们就来看看使用const声明引用类型的易出现的错误。
const obj=
{
"man":"fu",
"wife":"zhang"
}
obj.abc="abc";复制代码
我们先来考虑下上面的代码会报错?当然不会了,为什么了?是因为我们在定义时给常量obj赋值的是对象的内存地址而不是对象本身,虽然对象里面的内容改变了但是对象的内存地址没有发生改变所以不会报错。如果我们把obj指向另一个对象肯定会报错,因为内存地址发生了变化。如下:
const obj=
{
"man":"fu",
"wife":"zhang"
}
abj={}//报错复制代码
类似的还有数组等引用数据类型,数组里面可以增删改数据不会报错,但是如果你是指向另一个数组就好报错。
函数的扩展
箭头函数
箭头函数时Es6新增的函数写法,我们通过使用箭头函数可以让我们代码的更加的简洁,代码量更少。看如下的例子:
//在Es5中我们声明一个函数:
function f()
{
console.log("......f........");
}
//Es6我们使用箭头函数我们可以将上面的代码简化为:
let f=()=>console.log("....f.....");复制代码
由上面看代码是不是简化很多代码量是不是少了很多。
下面我们就来具体的学习下箭头函数:
1、在Es5中我们定义一个函数。
function f(a+b)
{
return a+b;
}
//定义一个匿名函数
var f=function()
{
return a+b;
}复制代码
2、Es6箭头函数
形式:就是将Es5的匿名函数去掉function在加上=>。
var f=(a,b)=>{return a+b;}复制代码
3、Es6箭头函数参数的简写。
//当形参只有一个是我们可以将()省略掉如下.
var f=a=>{return a}
//但需要注意的如果没参数我们的()是不能省略掉的.
var f=()=>{console.log("..f...")}复制代码
4、Es6箭头函数返回值的简写。
当函数体中只有一个语句且没有return语句是我们可以将{}省略掉
var f=()=>console.log(".....f.....");
当函数体中只有一个return语句是我们也可以将return和{}一同省略掉
var f=()=>"省略了";
复制代码
5、Es6箭头函数返回值是一个对象(易错点)。
我们先考虑下如过函数要返回一个对象,如下的写法对?
var f=()=>{"name":"fu"}
console.log(f());复制代码
当然是不对的感觉就不对,如果这样写程序在预编译时就会报错。那时为什么?因为{}被占用解释为代码段了所以就错了,那我们应该怎么写了?我们应该将对象使用()包起来,如下就可以正确正确编译执行了。
var f=()=>({"name":"fu"}) console.log(f());//{ name: 'fu' }复制代码
6、Es6箭头函数中的this指向问题。
这里我们只需要记住箭头函数中是没有this的箭头函数中this是指向箭头函数的上一级的。
var obj= { f() { console.log(this); }, f1:()=> { console.log(this); } } obj.f();//指向obj对象 obj.f1();//在浏览中之间window对象复制代码
字符串的扩展
模板字符串
在Es6中新增了一种字符串的输出方法叫做模板字符串,它可以保留我们原本要输出的字符串样式,是一种非常赞的方法。
语法:将我们要输出的字符串用反引号( ` )包起来,将变量使用${变量}即可。注意反引好是1的左边按钮。
let name="我叫史森明";
console.log(`name: ${name}`);//name: 我叫史森明
const sql = `
SELECT * FROM Users
WHERE FirstName='Mike'
LIMIT ;
`
//输出结果:
SELECT * FROM Users
WHERE FirstName='Mike'
LIMIT ;
完全保留了我们的定义的输出格式是不是很爽。复制代码
注意:多行模板字符串会在每一行的最后添加一个“\n”字面量,所以在读取多行字符串的长度时,除最后一行以外,每一行的长度都会加1,即增加了”\n”。
新增的字符串操作方法
1、trim()
trim()去除空格,一般配合正则表达式使用,默认去除左右两边的空格。
衍生方法:trimLeft():去除左边空格。trimRight():去除右边空格。
2、repeat()
repeat(参数)重复字符串 参数:表示要重复的次数。
let str="abc";
console.log(str.repeat(5));//abcabcabcabcabc复制代码
其实我们之前学过的数组也能完成上面repeat的功能:代码如下
//我们重复4遍
let str="123";
console.log(new Array(5).join(str));//123123123123复制代码
原理我就不说这个比较简单。
3、includes()
includes()判断一个字符串中是否包含一个指定的字符 参数:指定的字符。
let str="abc def";
console.log(str.includes("e"));//true复制代码
4、startsWith ()和 endsWith()
startsWith()是判断一个字符串是否以某个指定的字符串开始。
endsWith()是判断一个字符串是否以某个指定字符串结束的。
let str="abc def";
console.log(str.startsWith("abc"));//true
console.log(str.startsWith("abcd"));//false
console.log(str.endsWith("def"));//true
conaole.log(str.endsWith("efg"));//false复制代码
5、padStart()和padEnd()
padStart(参数1,参数2):参数1表示总共多少位,参数2表示指定的符号。此方法就是就是就是用指定的符号从头开始填充。
padEnd(参数1,参数2):参数1表示总共多少位,参数2表示指定的符号。此方法就是就是就是用指定的符号从尾部开始填充。
let str="abc";
console.log(str.padStart(10,"*"));//*******abc
console.log(str.padEnd(10,"*"));//abc*******
//最常用的用法
let money=5;
console.log(money.toFixed(2).padStart(6,"0"));//005.00复制代码
数组扩展
新增数组操作方法
1、Array.from()
from()是Array的静态方法,其主要的作用是将类数组转化为真正的数组返回值是转化完的真数组。
function f(a,b)
{
console.log(Array.isArray(arguments))//false
let arr=Array.from(arguments);
console.log(Array.isArray(arr));//true
}
f(1,2);复制代码
我们知道arguments是一个类数组不是真数组,Array.isArray()方法是判断是否为真的数组。
2、Array.of()
of()是将一个值转为数组,主要是用来创建数组解决以前创建数组的痛点。
以前创建数组的痛点:
//比如我们要创建一个只包含数值3的数组
var arr=new Array(3);
console.log(arr);//[ <3 empty items> ]
它却给我创建了包含了三个空元素的数组,这不是我们想要看到。复制代码
这是我们使用新增的of()方法就可以很好地解决这个问题。
var arr=Array.of(3);
console.log(arr);//[ 3 ]复制代码
3、find()和findIndex()
find(参数)用于找出第一个符合条件的元素,如果没有找到则返回undefined
findIndex(参数)用于返回第一个符合条件元素的索引,如果没找到则返回-1;
参数:是一个回调函数function(item,index,arr){} 形参:item :元素 index:索引 arr:原数组
回调函数可以写成箭头函数的形式必须要有返回值没返回值返回回来的undefined。
注意:只会找到第一个符合条件的。
let arr=[
{"name":"fu"},
{"name":"zhang"}
]
let rs=arr.find(item=>item.name=="fu");
console.log(rs);//{ name: 'fu' }
let rss=arr.findIndex(item=>item.name=="zhang");
console.log(rss);//1复制代码
4、includes()
includes()判断数组中是否存在着指定的元素,返回值是true和false。
let arr=[1,2,3];
console.log(arr.includes(1));//true复制代码
注意:indexOf()访问方法与includes()类似只不过返回的是索引,两者还是有区别indexOf()方法对NaN的判断是有误的,而includes()则会判断的比较准确。
let arr=[NaN,1,2,3];
console.log(arr.indexOf(NaN)); //-1
consoel.log(arr.includes(arr));// true复制代码
5.fill()
fill(参数1,参数2,参数3)给数组填充指定的值.参数1:指定元素,参数2:开始位置(可选),参数3:结束位置(可选)。
let arr=new Array(5);
arr.fill("*")
console.log(arr); //[ '*', '*', '*', '*', '*' ]复制代码
当数组里面有内容时会覆盖前面的内容。
let arr=[1,2,3];
arr.fill("*");
console.log(arr);//[ '*', '*', '*' ]复制代码
指定位置添加:
let arr=[1,2,3];arr.fill("*",0,1);console.log(arr);//[ '*', 2, 3 ]复制代码
6、数组的扩展运算符
数组的扩展运算符是“...”,功能:是将数据转化为数组。
let arr=[1,2,3];
let res=[...arr];
console.log(res);//[ 1, 2, 3 ]复制代码
扩展运算符实际上是数组的深拷贝。
let arr0=[1,2,3];
let arr1=[...arr0]
arr1[0]=666;
consoe.log(arr0);//[ 1, 2, 3 ]
console.log(arr1);//[ 666, 2, 3 ]复制代码
扩展运算符可以将类数组转化为真数组。
function f(a,b)
{
console.log(Array.isArray([...arguments]));//true
}
f(1,2);复制代码
扩展运算符必须用容器包包起来不然报错。
let arr=[1,2,3];
...arr //预编译报错
console.log(...arr); //1 2 3 复制代码
扩展运算符可以实现将字符串转化字符数组,还可以用于合并两个数组。
let str="abcdefg";
let rs=[...str];
console.log(rs);//[ 'a', 'b', 'c', 'd', 'e', 'f', 'g' ]
let str2=[1,2,3,4,5,6];
console.log([...rs,...str2]);//[ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 1, 2, 3, 4, 5, 6 ]复制代码
数组的解构赋值
Es6允许按照一定的模式,从数组和对象提取值,对变量进行赋值叫做解构。是给变量赋值。
在没有解构赋值之前:
//我们要去数组的值
let arr=[1,2,3,4];
let a=arr[0];
let b=arr[1];
let c=arr[2];
let d=arr[3];复制代码
使用解构赋值:
let arr=[1,2,3,4];
let [a,b,c,d]=arr;
console.log(a);//1
console.log(b);//2复制代码
上面代码使用解构赋值的话就会省去我们自己在定义变量的步骤。
完全解构:当赋值号左右两边的数组结构完全一样时叫做完全解构,按照对应的位置为变量进行赋值。
let [a,b]=[1,2];
console.log(a,b);//1 2复制代码
部分解构:当复制好左右两边的数组结构不完全一样时叫做不完全解构。空的变量会被赋为undefined。
let [a,b,c,d]=[1,2,3];
console.log(a);//1
console.log(b);//2
console.log(c);//3
console.log(d);//undefined复制代码
解构数组可以设置默认值。
let [a,b,c,d,e=3]=["a","b","c","d"];
console.log(a);
console.log(b);
console.log(c);
console.log(d);
console.log(e);
//输出结果:
a
b
c
d
3复制代码
跳过某元素和嵌套解构
let[a,,b]=[1,2,3];
console.log(a,b); //1 3
let [m,n,q]=[4,[7,8],6];
console.log(m,n,q); //4 [ 7, 8 ] 6复制代码
数据结构的扩展
在Es5中只有两种数据结构:数组和集合。
数组:数组中的元素是有序的。
集合:集合中的元素时无序的。
在Es6中又新添加了两种新的数据结构set和map。
set
1、数组和set的区别。
数组时有序的且元素可以重复,而set集合中元素不是时无序的是唯一的,不能重复。
set不是一个真的数组而是一个类数组。
2、创建set集合
//创建一个数组是通过数组创建的语法糖来进行创建的
let arr=[1,2,3,4]
//创建一个set集合
let set=new Set();
复制代码
在创建set集合我们注意的是创建set时只能通过new的方式,并没相应语法糖。
3、给set集合添加元素
//第一种方式
let set=new Set("1",1,true) //注意这种方式是不能添加的是错的。
//第二种:添加一个对象
let set=new Set({"name":"fu"});//这种也是错的。
//第三种:添加一个数组
let set=new Set([1,2,3]);
console.log(set);//Set { 1, 2, 3 } 向里面创一个数组这种是可以的。复制代码
单个添加元素的话使用add()方法来进行添加。
let set=new Set();
set.add("adascd");
set.add(true);
set.add([1,2,3]);
set.add({"name":"fu"});
console.log(set);//Set { 'adascd', true, [ 1, 2, 3 ], { name: 'fu' } }
复制代码
4、遍历set
注意的是set不能使用for in进行遍历因为for in是通过索引来遍历的而set没有连续的索引所以不能比遍历。
使用forEach遍历
let set=new Set([1,2,3,4]);
set.forEach(item=>console.log(item));
1
2
3
4复制代码
使用for of遍历
let set=new Set[1,2,3,4];
for(let p of set)
{
console.log(p);
}
1
2
3
4复制代码
需要注意的for in遍历是通过索引来遍历,而for of是通过元素来进行遍历。
5、set集合的常用用法解决数组的重复问题
let arr=[1,2,3,1,3,4,NaN,false,true,Nan];
let rs=[...(new Set(arr))];
console.log(rs);//[ 1, 2, 3, 4, NaN, false, true ]复制代码
map
区别在于:对象中的键名只能是字符串,如果使用map,它里面的键可以是任意值。
1、创建map
let map =new Map();复制代码
与set相同map创建时也是没有语法糖只能通过,只能通过new构造器的方式进行创建。
2、向map添加元素:通过set()方法进行添加。
let map=new Map();
map.set(false,"abc");
map.set([1,2,3],{"name":"fu"});
console.log(map);//Map { false => 'abc', [ 1, 2, 3 ] => { name: 'fu' } }复制代码
3、取出map中的元素:通过get()方法进行获取。
let map=new Map();map.set(false,"abc");abcmap.set([1,2,3],{"name":"fu"});console.log(map.get(false));//abc复制代码
4、map中键值是不能重复的,重复的键值后面的value值会将前面的value值覆盖,因为map里面的键值是有set实现的。
let map=new Map();map.set(false,"abc");map.set([1,2,3],{"name":"fu"});map.set(false,"fffff");console.log(map.get(false));//fffff复制代码
模式的扩展
严格模式
1、严格模式的启动:启动严格模式非常简单只需要一句话“use dstrict”即可。
2、严格模式非正常模式的区别:
a、在严格模式下不能使用没有var的变量。
c、在严格模式下不能把函数定义在if语句中。
d、在严格模式下函数不能有重名的形参。
"use strict"function f(a,b){ console.log(a,b); console.log(arguments[0],arguments[1]) a=666; console.log(a,b); console.log(arguments[0],arguments[1])
}f(1,2);
//输出结果
1 2
1 2
666 2
1 2
1 2
1 2
666 2
1 2
复制代码
f、在严格模式下function中的this就不再在是window。
"use strict"function f(){ console.log(this) //undefined}f();复制代码
g、严格模式也是具有作用域我们可以在这个代码段中启动也可以在函数中启动严格模式。
a=1; //不会报错 function f() { "use strict" b=111;//not defined } f();复制代码
类的引入
在Es6中引入了class用于创建类进行对象的批量生成。
在Es5中我们批量生成对象有两种方式:工厂模式和构造器+原型模块式。
由于工厂模式:生产出来对象没有详细的构造器都为Object函数式对象的来源不明,且方法不能共享,造成内存的浪费。所以不怎么用。
构造器+原型模式:
function Rect(w,h) { if(!(this instanceof Rect))。 { return new Rect(w,h); } this.width=w; this.height=h;}; Rect.prototype= { constructor:Rect, getArea:function(){return this.width*this.height;}, getC:function(){return 2*(this.width+this.height);}, }
let r1=new Rect(4,5);console.log(r1.getArea());console.log(r1.getC());let r2=Rect(5,6);console.log(r2.getArea()); 复制代码
构造器+原型模式的优点:可以进行批量的生产对象,并且对象都有属于自己的构造器。
构造器+原型模式的缺点:但是在实现继承时比较麻烦,属性继承要使用call(),方法继承要使用浅拷贝。
Es6的class关键字。
3、constructor()构造函数进行属性的初始化。
class NBAplayer{ constructor(name,age,height)//相当于java中的构造函数进行属性的初始化 { this.name=name; this.age=age; this.height=height; } say() { return `我是${this.name},我今年${this.age}岁,我身高${this.height}`; } } let n1=new NBAplayer("科比",33,198); console.log(n1.say());复制代码
a、用 extends 关键字来实现继承 。
b、在子类中的构造器 constructor 中,必须要显式调用父类的 super 方法,如果不调用,则 this 不可用。
lass MvpPlayer extends NBAplayer{ constructor(name,age,height,year) { super(name,age,height);//继承父属性 this.year=year; }; showMvp()//定义自己的方法 { return `我是${this.name},我今年${this.age}岁,我是${this.year}的MVP` }; static jump()//定义自己的方法 { console.log("------------jump----------"); }}let mvp1=new MvpPlayer("小库里",25,200,2019)console.log(mvp1.say());console.log(mvp1.showMvp());MvpPlayer.jump();复制代码
class关键字也是相当的给力。
以上就是我对Es6新特性的理解与总结。这也是我在掘金上的第一篇文章不免有错误和不足,希望发现错误能在评论区指正,让我们共同进步。最后感谢你能阅读这篇文章,希望这篇文章能对你在学习上有帮助。