ES6初学
1.新增关键词
let和
const
- ###
let
和const
声明的是块级作用域
let a=0;
{
let a=1;
}
console.log(a) // 0
var a=1;
{
var a=1;
}
console.log(a) // 0
//假设有5个button标签
var allBtns=document.querySelectorAll("button");
for( var i=0;i<allBtns.length;i++ ){
allBtns[i].onclick=function(){
console.log(i);
}
}
解决方法一
//假设有5个button标签
var allBtns=document.querySelectorAll("button");
for( var i=0;i<allBtns.length;i++ ){
(function(i){
allBtns[i].onclick=function(){
console.log(i);
};
})(i)
}
解决方法二
//假设有5个button标签
var allBtns=document.querySelectorAll("button");
for( var i=0;i<allBtns.length;i++ ){
allBtns[i].onclick=(function(i){
return function(){
console.log(i);
}
})(i);
}
而使用let
定义,则可以方便很多
解决方法三
//假设有5个button标签
var allBtns=document.querySelectorAll("button");
for( let i=0;i<allBtns.length;i++ ){
allBtns[i].onclick=function(){
console.log(i);
}
}
/*
*因为let定义的是块级作用域变量
*每当循环一次,会产生一个新的块级作用域
*每个作用域的变量互不影响,互不干扰
*/
- ###
let
和const
不能像var
一样变量提升
console.log(a) //undefined
var a=1;
console.log(b) //ReferenceError: Cannot access 'b' before initialization
let b=1;
- ### 在当前作用域内,使用变量不能再
let
和const
定义之前,叫做暂时性死区(TDZ)
{
console.log(a) //undefined
var a=1;
}
{
console.log(a) //ReferenceError: Cannot access 'a' before initialization
let a=1;
}
这样子导致了typeof
函数不再是一个安全的函数了
{
typeof(a); //"undefined"
var a=0;
}
{
typeof(a); //ReferenceError: Cannot access 'a' before initialization
let a=0;
}
有些短暂性死区比较隐蔽
function fun(a=b,b=2){
return a+b;
}
fun(1,2) //报错
但是需要注意的是,出现这种情况,是需要在没有给函数传参的情况下,下面这种情况就不会了
function fun(a=b,b){
return a+b;
}
fun(1,2); //3
- ###
let
和const
不允许重复定义
let a;
let a; //SyntaxError: Identifier 'a' has already been declared
const PI=3.14;
const PI=3.14; //SyntaxError: Identifier 'PI' has already been declared
- ###
const
初始化定义后,必须马上赋值
const PI; //SyntaxError: Missing initializer in const declaration
- ###
const
不允许重新定义的是指向地址值保存的数据
const obj={
name:"jpc",
age:19
}
obj.name="qqq"; //正常赋值
console.log(obj.name) //qqq
- 常量声明最好使用大写
- ### ES5到ES6函数作用域的变化[特别注意]
//ES5环境
function fun(){
console.log("I am outside");
}
(function(){
if(false){
function fun(){
console.log("I am inside");
}
}
fun() // I am inside
})();
//ES6环境
function fun(){
console.log("I am outside");
}
(function(){
if(false){
function fun(){
console.log("I am inside");
}
}
fun() // TypeError: fun is not a function
})();
原理
//ES6环境
function fun(){
console.log("I am outside");
}
(function(){
let fun=undefined;
if(false){
function fun(){
console.log("I am inside");
}
}
fun() // TypeError: fun is not a function
})();
2.解构赋值
- ### 普通数组的解构赋值
var arr=[1,2,3];
var [a,b,c]=arr;
console.log(a,b,c) // 1,2,3
var arr=[1,[1,2],3];
var [a,b,c]=arr;
console.log(a,b,c) // 1,[1,2],3
var arr=[1,[1,2],3];
var [a,[b],c]=arr;
console.log(a,b,c) // 1,1,3
var arr=[1,2,3];
var [a,b,c,d]=arr;
console.log(a,b,c,d) // 1,2,3,undefined
支持默认值得设置
var arr=[1,2,3];
var [a,b,c,d=4]=arr;
console.log(a,b,c,d) // 1,2,3,4
特别注意的是
var arr=[1,2,3,undefined];
var [a,b,c,d=4]=arr;
console.log(a,b,c) // 1,2,3,4
/*
*当赋值的值为undefiend基本数据类型,且有默认值,则赋值的还是默认值
*/
var arr=[1,2,3,null];
var [a,b,c,d=4]=arr;
console.log(a,b,c) // 1,2,3,null
/*
*null不受影响
*/
##### 数组的解构赋值只要支持Iterator接口的对象,都可以进行解构赋值,基本数据类型不支持数组的解构赋值
var [a,b,c]=new Set([1,2,3]);
##### 默认值为表达式时,是惰性求值,只有当赋值的值为undefined
时,才会调用
var [a=f()]=[0];
console.log(a) //0
function f(){
console.log("我执行了")// 不执行
}
- ## 对象的解构赋值
var obj={
name:"jpc",
age:19
}
var {name,age,sex}=obj;
console.log(name,age,sex); //"jpx" 19 undefined
##### 对象是依据键名配对的方式进行解构赋值的,所以obj对象内没有sex属性,sex变量的值就为undefined
const { log } = console;
log('hello') // hello
##### 如果变量名与属性名不同,但是又需要赋值,可以如此解决
/*
*前提
*把obj对象内的firstName属性和lastName属性赋值到全局变量first和last中
*/
var obj={
firstName:"Mark",
lastName:"Cheng"
}
var {firstName:first,lastName:last}=obj;
console.log(first,last); //Mark Cheng
/*
*这样子的写法我现在很不能够理解,我认为应该这么写的
*var {first:firstName,last:lastName}=obj;
*貌似又理解了
*/
- ### 字符串的解构赋值
var str="abcd";
var [a,b,c]=str;
console.log(a,b,c) // a b c
##### 当一个变量被定义了,然后在解构赋值,可能会报错,可以在外加一个圆括号解决
var x;
({x} = {x: 1});
3.函数的扩展&箭头函数
- ### 箭头函数基本写法
var fn=(a,b)=>{ return a+b; };
console.log(fn(1,2)); //3
var fn=(a,b)=>a+b;
console.log(fn(1,2)); //3
var fn=(a,b)=>({a:a,b:b});
console.log(fn(1,2)); //{a:1,b:2}
var fn=a=>{ return a; };
console.log(fn(1)); //a
- 箭头函数的
this
指向代码所在对象的对象,且不能指定this
- 箭头函数内没有
arguments
对象 - 箭头函数作为构造函数使用
- 不能使用
yield
命令,不能做Generator
函数 - ### ES6中,函数可以设置默认参数了
function fun(a=0,b=0){
return a+b;
}
console.log(fun()) // 0
传统写法
function fun(a,b){
a= a || 0;
b= b || 0;
return a+b;
}
console.log(fun()) // 0
let fun2=({a,b})=>{
console.log(a,b);
}
fun2();//TypeError: Cannot destructure property 'a' of 'undefined' as it is undefined.
/*
*函数fun2需要输入一个对象参数,而我们调用函数的时候,
*由于没有传值,所以参数值为undefined,
*而函数内部调用对象参数下的a属性时,
*undefiend.a的形式,所以报错
*/
- ### 如果函数尾参数不设置参数,而在非尾位置设置了参数没有意义的
function fun(x=1,y=10,z){
return x+y+z;
}
fun(7,8,9); //24
/*
*此时,就算非尾参数都设置了默认值,但是没有意义
*/
- 函数的
length
属性
这个属性计算的是实参传入的个数,即除去含默认值的参数之后的参数总数
function fun(a,b,c){
}
fun.length // 3
function fun2(a,b,c=0){
//
}
fun2.length //2
- rest参数(
...变量名
)
可以为函数获取到剩余的参数,并转换为真数组,及时没有剩余的参数,也会转换为空数组
rest参数之后不允许有其他参数了
function fun(x,y,...all){
console.log(all); //[2,3,4,5]
console.log(Object.prototype.toString.call(all)); //"[object Array]"
}
fun(0,1,2,3,4,5);
- 函数内使用了严格模式,那么参数默认值、解构赋值和rest参数都不能设置了
这里需要注意的是,是在函数内使用了严格模式,即函数外作用域(或全局)设置了严格模式,是不受到影响的
//设置参数默认值的报错
function fun(x=0){
"use strict";
console.log(x) //报错
}
fun(1);
//设置解构赋值的报错
function fun({x}){
"use strict";
console.log(x) //报错
}
fun({x:1});
//设置rest参数的报错
function fun(...x){
"use strict";
console.log(x) //报错
}
fun(0,1,2,3);
//不在函数内使用严格模式,无报错,正常运行
"use strict";
function fun(x=0){
console.log(x) //1
}
fun(1);
function fun({x}){
console.log(x) //1
}
fun({x:1});
function fun(...x){
console.log(x) //[0,1,2,3]
}
fun(0,1,2,3);
- 函数的
name
属性 - 具名函数返回的实际的函数名,不管是否赋值给了变量
function fun(){} fun.name // fun let fun3=function fun2(){} fun3.name //fun2
- 匿名函数赋值给变量后,ES5中返回的是空字符串(""),ES6中返回的是变量名称
ES5环境 let fun=function (){} fun.name //""
ES6环境 let fun=function (){} fun.name //"fun"
- 尾调用--函数
return
一个函数调用 - 尾递归--函数
return
调用自身 - ### 柯里化--把多参数的函数转换成单参数函数的形式
4.迭代器、生成器
Iterator
迭代器
数组,字符串,Generator
生成器
yield
必须包含在当前函数内,不能在嵌套函数内- ##### 如果在另外一个表达式内,必须要加上括号
function* demo() {
console.log('Hello' + yield); // SyntaxError
console.log('Hello' + yield 123); // SyntaxError
console.log('Hello' + (yield)); // OK
console.log('Hello' + (yield 123)); // OK
}
- #### 如果作为函数的参数不需要括号
function *Gen4(x,y){
console.log(yield x,yield y);
return x+y;
}
var f4=Gen4(5,8);
f4.next() // {value:5,done:false}
f4.next() // {value:8,done:false}
f4.next() // undefined undefined
// {value:13,done:true}
- ####
Generator.prototype.return
function *Gen2(){
yield 1;
yield 2;
yield 3;
return "over";
}
var f2=Gen2();
f2.next() //{ value:1,done:false }
f2.return(99) //{ value:99,done:true }
f2.next() //{ value:undefined,done:true }
- #### 如果里面有
try{}finally{}
语句,当在执行try语句的代码块的时候,使用return方法,会直接进入到finally的语句中,如果没有return,最后的是提供的return值,否则就是默认的
function *Gen3(){
yield 1;
yield 2;
try{
yield 3;
yield 4;
}finally{
yield 5;
yield 6;
return 7;
}
return 8;
}
var f3=Gen3();
f3.next(); // {value: 1, done: false}
f3.next(); // {value: 2, done: false}
f3.next(); // {value: 3, done: false}
f3.return(99) //{value: 5, done: false}
f3.next(); // {value: 6, done: false}
f3.next(); // {value: 7, done: true}
function *Gen3(){
yield 1;
yield 2;
try{
yield 3;
yield 4;
}finally{
yield 5;
yield 6;
}
return 8;
}
var f3=Gen3();
f3.next(); // {value: 1, done: false}
f3.next(); // {value: 2, done: false}
f3.next(); // {value: 3, done: false}
f3.return(99) //{value: 5, done: false}
f3.next(); // {value: 6, done: false}
f3.next(); // {value: 99, done: true}
function *Gen3(){
yield 1;
yield 2;
try{
yield 3;
yield 4;
}finally{
yield 5;
yield 6;
return 7;
}
return 8;
}
var f3=Gen3();
f3.next(); // {value: 1, done: false}
f3.next(); // {value: 2, done: false}
f3.next(); // {value: 3, done: false}
f3.next(); // {value: 4, done: false}
f3.next(); // {value: 5, done: false}
f3.next(); // {value: 6, done: false}
f3.next(); // {value: 7, done: true}
- Generator函数内调用Generator函数的方法(加*号)
function *genOuter(){ yield 0; yield *genInner(); yield 6; } function *genInner(){ yield 1; yield 2; yield 3; yield 4; yield 5; } var gen=genOuter(); gen.next(); //{ value:0,done:false } gen.next(); //{ value:1,done:false } gen.next(); //{ value:2,done:false } gen.next(); //{ value:3,done:false } gen.next(); //{ value:4,done:false } gen.next(); //{ value:5,done:false } gen.next(); //{ value:6,done:false } gen.next(); //{ value:undefined,done:true }
不加*号
function *genOuter(){ yield 0; yield genInner(); yield 6; } function *genInner(){ yield 1; yield 2; yield 3; yield 4; yield 5; } var gen=genOuter(); gen.next(); //{ value:0,done:false } gen.next(); //{ value:getInner,done:false } (一个遍历器对象) gen.next(); //{ value:6,done:false } gen.next(); //{ value:undefined,done:true }
- Generator函数不能做为构造函数,否则会报错
function* F() { yield this.x = 2; yield this.y = 3; } new F() // TypeError: F is not a constructor
5.小总结
Promise
是为了解决回调函数多层嵌套、不易于开发维护的结果,回调地狱Generator
可以让事件暂停,去做另外一件事,然后继续做这件事async
是Generator
的语法糖,更加自动化,更加便捷- 可原生迭代的 map,set,字符串,数组,dom节点
6.Promise
resolve
-->成功
reject
-->失败
catch
finally
Promise.all
全部完成后调用
Promise.race
竞争,只处理一个结果
示例代码
- Promise可以进行链式调用,如果第一个then不自定义返回的Promise实例对象,则第二个then获取的一个新的Promise且为resolve状态
本作品采用 知识共享署名-相同方式共享 4.0 国际许可协议 进行许可。