Node.js从4.0开始,支持ES6的大多数新特性,例如:classes、typed arrays、generators、Promises、Symbols、collections、arrowfunctions、block scoping、template strings等。
数据类型
(1)原始类型(Primitive Type):
string、number、bollean、null、undefined,原始类型数据直接赋值即可。
(2)引用类型(Reference Type):
包含ES原生对象、Node.js对象、用户自定义对象三类;引用类型数据一般需要使用new关键字进行创建。
模板字符串(Template String)
ES6标准中,可以使用"模板"方式定义字符串:
var info=`
用户名:tom
密码:123456
`;
模板字符串中可以使用${}拼接变量,并执行运算:
var price=3.5,count=3;
var info=`
单价:${price}
数里:${count}
小计:${price*count}
`
console.log(info);
变量和常量
(1)声明常量
var empName1='tom';//局部变量
empName2='Mary';//全局变量
注意:省略var的全局变量在严格模式下是非法的
(2)声明常量—ES6新特性
const fs=require('fs');
变量的作用域
ES6中,变量的作用域分为三种:
(1)局部作用域:只能在当前函数内使用
(2)全局作用域:可以在任意函数内使用—是全局对象的成员
(3)块级作用域:只能在当前块内使用—ES6新特性
"use strict";
for(let i=0;i<10;i++){
console.log(i)//正常执行
}
console.log(i)//执行错误
运算符
(1)算术运算 | + - * / % ++ -- |
---|---|
(2)比较运算 | > < >= <= == ==== != !== |
(3)逻辑运算 | && |
(4)位运算 | & |
(5)赋值运算 | += -= *= /= %= |
(6)三目运算 | ? : |
(7)特殊运算 | typeof (判断变量类型) instanceof(判断一个对象是否从属于某种类型) viod(对任何值返回undefined) , . [ ](取下标运算符) => |
逻辑结构
(1)循环结构
for | |
---|---|
for...in... | 常用于遍历对象 |
for...of... | 只能遍历数组,不能遍历对象(ES6新特性) |
while | |
do...while |
(2)选择结构
if...else... | |
---|---|
switch...case... |
for...in和for...of
(1) for...in循环可以遍历数组的下标或对象成员名
var arr=[90,80,70]
for(var i in arr){
console.log(i);//将输出0 1 2
}
(2)ES6新增的for...of循环可以遍历数组的元素值
var arr=[90,80,70];
for(var v of arr){
console.log(v);//将输出90 80 70
}
函数
函数:就是一系列语句的集合,为了实现某种可以重复使用的特定功能。
(1)命名函数:指定了名称的函数对象
function add(num1,num2){
return num1+num2;
}
add(10,20);
(2)匿名函数:未指定名称的函数对象
function(num){
arguments.calle(num-1);//递归调用匿名函数
}
自调函数(匿名函数的自调)
JS中,全局变量会变成全局对象的成员,造成全局对象的污染;很多JS工具和框架都在设计时,为了避免此问题,常常使用匿名函数的自调:
(function(g){
var ns="myNamespace";
})(global);
+function(g){
var ns="myNamespace";
}(global)
匿名函数在使用时必须使用function关键字,在大型的WEB应用中匿名函数会使用的很多很多,所以ES6中为了简化这种匿名函数的用法,引入了箭头函数的概念。
箭头函数
ES6中,为了简化匿名函数的编写,引入了监听函数语法:
setInterval(()=>{
console.log('timer...');
},1000);
箭头函数()是形参列表,function关键字被省略,小括号和大括号中间使用箭头指示符连接;大括号中还是普通函数一样编写函数体。今后在编写Node.js程序中会经常使用箭头函数,Node.js官方给出的例子也几乎都是使用的箭头函数。
fs.readFile('index.html',(err,data)=>{
if(err)throw err;
console.log(data);
});
箭头函数与普通匿名函数的区别:
1、普通匿名函数中使用了this,this指当前函数对象;
2、在箭头函数中this指与箭头函数本身同一级的this;
3、在箭头函数中使用arguments.callee不在指向当前匿名函数。
闭包
闭包(Closure),是ECMAScript中特有的现象。一个闭包,可以看做一种特殊的对象,包含若干函数对象,以及这些函数对象执行时必须依赖的执行上下文对象。
function outer(){
var num=10;
function inner(){
console.log(num)
}
return inner;
}
var fn=outer();
fn();
有一个function,外部函数叫outer,outer有一个形参叫num=10,我们知道num实在outer函数体内,那么他的作用范围也是仅限于outer函数内,离开outer那么num也没有作用了。在outer函数内又定义了一个内部函数叫inner,inner函数需要输出num变量,由于inner内部函数内并没有定义,所以会使用outer内定义的局部变量num。outer函数自身并没有调用inner,而是将inner内部函数return返回了。outer函数被调用,就会有一个返回值指向了inner函数,也就说fn指向了inner函数,inner函数被外部调用。inner函数不能直接被调用,因为inner函数执行必须依赖一个外部函数执行时,他创建的上下文对象所遗留的num变量。每一次调用outer函数,都返回了一个函数对象,以及这个函数对象运行所必须依赖的一个外部函数提供的上下文。(每调用一次outer函数都会产生一个闭包对象)
闭包引起的错误
无论是前端JS还是Node.js,闭包使用不当,都有可能引起看似奇怪的错误:
//假设前端项目有三个button元素
var list=document.querySelectAll("button");
for(var i=0;i<list.length,i++){
list[i].onclick=function(){
console.log(i);
}
}
//点击每个按钮,会分别输出什么?
理想结果:三个按钮,点击第几个按钮就输出数字对应的下标
实际结果:点击每个按钮都会输出数字3
原因?:这段代码中给这三个按钮各自绑定了一个匿名的监听函数,相当于这段代码执行完成后给外界返回了三个函数对象,这三个函数运行都需要依赖于外部产生的执行上下文对象,他里边有且只有一个i(三个函数对象,外加一个执行上下文对象里的i,这是一个典型的闭包)。循环结束变量i停留在3上,最终任何一个按钮被点击时,他们再去查找变量i,变量i已经被固定在3了,所以不会拿到0、1、2这种结果。
使用Node.js,也可以产生类似前端中的闭包错误:
//目标:1s后,先输出0、1、2
for(var i=0;i<3;i++){
setTimeout(()=>{
console.log(i)
},1000)
}
//实际的运行结果呢?
3
3
3
那么我们怎么解决呢?
for (var i = 0; i < 3; i++) {
setTimeout(((num) => {
return () => {
console.log(num)
}
})(i), 1000);
}
对象
ECMAScript中提供了多种创建对象的语法:
(1)对象直接量方式(这种对象不具有反复使用性)
var e1={ename:'tom',work:function(){}}
(2)构造函数方式
function Emp(ename){
this.ename=ename;
this.work=function(){}
}
var e2=new Emp('tom');
这里边指定emp所需要的成员属性和成员方法,我们在需要emp类型对象时,可以直接new一个emp对象。
(3)原型继承方法
var parent=new Object();
var child=Object.create(parent);
先创建一个parent对象,再使用Object.creaet()方法创建一个parent对象的子对象。
(4)class方式—ES6新特性
ES6借鉴了其他编程语言声明对象的方式,正式启用了class关键字,有了"类"的概念。
class:类,是一组相似对象的属性和行为的抽象集合。
instance:实例,是从属于某个类的一个具体对象。
//class关键字必须用严格模式
"use strict"
//声明一个类
class Emp{
//构造方法
constructor(ename){
this.ename=ename;
}
//实例方法
work(){
console.log(`ENAME:${this.ename}`);
}
}
//创建类的实例
var e3=new Emp('tom');
e3.work();
对象的继承
继承,是面向对象编程的一个重要概念,使子对象可以继承父对象中声明的成员,从而极大的提高代码的复用性。
ES6之前的继承都是通过对象的原型(prototype)来实现的:
var graphic={bgColor:'red',borderColor:'blank'}
var rect={width:500,height:300}
Object.setPrototypeOf(rect,graphic);//原型继承
console.log(rect.width);
console.log(rect.hight);
console.log(rect.bgColor);//继承来的成员
console.log(rect.borderColor);//继承来的成员
ES6中的继承通过使用class配合extentds关键字来实现。
//声明父类
class Emp{
constructor(ename){
this.ename=ename;
}
work(){
return `ENAME:${this.ename}`;
}
}
//声明子类
class Programmer extends Emp{
constructor(ename,skills){
//调用父类构造方法
super(ename);
this.skills=skills;
}
work(){
return super.work()+`SKILLS:${this.skills}`;
}
}
var p1=new Programmer('tom','js');
console.log(p1.work());
对象的分类
(1)ECMAScript预定义对象
String、Boolean、Number、Date、Array、Math、RegExp、function、Object、Error、EvalError、RangeError、ReferenceError、SyntaxError、TypeError、UriError等16个对象;
(2)Node.js核心模块定义的对象
Bufefer、WriteStream、Socket、ChildProcess等数百个;
(3)等三方模块定义的对象
(4)用户自定义的对象
注意:Node.js中不支持BOM和DOM对象,如:window、document等