JsES6基本概念.docx
《JsES6基本概念.docx》由会员分享,可在线阅读,更多相关《JsES6基本概念.docx(17页珍藏版)》请在冰点文库上搜索。
![JsES6基本概念.docx](https://file1.bingdoc.com/fileroot1/2023-6/12/fd92890c-1242-4755-bf2d-eca05bb115e9/fd92890c-1242-4755-bf2d-eca05bb115e91.gif)
JsES6基本概念
ECMAScript6.0(以下简称ES6)是JavaScript语言的下一代标准,已经在2015年6月正式发布了。
它的目标,是使得JavaScript语言可以用来编写复杂的大型应用程序,成为企业级开发语言。
ECMAScript和JavaScript的关系
一个常见的问题是,ECMAScript和JavaScript到底是什么关系?
要讲清楚这个问题,需要回顾历史。
1996年11月,JavaScript的创造者Netscape公司,决定将JavaScript提交给标准化组织ECMA,希望这种语言能够成为国际标准。
次年,ECMA发布262号标准文件(ECMA-262)的第一版,规定了浏览器脚本语言的标准,并将这种语言称为ECMAScript,这个版本就是1.0版。
该标准从一开始就是针对JavaScript语言制定的,但是之所以不叫JavaScript,有两个原因。
一是商标,Java是Sun公司的商标,根据授权协议,只有Netscape公司可以合法地使用JavaScript这个名字,且JavaScript本身也已经被Netscape公司注册为商标。
二是想体现这门语言的制定者是ECMA,不是Netscape,这样有利于保证这门语言的开放性和中立性。
因此,ECMAScript和JavaScript的关系是,前者是后者的规格,后者是前者的一种实现(另外的ECMAScript方言还有Jscript和ActionScript)。
日常场合,这两个词是可以互换的。
在讲解ES6语法之前,我们得先了解下Babel。
Babel是一个广泛使用的ES6转码器,可以将ES6代码转为ES5代码,从而在现有环境执行。
这意味着,你可以用ES6的方式编写程序,又不用担心现有环境是否支持。
下面是一个例子。
//转码前
input.map(item=>item+1);
//转码后
input.map(function(item){
returnitem+1;
});
上面的原始代码用了箭头函数,Babel将其转为普通函数,就能在不支持箭头函数的JavaScript环境执行了。
1、变量声明 const 和 let
我们都是知道在 ES6 以前,var 关键字声明变量。
无论声明在何处,都会被视为声明在函数的最顶部(不在函数内即在全局作用域的最顶部)。
这就是函数变量提升例如:
functionaa(){
if(bool){
vartest='helloman';
}else{
console.log(test);//bool 是false的话, 执行结果是 undefined
}
}
以上的代码实际上是:
functionaa(){
vartest;//变量提升
if(bool){
test='helloman';
}else{
//此处访问 test值为 undefined
console.log(test);
}
//此处访问 test值为 undefined
}
所以不用关心bool是否为trueorfalse。
实际上,无论如何test都会被创建声明。
接下来 ES6 主角登场:
我们通常用 let 和 const 来声明,let 表示变量、const 表示常量。
let 和 const 都是块级作用域。
怎么理解这个块级作用域?
在一个函数内部
在一个代码块内部
说白了{}大括号内的代码块即为let 和 const的作用域。
看以下代码:
functionaa(){
if(bool){
lettest='helloman'
}else{
//报错, testisnotdefined
console.log(test)
}
}
let的作用域是在它所在当前代码块,但不会被提升到当前函数的最顶部。
再来说说const。
constname='lux'
name='joe'//再次赋值此时会报错, Assignmenttoconstantvariable.
说一道面试题
varfuncs=[]
for(vari=0;i<10;i++){
funcs.push(function(){console.log(i)})
}
funcs.forEach(function(func){
func()
})
这样的面试题是大家常见,很多同学一看就知道输出10十次
但是如果我们想依次输出0到9呢?
两种解决方法。
直接上代码。
//ES5告诉我们可以利用闭包解决这个问题
varfuncs=[]
for(vari=0;i<10;i++){
func.push((function(value){
returnfunction(){
console.log(value)
}
}(i)))
}
//es6
for(leti=0;i<10;i++){
func.push(function(){
console.log(i)
})
}
达到相同的效果,es6简洁的解决方案是不是更让你心动!
!
!
2、模板字符串
es6 模板字符简直是开发者的福音啊,解决了 ES5 在字符串功能上的痛点。
第一个用途,基本的字符串格式化。
将表达式嵌入字符串中进行拼接。
用 ${} 来界定。
//es5
varname='lux'
console.log('hello'+name)
//es6
constname='lux'
console.log(`hello${name}`)//hellolux
第二个用途,在 ES5 时我们通过反斜杠(\)来做多行字符串或者字符串一行行拼接。
ES6反引号(``)直接搞定。
//es5
varmsg="Hi\
man!
"
//es6
consttemplate=`
`
//实例
constname="zhangsan"
consttemplate=`
`
console.log(template);
//
对于字符串 es6 当然也提供了很多厉害的方法。
说几个常用的。
//1.includes:
判断是否包含然后直接返回布尔值
letstr='hahay'
console.log(str.includes('y'))//true
//2.repeat:
获取字符串重复n次
lets='he'
console.log(s.repeat(3))//'hehehe'
//如果你带入小数,Math.floor(num)来处理
3、函数
3.1、函数默认参数
在 ES5 我们给函数定义参数默认值是怎么样?
functionaction(num){
num=num||200;
//当传入num时,num为传入的值
//当没传入参数时,num即有了默认值200
returnnum;
}
但细心观察的同学们肯定会发现,num 传入为0的时候就是 false,此时 num=200与我们的实际要的效果明显不一样
ES6为参数提供了默认值。
在定义函数时便初始化了这个参数,以便在参数没有被传递进去时使用。
functionaction(num=200){
console.log(num);
}
action()//200
action(300)//300
3.2、箭头函数
ES6很有意思的一部分就是函数的快捷写法。
也就是箭头函数。
箭头函数最直观的三个特点
不需要 function 关键字来创建函数
省略 return 关键字
继承当前上下文的this关键字
//例如:
[1,2,3].map(x=>x+1)
//等同于:
[1,2,3].map((function(x){
returnx+1
}).bind(this))
说个小细节。
当你的函数有且仅有一个参数的时候,是可以省略掉括号的。
当你函数返回有且仅有一个表达式的时候可以省略{};例如:
varpeople=name=>'hello'+name
//参数 name 就没有括号
作为参考
varpeople=(name,age)=>{
constfullName='h'+name
returnfullName
}
//如果缺少()或者{}就会报错
4、拓展的对象功能
对象初始化简写
ES5我们对于对象都是以键值对的形式书写,是有可能出现键值对重名的。
例如:
functionpeople(name,age){
return{
name:
name,
age:
age
};
}
键值对重名,ES6 可以简写如下:
functionpeople(name,age){
return{
name,
age
};
}
ES6同样改进了为对象字面量方法赋值的语法。
ES5 为对象添加方法:
constpeople={
name:
'lux',
getName:
function(){
console.log(this.name)
}
}
ES6通过省略冒号与function关键字,将这个语法变得更简洁
constpeople={
name:
'lux',
getName(){
console.log(this.name)
}
}
ES6对象提供了 Object.assign() 这个方法来实现浅复制。
Object.assign() 可以把任意多个源对象自身可枚举的属性拷贝给目标对象,然后返回目标对象。
第一参数即为目标对象。
在实际项目中,我们为了不改变源对象。
一般会把目标对象传为{}
constobj=Object.assign({},objA,objB)
5、更方便的数据访问--解构
数组和对象是JS中最常用也是最重要表示形式。
为了简化提取信息,ES6新增了解构,这是将一个数据结构分解为更小的部分的过程。
ES5我们提取对象中的信息形式如下:
constpeople={
name:
'lux',
age:
20
}
constname=people.name
constage=people.age
console.log(name+'---'+age)
是不是觉得很熟悉,没错,在 ES6 之前我们就是这样获取对象信息的,一个一个获取。
现在,解构能让我们从对象或者数组里取出数据存为变量,例如
//对象
constpeople={
name:
'lux',
age:
20
}
const{name,age}=people
console.log(`${name}---${age}`)
//数组
constcolor=['red','blue']
const[first,second]=color
console.log(first)//'red'
console.log(second)//'blue'
6、SpreadOperator展开运算符
ES6中另外一个好玩的特性就是 SpreadOperator 也是三个点儿...接下来就展示一下它的用途。
组装对象或者数组
//数组
constcolor=['red','yellow']
constcolorful=[...color,'green','pink']
console.log(colorful)//[red,yellow,green,pink]
//对象
constalp={fist:
'a',second:
'b'}
constalphabets={...alp,third:
'c'}
console.log(alphabets)//{"fist":
"a","second":
"b","third":
"c"}
有时候我们想获取数组或者对象除了前几项或者除了某几项的其他项
//数组
constnumber=[1,2,3,4,5]
const[first,...rest]=number
console.log(rest)//2,3,4,5
//对象
constuser={
username:
'lux',
gender:
'female',
age:
19,
address:
'peking'
}
const{username,...rest}=user
console.log(rest)//{"address":
"peking","age":
19,"gender":
"female"}
对于Object而言,还可以用于组合成新的Object。
(ES2017stage-2proposal)当然如果有重复的属性名,右边覆盖左边
constfirst={
a:
1,
b:
2,
c:
6,
}
constsecond={
c:
3,
d:
4
}
consttotal={...first,...second}
console.log(total)//{a:
1,b:
2,c:
3,d:
4}
7、import和export
import 导入模块、export 导出模块
//全部导入
importpeoplefrom'./example'
//有一种特殊情况,即允许你将整个模块当作单一对象进行导入
//该模块的所有导出都会作为对象的属性存在
import*asexamplefrom"./example.js"
console.log(example.name)
console.log(example.age)
console.log(example.getName())
//导入部分
import{name,age}from'./example'
//导出默认,有且只有一个默认
exportdefaultApp
//部分导出
exportclassAppextendComponent{};
以前有人问我,导入的时候有没有大括号的区别是什么。
下面是我在工作中的总结:
1.当用 exportdefaultpeople 导出时,就用importpeople导入(不带大括号)
2.一个文件里,有且只能有一个 exportdefault。
但可以有多个 export。
3.当用 exportname时,就用 import{name} 导入(记得带上大括号)
4.当一个文件里,既有一个 exportdefaultpeople,又有多个 exportname或者exportage 时,导入就用importpeople,{name,age}
5.当一个文件里出现n多个export导出很多模块,导入时除了一个一个导入,也可以用 import*asexample
8、Promise
在 promise 之前代码过多的回调或者嵌套,可读性差、耦合度高、扩展性低。
通过Promise机制,扁平化的代码机构,大大提高了代码可读性;用同步编程的方式来编写异步代码,保存线性的代码逻辑,极大的降低了代码耦合性而提高了程序的可扩展性。
说白了就是用同步的方式去写异步代码。
发起异步请求
fetch('/api/todos')
.then(res=>res.json())
.then(data=>({data}))
.catch(err=>({err}));
今天看到一篇关于面试题的很有意思。
setTimeout(function(){
console.log
(1)
},0);
newPromise(functionexecutor(resolve){
console.log
(2);
for(vari=0;i<10000;i++){
i==9999&&resolve();
}
console.log(3);
}).then(function(){
console.log(4);
});
console.log(5);
Excuseme?
这个前端面试在搞事!
当然以上 promise 的知识点,这个只是冰山一角。
需要更多地去学习应用。
9、Generators
生成器(generator)是能返回一个迭代器的函数。
生成器函数也是一种函数,最直观的表现就是比普通的 function 多了个星号*,在其函数体内可以使用yield关键字,有意思的是函数会在每个yield后暂停。
这里生活中有一个比较形象的例子。
咱们到银行办理业务时候都得向大厅的机器取一张排队号。
你拿到你的排队号,机器并不会自动为你再出下一张票。
也就是说取票机“暂停”住了,直到下一个人再次唤起才会继续吐票。
OK。
说说迭代器。
当你调用一个 generator 时,它将返回一个迭代器对象。
这个迭代器对象拥有一个叫做 next 的方法来帮助你重启 generator 函数并得到下一个值。
next 方法不仅返回值,它返回的对象具有两个属性:
done 和 value。
value 是你获得的值,done 用来表明你的 generator 是否已经停止提供值。
继续用刚刚取票的例子,每张排队号就是这里的 value,打印票的纸是否用完就这是这里的 done。
//生成器
function*createIterator(){
yield1;
yield2;
yield3;
}
//生成器能像正规函数那样被调用,但会返回一个迭代器
letiterator=createIterator();
console.log(iterator.next().value);//1
console.log(iterator.next().value);//2
console.log(iterator.next().value);//3
那生成器和迭代器又有什么用处呢?
围绕着生成器的许多兴奋点都与异步编程直接相关。
异步调用对于我们来说是很困难的事,我们的函数并不会等待异步调用完再执行,你可能会想到用回调函数,(当然还有其他方案比如 Promise 比如 Async/await)。
生成器可以让我们的代码进行等待。
就不用嵌套的回调函数。
使用generator可以确保当异步调用在我们的generator函数运行一下行代码之前完成时暂停函数的执行。
那么问题来了,咱们也不能手动一直调用next()方法,你需要一个能够调用生成器并启动迭代器的方法。
就像这样子的
functionrun(taskDef){//taskDef即一个生成器函数
//创建迭代器,让它在别处可用
lettask=taskDef();
//启动任务
letresult=task.next();
//递归使用函数来保持对next()的调用
functionstep(){
//如果还有更多要做的
if(!
result.done){
result=task.next();
step();
}
}
//开始处理过程
step();
}
生成器与迭代器最有趣、最令人激动的方面,或许就是可创建外观清晰的异步操作代码。
你不必到处使用回调函数,而是可以建立貌似同步的代码,但实际上却使用yield来等待异步操作结束。
用var声明的变量:
if(true){
vara=1;
console.log(a);//1可以访问到
}
console.log(a);//1可以访问到
用let声明的变量:
if(true){
leta=1;
console.log(a);//1可以访问到
}
console.log(a);//不可以
上面的这两个栗子,我们可以看出:
用var声明的a在包裹它的作用域空间外(代码块外)可以访问到;
而let声明的a在包裹它的作用域空间外(代码块外)不可以访问到;
还有就是let不可以同名,而var可以同名(只不过前一个声明的会被覆盖);
vara=1;
vara=2;
console.log(a);//2
letb=1;
letb=2;
console.log(b);//(报错鸟)caughtSyntaxError:
Identifier'a'hasalreadybeendeclared
const是一个常量,只能声明一次,而且不能重复声明,而且不能更改;
consta=1;
consta=2;
console.log(a);//报错
但是如果定义的常量是一个对象,我们去修改这个常量里的值不会报错;
const foo={
b:
10
}
foo.b=11;
console.log(foo.b)//11
这里const 定义一个foo之后,对其添加了属性prop,随后又对该属性进行了修改,为什么没有报错?
那是因为常量foo本身存储的是一个地址,该地址指向一个对象,不可变的是foo这个地