什么是ES6? html
因为JavaScript是上个世纪90年代,由Brendan Eich在用了10天左右的时间发明的;虽然语言的设计者很牛逼,可是也扛不住"时间紧,任务重"。所以,JavaScript在早期有不少的设计缺陷;而它的管理组织为了修复这些缺陷,会按期的给JS添加一些新的语法特性。JavaScript先后更新了不少个版本,咱们要学的是ES6这个版本。前端
ES6是JS管理组织在2015年发布的一个版本,这个版本和以前的版本大不同,包含了大量实用的,拥有现代化编程语言特点的内容,好比:Promise, async/await, class继承等。所以,咱们能够认为这是一个革命性的版本。java
使用const
来定义一个常量,常量也就是不能被修改,不能被从新赋值的变量。node
使用let
来定义一个变量,而不要再使用var
了,由于var
有不少坑;能够认为let
就是修复了bug的var
。好比,var容许重复声明变量并且不报错;var的做用域让人感受疑惑。python
最佳实践:优先用const
,若是变量须要被修改才用let
;要理解目前不少早期写的项目中仍然是用var
。git
ES6 容许咱们按照必定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)es6
const arr = [1, 2, 3] //咱们获得了一个数组 let [a, b, c] = arr //能够这样同时定义变量和赋值 console.log(a, b, c); // 1 2 3
对象的解构赋值(经常使用)github
const obj = { name: '俊哥',address:'深圳', age: '100'} //咱们获得了一个对象 let {name, age} = obj //能够这样定义变量并赋值 console.log(name, age); //俊哥 100
函数参数的解构赋值(经常使用)npm
const person = { name: '小明', age: 11} function printPerson({name, age}) { // 函数参数能够解构一个对象 console.log(`姓名:${name} 年龄:${age}`); } printPerson(person) // 姓名:小明 年龄:11
ES6 对函数增长了不少实用的扩展功能。编程
参数默认值,从ES6开始,咱们能够为一个函数的参数设置默认值
function foo(name, address = '深圳') { console.log(name, address); } foo("小明") // address将使用默认值 foo("小王", '上海') // address被赋值为'上海'
箭头函数,将function
换成=>
定义的函数,就是箭头函数
function add(x, y) { return x + y } // 这个箭头函数等同于上面的add函数 (x, y) => x +y; // 若是函数体有多行,则须要用大括号包裹 (x, y) => { if(x >0){ return x + y }else { return x - y } }
因为js一开始被设计为函数式语言,万物皆函数。全部对象都是从函数原型继承而来,经过继承某个函数的原型来实现对象的继承。可是这种写法会让新学者产生疑惑,而且和传统的OOP语言差异很大。ES6 封装了class语法来大大简化了对象的继承。
class Person { constructor(name, age){ this.name = name this.age = age } // 注意:没有function关键字 sayHello(){ console.log(`你们好,我叫${this.name}`); } } class Man extends Person{ constructor(name, age){ super(name, age) } //重写父类的方法 sayHello(){ console.log('我重写了父类的方法!'); } } let p = new Person("小明", 33) //建立对象 p.sayHello() // 调用对象p的方法,打印 你们好,我叫小明 let m = new Man("小五", 33) m.sayHello() // 我重写了父类的方法!
ES6 的新语法有不少,有人将它总结为了一本书。固然,ES6提出的只是标准,各大浏览器和node基本实现了90%以上的新特性,极其个别尚未实现。咱们目前讲的是最基本的一些语法,因为大家还未了解同步和异步的概念;Promise和async/await的内容将会在后面的课程中讲解。
ES6 入门教程:http://es6.ruanyifeng.com/
各大浏览器的支持程度:http://kangax.github.io/compat-table/es6/
我给你们讲个故事。
好久好久之前,浏览器只能展现文本和图片,并不能像如今这样有动画,弹窗等绚丽的特效。为了提高浏览器的交互性,Javascript就被设计出来;并且很快统一了全部浏览器,成为了前端脚本开发的惟一标准。
随着互联网的不断普及和Web的迅速发展,几家巨头公司开始了浏览器之战。微软推出了IE系列浏览器,Mozilla推出了Firefox浏览器,苹果推出了Safari浏览器,谷歌推出了Chrome浏览器。其中,微软的IE6因为推出的早,并和Windows系统绑定,在早期成为了浏览器市场的霸主。没有竞争就没有发展。微软认为IE6已经很是完善,几乎没有可改进之处,就解散了IE6的开发团队。而Google却认为支持现代Web应用的新一代浏览器才刚刚起步,尤为是浏览器负责运行JavaScript的引擎性能还可提高10倍,因而本身偷偷开发了一个高性能的Javascript解析引擎,取名V8,而且开源。在浏览器大战中,微软因为解散了最有经验、战斗力最强的浏览器团队,被Chrome远远的抛在身后。。。
浏览器大战和Node有何关系?
话说有个叫Ryan Dahl的歪果仁,他的工做是用C/C++写高性能Web服务。对于高性能,异步IO、事件驱动是基本原则,可是用C/C++写就太痛苦了。因而这位仁兄开始设想用高级语言开发Web服务。他评估了不少种高级语言,发现不少语言虽然同时提供了同步IO和异步IO,可是开发人员一旦用了同步IO,他们就再也懒得写异步IO了,因此,最终,Ryan瞄向了JS。由于JavaScript是单线程执行,根本不能进行同步IO操做,只能使用异步IO。
另外一方面,由于V8是开源的高性能JavaScript引擎。Google投资去优化V8,而他只需拿来改造一下。
因而在2009年,Ryan正式推出了基于JavaScript语言和V8引擎的开源Web服务器项目,命名为Node.js。虽然名字很土,可是,Node第一次把JavaScript带入到后端服务器开发,加上世界上已经有无数的JavaScript开发人员,因此Node一会儿就火了起来。
相同点就是都使用了Javascript这门语言来开发。
浏览器端的JS,受制于浏览器提供的接口。好比浏览器提供一个弹对话框的Api,那么JS就能弹出对话框。浏览器为了安全考虑,对文件操做,网络操做,操做系统交互等功能有严格的限制,因此在浏览器端的JS功能没法强大,就像是压在五行山下的孙猴子。
NodeJs彻底没有了浏览器端的限制,让Js拥有了文件操做,网络操做,进程操做等功能,和Java,Python,Php等语言已经没有什么区别了。并且因为底层使用性能超高的V8引擎来解析执行,和自然的异步IO机制,让咱们编写高性能的Web服务器变得垂手可得。Node端的JS就像是被唐僧解救出来的齐天大圣同样,法力无边。
NodeJS在用户代码层,只启动一个线程来运行用户的代码。每当遇到耗时的IO操做,好比文件读写,网络请求,则将耗时操做丢给底层的事件循环去执行,而本身则不会等待,继续执行下面的代码。当底层的事件循环执行完耗时IO时,会执行咱们的回调函数来做为通知。
同步就是你去银行排队办业务,排队的时候啥也不能干(阻塞);异步就是你去银行用取号机取了一个号,此时你能够自由的作其余事情,到你的时候会用大喇叭对你进行事件通知。而银行系统至关于底层的事件循环,不断的处理耗时的业务(IO)。
可是NodeJs只有一个线程用来执行用户代码,若是耗时的是CPU计算操做,好比for循环100000000次,那么在循环的过程当中,下面的代码将会没法执行,阻塞了惟一的一个线程。因此,Node适合大并发的IO处理,不适合CPU密集型的计算操做。Web开发大部分都是耗时IO操做,因此Node很是适合进行Web开发。若是真的遇到了CPU密集的计算,好比从1亿个用户中计算出哪些人和你兴趣相投的这个功能,就很是耗CPU,那这个功能就交由C++,C,Go,Java这些语言实现。像淘宝,京东这种大型网站绝对不是一种语言就能够实现的。
语言只是工具,让每一种语言作它最擅长的事,才能构建出稳定,强大的系统。
在浏览器端写JS,其实就是使用浏览器给咱们提供的功能和方法来写代码。
在Node端写JS,就是用Node封装好的一系列功能模块来写代码。NodeJS封装了网络,文件,安全加密,压缩等等不少功能模块,咱们只须要学会经常使用的一些,而后在须要的时候去查询文档便可。
下载地址:http://nodejs.cn/download/
安装完毕,在命令行输入:node -v
查看node的版本,若是能成功输出,证实安装没有问题。
npm是Nodejs自带的包管理器,当你安装Node的时候就自动安装了npm。通俗的讲,当咱们想使用一个功能的时候,而Node自己没有提供,那么咱们就能够从npm上去搜索并下载这个模块。每一个开发语言都有本身的包管理器,好比,java有maven,python有pip。而npm是目前世界上生态最丰富,可用模块最多的一个社区,没有之一。基本上,你所能想到的功能都不用本身手写了,它已经在npm上等着你下载使用了。
npm的海量模块,使得咱们开发复杂的NodeJs的程序变得更为简单。
学习2个知识点:
怎么生成package.json
怎么从npm安装包,并保存到package.json
文件中?
全局变量是指咱们在任何js文件的任何地方均可以使用的变量。
__dirname
:当前文件的目录
__filename
:当前文件的绝对路径
console
:控制台对象,能够输出信息
process
:进程对象,能够获取进程的相关信息,环境变量等
setTimeout/clearTimeout
:延时执行
setInterval/clearInterval
:定时器
path模块供了一些工具函数,用于处理文件与目录的路径
path.basename
:返回一个路径的最后一部分
path.dirname
:返回一个路径的目录名
path.extname
:返回一个路径的扩展名
path.join
:用于拼接给定的路径片断
path.normalize
:将一个路径正常化
文件操做相关的模块
fs.stat/fs.statSync
:访问文件的元数据,好比文件大小,文件的修改时间
fs.readFile/fs.readFileSync
:异步/同步读取文件
fs.writeFile/fs.writeFileSync
:异步/同步写入文件
fs.readdir/fs.readdirSync
:读取文件夹内容
fs.unlink/fs.unlinkSync
:删除文件
fs.rmdir/fs.rmdirSync
:只能删除空文件夹,思考:如何删除非空文件夹?使用
fs-extra
第三方模块来删除。
fs.watchFile
:监视文件的变化
传统的
fs.readFile
在读取小文件时很方便,由于它是一次把文件所有读取到内存中;假如咱们要读取一个3G大小的电影文件,那么内存不就爆了么?node提供了流对象来读取大文件。流的方式其实就是把全部的数据分红一个个的小数据块(chunk),一次读取一个chunk,分不少次就能读取特别大的文件,写入也是同理。这种读取方式就像水龙头里的水流同样,一点一点的流出来,而不是一会儿涌出来,因此称为流。
const fs = require('fs') const path = require('path') // fs.readFile('bigfile', (err, data)=>{ // if(err){ // throw err; // } // console.log(data.length); // }) // 需求复制一份MobyLinuxVM.vhdx文件 const reader = fs.createReadStream('MobyLinuxVM.vhdx') const writer = fs.createWriteStream('MobyLinuxVM-2.vhdx') // let total = 0 // reader.on('data', (chunk)=>{ // total += chunk.length // writer.write(chunk) // }) // reader.on('end',()=>{ // console.log('总大小:'+total/(1024*1024*1024)); // }) reader.pipe(writer);
任务:用如下知识点完成大文件的拷贝。
fs.createReadStream/fs.createWriteStream
reader.pipe(writer)
咱们知道,若是咱们以同步的方式编写耗时的代码,那么就会阻塞JS的单线程,形成CPU一直等待IO完成才去执行后面的代码;而CPU的执行速度是远远大于硬盘IO速度的,这样等待只会形成资源的浪费。异步IO就是为了解决这个问题的,异步能尽量不让CPU闲着,它不会在那等着IO完成;而是传递给底层的事件循环一个函数,本身去执行下面的代码。等磁盘IO完成后,函数就会被执行来做为通知。
虽然异步和回调的编程方式能充分利用CPU,可是当代码逻辑变的愈来愈复杂后,新的问题出现了。请尝试用异步的方式编写如下逻辑代码:
先判断一个文件是文件仍是目录,若是是目录就读取这个目录下的文件,找出结尾是txt的文件,而后获取它的文件大小。
恭喜你,当你完成上面的任务时,你已经进入了终极关卡:Callback hell回调地域!
为了解决Callback hell的问题,Promise
和async/await
诞生。
promise
的做用是对异步回调代码包装一下,把原来的一个回调函数拆成2个回调函数,这样的好处是可读性更好。语法以下:
语法注意:Promise内部的resolve和reject方法只能调用一次,调用了这个就不能再调用了那个;若是调用,则无效。
// 建立promise对象 let promise = new Promise((resolve, reject)=>{ // 在异步操做成功的状况选调用resolve,失败的时候调用reject fs.readFile('xxx.txt',(err, data)=>{ if(err){ reject(err) }else { resolve(data.toString()) } }) }); // 使用promise promise.then((text)=>{ //then方法是当Promise内部调用了resolve的时候执行 }).catch((err)=>{ //catch方法是当Promise内部调用了reject的时候执行 console.log(err); })
async/await
的做用是直接将Promise异步代码变为同步的写法,注意,代码仍然是异步的。这项革新,具备革命性的意义。
语法要求:
await
只能用在async
修饰的方法中,可是有async
不要求必定有await
。
await
后面只能跟async
方法和promise
。
假设拥有了一个promise对象,如今使用async/await能够这样写:
async function asyncDemo() { try { // 当promise的then方法执行的时候 let text = await promise // 当你用promise包装了全部的异步回调代码后,就能够一直await,真正意义实现了以同步的方式写异步代码 console.log('异步道明执行'); }catch (e){ // 捕获到promise的catch方法的异常 console.log(e); } } asyncDemo() console.log('我是同步代码');
小任务
使用promise和async/await来重写上面的逻辑代码,来感觉一下强大的力量吧!。
异步代码的终极写法:
先使用promise
包装异步回调代码,可以使用node提供的util.promisify
方法;
使用async/await
编写异步代码。
封装了http server 和 client的功能,就是说能够充当server处理请求,也能够发出请求。
http.createServer
:建立server对象
http.get
:执行http get请求
const http = require('http') const server = http.createServer((req, res)=>{ // console.log(`url: ${req.url} method: ${req.method}`) // res.writeHead(200, {'Content-Type':'text/plain;charset=utf-8'}) // res.end('收到了请求') router(req, res) }); // 执行get请求 // http.get("http://www.baidu.com", (res)=>{ // // console.log(res); // res.setEncoding('utf-8') // // let data = '' // res.on('data', (chunk)=>{ // data += chunk // }) // res.on('end', ()=>{ // console.log(data); // }) // })
功能需求:启动一个服务,当用户访问服务时,给用户展现指定目录下的全部文件;若是子文件是目录,则能继续点进去浏览。
const http = require('http') const fs = require('fs') const path = require('path') const util = require('util') const server = http.createServer((req, res)=>{ console.log(req.url); // 过滤favicon.ico的请求 if(req.url === '/favicon.ico'){ res.end(''); return } showDir(req, res) }); server.listen(4000) /** * 展现出指定目录下 的文件列表 * @param req * @param res */ async function showDir(req, res) { let target = 'html' if(req.url !== '/'){ target = req.url } const preaddir = util.promisify(fs.readdir) let files = await preaddir(path.join(__dirname, target)) // html -> html/aaa -> html/aaa/ccc let lis = ''; for (let i = 0; i < files.length; i++) { let file = files[i]; let stat = await util.promisify(fs.stat)(path.join(__dirname, target, file)) if(stat.isDirectory()){ let p = target + '/' + file lis += `<li><a href="${p}">${file}</a></li>` }else { lis += `<li>${file}</li>` } } res.writeHead(200, {'Content-type': 'text/html;charset=utf-8'}) res.end(makeHtml(lis)) } function makeHtml(lis) { return ` <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>文件浏览器</title> <style> *{padding:0;margin:0} ul{ padding: 15px; } ul>li{ list-style: none; padding: 10px; transition: all 1s; } li:hover{ } li:not(:first-child){ border-top: 1px solid #ccc; } </style> </head> <body> <ul>${lis}</ul> </body> </html> ` }