使⽤ const 来定义⼀个常量,常量也就是不能被修改,不能被重新赋值的变量。
使⽤ let 来定义⼀个变量,⽽不要再使⽤ var 了,因为 var 有很多坑;可以认为 let 就是修 复了bug的 var 。⽐如,var允许重复声明变量⽽且不报错;var的作⽤域让⼈感觉疑惑。最佳实践:优先⽤ const ,如果变量需要被修改才⽤ let ;要理解⽬前很多早期写的项⽬中仍 然是⽤ var 。var i = 10;
console.log("var :", i);
var i = 100;
console.log("var :", i);
function test() {
var m = 10;
console.log("test m :", m);
}
test(); //console.log("test outside :", m);
let j = "hello"
console.log("j :", j);
j = "HELLO"
console.log("j :", j);
const k = [1, 2, 3, 4];
console.log("k0 :", k);
k[0] = 100;
console.log("k1 :", k);
2.解构赋值
ES6 允许我们按照⼀定模式,从数组和对象中提取值,对变量进⾏赋值,这被称为解构 (Destructuring)
数组的解构赋值const arr = [1, 2, 3] //我们得到了⼀个数组
let [a, b, c] = arr //可以这样同时定义变量和赋值
console.log(a, b, c); // 1 2 3
对象的解构赋值(常⽤)
const obj = { name: '俊哥',address:'深圳', age: '100'} //我们得到了⼀个对象
let {name, age} = obj //可以这样定义变量并赋值
console.log(name, age); //俊哥 100
函数参数的解构赋值(常⽤)
const person = {name: '⼩明', age: 11}
function printPerson({name, age}) { // 函数参数可以解构⼀个对象
console.log(`姓名:${name} 年龄:${age}`);
}
printPerson(person) // 姓名:⼩明 年龄:11
3. 函数扩展
ES6对函数增加了很多实⽤的扩展功能。
参数默认值,从ES6开始,我们可以为⼀个函数的参数设置默认值, go语⾔有默认值吗? 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
}
}
4. Class继承
由于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() // 我重写了⽗类的⽅法!
5. 理解NodeJS的事件驱动和异步IO
NodeJS在⽤户代码层,只启动⼀个线程来运⾏⽤户的代码==(go语⾔?)==。每当遇到耗时的 IO(⽂件IO,⽹络IO,数据库IO) *** 作,⽐如⽂件读写,⽹络请求,则将耗时 *** 作丢给底层的事件循 环去执⾏,⽽⾃⼰则不会等待,继 续执⾏下⾯的代码。当底层的事件循环执⾏完耗时IO时,会执⾏我 们的回调函数来作为通知。(写demo)
同步就是你去银⾏排队办业务,排队的时候啥也不能⼲(阻塞);异步就是你去银⾏⽤取号机取了⼀个 号,此时你可以⾃由的做其他事情,到你的时候会⽤⼤喇叭对你进⾏事件通知。⽽银⾏系统相当于底 层的事件循环,不断的处理耗时的业务(IO)。
function foo1(name, age, callback) { }
function foo2(value, callback1, callback2) { }
同步调用(阻塞)
请事先在当前⽬录下准备⽂件"input.txt",写⼊任意数据。
var fs = require("fs");
data = fs.readFileSync('input.txt');
console.log(data.toString());
console.log("程序执⾏结束!");
异步调用(非阻塞)
var fs = require("fs");
fs.readFile('input.txt', function (err, data) {
if (err) return console.error(err);
console.log(data.toString());
});
console.log("程序执⾏结束!");
7.模块系统
为了让Node.js的⽂件可以相互调⽤,Node.js提供了⼀个简单的模块系统 ⼀个⽂件就是⼀个模块,使⽤export关键字实现
//hello.js
let Hello = () => {
console.log("hello world!")
}
module.exports = Hello;
//main.js var Hello = require('./hello');
Hello();
8. path模块
path模块供了⼀些⼯具函数,⽤于处理⽂件与⽬录的路径path.basename :返回⼀个路径的最后⼀部分path.dirname :返回⼀个路径的⽬录名path.extname :返回⼀个路径的扩展名 path.join :⽤于拼接给定的路径⽚段path.normalize :将⼀个路径正常化path.resolve([from …], to)
var path = require("path");
// 格式化路径
console.log('normalization : ' + path.normalize('/test/test1//2slashes/1slash/tab/. .'));
// 连接路径
console.log('joint path : ' + path.join('/test', 'test1', '2slashes/1slash', 'tab', '..'));
// 转换为绝对路径
console.log('resolve : ' + path.resolve('main.js'));
// 路径中⽂件的后缀名
console.log('ext name : ' + path.extname('main.js'));
9. fs模块
⽂件 *** 作相关的模块
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 :监视⽂件的变化
let fs = require('fs');
var filename = './testfile.txt';
//同步读取
var data = fs.readFileSync(filename);
console.log(data.toString());
//异步读取
fs.readFile(filename, function (err, data) {
console.log("========= read done!");
console.log(data.toString())
});
console.log("111111111111");
//打开⽂件
fs.open(filename, 'r+', function (err, fd) {
if (err) {
return console.err("open failed!");
}
console.log("open ok!");
})
fs.stat(filename, function (err, stat) {
console.log("is file :" + stat.isFile());
console.log("is dir :" + stat.isDirectory());
})
var output = './output.txt';
//同步写⽂件
var data = "你好"
var res = fs.writeFileSync(output, data)
console.log("res :", res); //undefine
//异步写文件
fs.writeFile(output, data, function (err) {
if (err) {
//console.log("")
}
console.log("writeFile write ok!!!")
})
fs.appendFile(output, data, function (err) {
if (err) {
//console.log("")
}
console.log("appendFile ok!!!")
})
}
10. Promise和asnyc/await
虽然异步和回调的编程⽅式能充分利⽤CPU,但是当代码逻辑变的越来越复杂后,新的问题出现了。 请尝试⽤异步的⽅式编写以下逻辑代码:
先判断⼀个⽂件是⽂件还是⽬录,如果是⽬录就读取这个⽬录下的⽂件,找出结尾是txt的⽂件, 然后获取它的⽂件⼤⼩。
恭喜你,当你完成上⾯的任务时,你已经进⼊了终极关卡:Callback hell回调地狱!
为了解决Callback hell的问题, Promise 和 async/await 诞⽣。
语法注意:Promise内部的resolve和reject⽅法只能调⽤⼀次,调⽤了这个就不能再调⽤了那个; 如果调⽤,则⽆效。
// 创建promise对象
let readFilePromise = new Promise(function (resolve, reject)=>{
// 在异步 *** 作成功的情况选调⽤resolve,失败的时候调⽤reject
fs.readFile('xxx.txt', (err, data) => {
if (err) {
reject(err)
} else {
resolve(data.toString())
}
})
});
// 使⽤promise
readFilePromise.then((text) => {
//then⾥⾯就是我们传给Promise的resolve
// then⽅法是当Promise内部调⽤了resolve的时候执⾏
//then有两个参数,then(onResolve, onReject),⼀般写第⼀个即可,第⼆个通过catch来处理 //
}).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 readFilePromise
// 当你⽤promise包装了所有的异步回调代码后,就可以⼀直await,真正意义实现了以同步 的⽅式写异步代码
console.log('异步道明执⾏');
} catch (e) {
// 捕获到promise的catch⽅法的异常
console.log(e);
}
}
asyncDemo()
console.log('我是同步代码');
⼩任务 使⽤promise和async/await来重写上⾯的逻辑代码,来感受⼀下强⼤的⼒量吧!。 异步代码的终极写法: 1. 先使⽤ promise 包装异步回调代码,可使⽤node提供的 util.promisify ⽅法; 2. 使⽤ async/await 编写异步代码。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)