前端框架Vue自学之ES6基本语法(补充)
本博客目的:记录Vue学习的进度和心得(ES6基本语法)
内容:掌握Vue中常用的ES6基础语法。
正文:
ES6基本语法
一、let与var(闭包,块级作用域)
1、事实上var的设计可以看成JavaScript语言设计上的错误,但是这种错误多半不能修复和移除,因为需要向后兼容。例如if/for的块级的影响(ES5中if、for没有作用域,只有函数有作用域),因而JS作者引入了let,我们可以将let看成更完美的var(let有块级作用域(作用域:在一定范围内是可用的),var通常是定义全局变量的)。
2、先前for的块级影响,通常的解决方法是使用闭包(因为函数有自己的作用域,应用外面的变量,能正确解决传参的问题)。(闭包是指有权访问另一个函数作用域中的变量的函数,或者说,函数对象可以通过作用域关联起来,函数体内的变量都可以保存在函数作用域内,这在计算机科学文献中称为“闭包”,所有的javascirpt函数都是闭包)
3、ES6中let具有块级作用域。ES5中var是没有块级作用域的。
二、const
1、在JavaScript中,使用const修饰的标识符为常量。不可再次赋值,可以用来保证数据的安全性。
2、建议:在ES6开发中,优先使用const,只有需要改变某一个标识符额时候才使用let。
3、在使用const定义标识符时,必须进行赋值。
4、常量的含义是向的对象不能修改,但是可以改变对象内部的属性。(相当于是在内存空间,const指定了一个固定的对象内存地址(指向对象),但是我们可以修改内部的数据)
三、对象字面量的增强写法
1、属性的增强写法。相当于把变量名称作为key,对应的值作为value。
1 const name = 'abc'; 2 const height = 1.80; 3 4 const obj = { 5 name, 6 height, 7 }; 8 console.log(obj);//{ name: 'abc', height: 1.8 }
2、函数的增强写法。
1 const obj = { 2 run(){ 3 4 }, 5 done(){ 6 7 } 8 };
四、for循环
1、普通的for循环。用具有块作用域的let控制循环变量。如for(let i = 0; i < item.length; i++){item[i]...}。
2、for(let i in items){i.....} 。{}里面可以正确调用i的索引。
3、for(let item of items){item}。{}里面可以直接遍历items,生成一个个item对象。
4、reduce/filter/map。对于数组来说:(看代码例子)【超级好用!】
filter函数的使用。filter中的回调函数有一个要求:必须返回一个布尔值,当返回true时,函数内部会自动将这次回调的参数加入到新的数组中,当返回false时,函数内部会过滤掉这个参数。
map函数的使用。对数组的每个元素都进行处理,返回处理后的结果。
reduce函数的使用。reduce是对数组中所有的内容进行汇总。可以传入两个参数,第一个参数是个函数,第二个参数是初始返回值。
上述函数可以链式组合。(这就是函数式编程)(tips:编程范式分类:命令式编程/声明式编程;面向对象编程(第一公民:对象)/函数式编程(第一公民:函数)。例如vue、react框架就是声明式编程,JavaScript就是函数式编程。)
1 const nums = [10, 20, 420, 50, 40,] 2 let newNums = nums.filter(function(n){ 3 return n < 100; 4 }); 5 console.log(newNums);//[ 10, 20, 50, 40 ] 6 let new2Nums = newNums.map(function(n){ 7 return n*2; 8 }); 9 console.log(new2Nums);//[ 20, 40, 100, 80 ] 10 let new3Nums = new2Nums.reduce(function(preValue, n) { 11 return preValue + n; 12 },0); 13 //一共遍历4次,第1次:preValue 0 n 20 返回20(0+20) 14 //第2次:preValue 20 n 40 返回 60; 15 //第3次:preValue 60 n 100 返回 160; 16 //第4次:preValue 160 n 80 返回 240; 17 console.log(new3Nums);//240 18 //上述函数可以链式组合 19 let finalResult = nums.filter(function(n){ 20 return n < 100; 21 }).map(function(n){ 22 return n*2; 23 }).reduce(function(preValue, n) { 24 return preValue + n; 25 }, 0); 26 console.log(finalResult);//240 27 //还可以结合ES6的箭头函数写法 28 let finalResult2 = nums.filter(n => n < 100).map(n => n * 2).reduce((pre, n) => pre + n,0) 29 console.log(finalResult2);//240
五、箭头函数的使用和this指向
1、箭头函数
箭头函数是ES6特有的一种函数的写法,其目的是简化写法。
1 // 格式1: 2 // const a = (参数列表) =>{ 3 // // 函数体 4 // } 5 // 例子1: 两个参数 6 const sum = (num1, num2) => { 7 return num1 + num2 8 } 9 // 例子2:一个参数时,小括号可以省略 10 const power = num => { 11 return num * num 12 }//等价于 const power = num => num * num
当函数体代码块只有一行代码时,还可以简写,把return省略。
1 const sum2 = (num1, num2) => num1 + num2
2、箭头函数的this指向(可以参考这个博客:js的this指向问题)
ES5中,this总是指向调用的对象。
箭头函数中的this是向外层作用域中, 一层层查找this,直到有this的定义(父级作用域的this保持一致)。下例中,第一个this,setTimeout中的function可以理解为callback(回调函数),相当于在setTimeout调用一个外部定义的函数,而这个函数没有显式绑定对象,所以其this指向window对象。而第二个this,由于使用的是箭头函数,箭头函数的this会绑定父级作用域,所以虽然在setTimeout调用一个外部定义的函数,这个箭头函数的外层作用域是aaa函数,这个aaa函数作用域的this,就是指向这个obj。
1 const obj = { 2 aaa() { 3 setTimeout(function () { 4 console.log(this);//指向window对象 5 }) 6 setTimeout(() => { 7 console.log(this);//指向obj对象 8 }) 9 } 10 }
六、Promise
1、什么是promise及基本使用
promise是异步(async)编程的一种解决方案。(ES6引入)
同步(sync)的缺点就是可能会产生阻塞,例如,用户在往服务器请求一些数据,如果使用的是同步,那么用户只能等待请求完数据才能进行别的操作,即产生了阻塞,这样用户体验是非常不好的。
处理异步事件的一种很常见的场景是网络请求。我们封装一个网络请求的函数,因为不能立即拿到结果,所以不能像简单的3+4=7一样将结果返回。所以往往我们会传入另外一个函数,在数据请求成功时,将数据通过传入的函数回调出去。如果只是一个简单的网络请求,那么这种方案不会给我们带来很大的麻烦。但是当网络请求非常复杂时,就会出现回调地狱(callback hell),回调中嵌套着回调。
所以使用promise可以解决这个问题。
这里,我们用一个定时器(setTImeout)来模拟异步事件:假设下面的data是从网络上1秒后请求的数据,console.log就是我们的处理方式。
我们将它换成promise代码,即进行promise封装。
其中,Promise是一个类,里面以一个函数作为参数,这个函数本身又包含两个参数:resolve,reject。resolve,reject本身又是函数。然后异步操作放置在promise中,但是我们不希望所有异步操作(如果很多个的话)都集中写在里面(不然和没有封装一样了,还是会出现回调地狱),我们需要调用resolve函数,resolve函数匹配一个函数叫then,然后把异步操作的代码放置在此,resolve把一些数据会传给then。注意then可以返回的一个新建的promise对象,在这个对象可以封装着另一个异步操作(这样代码结构逻辑就很清晰了)。这就是链式编程。
异步请求成功了调用resolve,失败了调用reject。对应地,reject函数对应一个函数叫catch,捕获错误,reject函数把这些数据传给catch,并执行catch函数。
当我们使用new Promise()时,new就会执行Promise类的构造函数(保存了一些状态信息;执行传入的函数),在执行传入的回调函数时,传入两个参数,resolve和reject。
2、Promise的三种状态和另外处理方式
首先,当我们开发中有异步操作时,就可以给异步操作包装一个Promise。异步操作之后会有三种状态:
pending:等待状态,比如正在进行网络请求,或者定时器没有到时间。
fulfill:满足状态,当我们主动回调了resolve时,就处于该状态,并且会回调.then()。
reject:拒绝状态,当我们主动回调了reject时,就处于该状态,并且会回调.catch()。
我们也可以在then里面把错误的情况写在里面。即then里面可以使用两个函数,函数1对应resolve传入的数据做处理,函数2对应reject传入的数据做处理(相当于catch())。
(此时,执行的是consolo.log(err))
3、Promise的链式调用
我们在看Promise的流程图时,发现无论是then还是catch都可以返回一个Promise对象。所以,我们的代码其实是可以进行链式调用的。
这里我们直接通过Promise包装了一下新的数据,将Promise对象返回了:Promise.resovle():将数据包装成Promise对象,并且在内部回调resolve函数;Promise.reject():将数据包装成Promise对象,并且在内部回调reject函数。
(进一步简化)
还可以进一步简化,then里面的return的对象会被promise封装的。
但并不是每次都是异步请求成功的,所以当有reject时,后续的then是不执行的,但catch可以捕获错误。
其也有简写,即使用Promise.reject()。此外,也可以是 throw 'error'来代替return Promise.reject('error')来抛出异常,也是可以被catch()捕获的。
4、Promise的all方法使用
假设我们需求本身需要两个网络请求,请求一和请求二。如何判断两个请求结果都有了呢?使用Promise.all()。只有都完成了,才会执行这个promise的then函数。results(是个iterator)返回的是上述请求的结果(两个)。
七、