NodeJS基础
NodeJs代码就是把JS代码运行在了本地的电脑上面,它使用谷歌的V8引擎去执行代码,不用考虑代码的兼容性,它完全支持ES5以后的版本(ES5,ES6,ES7)
NodeJS与普通的JS还是有一点的区别
- NodeJS在这里它不包含DOM与BOM,只有ECMAScript
- NodeJS它的全局对象不再是window了,而一个global对象
- NodeJs的引入关系不再是浏览器页面里面的
<script>
,而是另一套导入机制叫CommonJs
ES6新特性
ES6是2015发布的标准,所以有时候也叫ECMAScript 2015
变量的定义let与const
let也是用于定义一个变量,但是与var还是有区别的,主要在以下方面
-
var定义以后默认值是undefined,可以在调用代码之后去定义
console.log(a); var a=123;
这个时候打印的结果是undefined
但是如果使用let来定义,则会报错
console.log(a); let a=123;
这个时候打印就会报错,提示a没有被定义
let定义的变量不存在声明提前(let定义的变量不能在定义之前调用)
-
let定义的变量它有区域,它的区域性是根据花括号来决定的
{ var a=123; let b=456; //变量b只在当前的花括号里面才能够使用 } console.log(a); //正常 console.log(b); //报错 b is not defined
const定义的标识符,我们叫常量,它只能够进行第一次赋值,不能更改
const a=123; //定义了一个常量a 定义以后不可改变
console.log(a);
a=456; //它会报错,提示不能更改;
因为const定义的标识符不可以更改,所以在定义的时候,一定要给一个初始值,不能够直接
const a;
const定义的变量与let的顺序是一样的,一定要先定义再调用,同时它也有区域性,根据花括号来确定它的区域性
变量的解构赋值
体现在数组当中
let与const与var都是用于定义变量了,它们除了在ES6当中有了区域性与顺序的区别以后,定义方式还有一定的改进
解构赋值可以进行一次批量的赋值
let a=1,b=2;c=3; //普通赋值方式
let [a,b,c]=[1,2,3]; //解构赋值方式 a为1,b为2,c为3
let [a,b,c]=[1,2]; //a为1,b为2,c为undefined
🔥注意事项:解构赋值只与赋值方式有关,与前面的let是没有任何关系,你可以使用let,还可以使用var与const
var arr=[1,2,3,4,5,6,7];
var [a,b,c,d]=arr;
//上面的代码其实就是下面的代码的写法
var [a,b,c,d]=[1,2,3,4];
console.log(a);
console.log(b);
console.log(c);
console.log(d);
体现在对象当中
let obj={
name:"张三",
sex:"男",
age:18
}
//如果按照以前的方式,我们要进行属性的取值
/*
let name=obj.name;
let sex=obj.sex;
let age=obj.age;
*/
let {name,sex,age}=obj;
//在对象解构赋值的时候,要定要注意,这个地方的变量名一定要与属性名一样
console.log(name);
console.log(sex);
console.log(age);
函数的扩展
ES6对ES5当中的函数做了很多的扩展,其中主要体现在1.定义方式,2.this指向,3.参数传值
箭头函数
箭头函数是ES6当中的匿名函数的一种简写方式
无参数的箭头函数
/*
setInterval(function(){
console.log(new Date().toLocaleString());
},1000);
*/
//新的定法
setInterval(()=>{
console.log(new Date().toLocaleString());
},1000);
上面的箭头函数是没有参数的箭头函数,如果有参数呢?
有参数的箭头函数
//有参数怎么处理
/*
setInterval(function(x){
console.log(x);
console.log(new Date().toLocaleString());
},1000,"张三");
*/
//有参数的箭头函数
//如果参数是多个,则我们需要用小括号扩起来 (x,y)={}
//如果参数只有一个,那么,这个小括号可以省略 x=>{}
//没有参数也不能省()=>{}
setInterval(x=>{
console.log(x);
console.log(new Date().toLocaleString());
},1000,"张三");
注意参数的情况
关于this的指向性问题
在回调方法里面,回调方法中的this指不到当前对象
/* 回调函数里面的this不指向当前对象
var s_name="张三";
var obj={
s_name:"李四",
sayHello:function(){
var that=this;
setTimeout(function(){
console.log(that.s_name); //打印结果是什么 肯定不可能是李四
},1000);
}
}
obj.sayHello();
*/
var s_name="张三";
var obj={
s_name:"李四",
sayHello:function(){
setTimeout(()=>{
console.log(this.s_name); //打印结果是什么 李四
},1000);
}
}
obj.sayHello();
箭头函数会跳过当前对象,去取外边的this(多半使用在回调函数里面)
正是因为这一种情况,所以 在定义对象的时候,对象里面不能够使用箭头函数,如下所示
var obj={
s_name:"张三",
sayHello:()=>{
console.log(this.s_name); //它拿不到当前对象的s_name属性 打印undefined
}
}
分析下面的代码
/**
* 分析判断
*/
this.s_name="张三";
let obj={
s_name:"李四",
sayHello:()=>{
setTimeout(() => {
console.log(this.s_name);
}, 1000);
}
}
obj.sayHello(); //打印张三
代码分析:setTimeout里面的箭头函数第一次会跳到sayHello里面去,跳到sayHello里面去了以后 这个方法又是一个箭头函数继续向外边跳,这个时候就会跳到最外层去,所以打印的结果是"张三"
函数参数默认值
函数在定义的时候可以定义参数,这个时候的参数是形参,在ES5里面,我们不能够去直接给形参一个默认的值
//在ES5里面定义函数参数的默认值
/*
function abc(x,y){
x=x||"abc";
y=y||"def";
console.log(x);
console.log(y);
}
abc("李四","张三");
*/
//在ES6里面定义函数的参数默认值
function abc(x="abc",y="def"){
console.log(x);
console.log(y);
}
abc();
abc("李四","张三");
this指向问题
在函数里面,普通函数当中的this应该是指向全局对象的,但是我们可以通call与apply的方式去更改this的指向,如下代码所示
function Person(x,y){
console.log(this);
console.log(x);
console.log(y);
}
var obj={
s_name:"张三"
}
//Person.call(obj,"a","b");
//Person.apply(obj,["a","b"]);
var aaa = Person.bind(obj,"a","b"); //这个地方的aaa就是一个新的方法名
//call与apply把指向性的this传递进去了以后,它会立即调用这个函数
//bind仅仅只会把this指向和参数传进去 它会返回一个新的方法名
aaa();
总结:bind与call和apply都可以改变方法中this的指向性,然后call与apply是直接调用了这个方法,而bind会返回一个新的方法名,然后你再通过这个新的方法名去调用
bind的参数传递与call是一样的
数组的扩展
-
Array.fill方法
var a=new Array(10); /* 在ES5里面,对每个元素去赋值 for(var i=0;i<a.length;i++){ a[i]="张三"; } */ a.fill("张三",2,5); a.fill("张三",2,-1); //2代表开始索引 //5代表结束索引的前一个 //-1代表倒数第1个 console.log(a);
-
Array.from方法
将类数组转换成真正的数组
它与我们之前学过的
Array.prototype.slice.call()
的用法效果一样,但是Array.from
有兼容性,在浏览器里面慎用,在NodeJs里面,可以随便使用 -
Array.of方法
该方法相当于去定义一个数组
var arr=new Array(10); //定义了一个长度为10的数组 var arr1=new Array("a"); //定义了一个数组,里面的第一个元素为"a" //原始的定义方式会存在一定的歧义,当前new Array在这里接收一个参数的时候,它有可能是长度,也有可能是元素 //这一种方式不是非常严谨,在ES6里面就会推荐其它的方式 //第一种方式:使用最开始的[] //第二种方式:Array.of() var arr2=Array.of(10); //var arr2=[10]; var arr3=Array.of("a",10,"11","b",true); // var arr3=["a",10,"11","b",true] console.log(arr3);
-
Array.find()方法
该访求是在指定数组当中去查找符合条件的元素,如果找到就返回这个元素,如果没找到就返回undefined
//这是ES5里面去找元素的方法(它只能找指定的元素,不能找满足条件的元素) /* var arr=[1,4,7,9,3,2,8]; var index=arr.indexOf(9); console.log(index); */ var arr=[1,4,7,9,3,2,8,11]; //能不能在上面的数组当中,去找一个值,这个值大于7 var a = arr.find((item,index,a)=>{ return item>7; }); console.log(a); //这个方法与map和filter都差不多,都可以 返回值 ,要注意,map与filter是返回满足条件的所有的值 //而find只返回第一个
-
Array.findIndex()方法
该方法与find方法一样,它返回的是符合条件的元素的索引位置
var arr=[1,4,7,9,3,2,8,11]; //能不能在上面的数组当中,去找一个值,这个值大于7 var a = arr.findIndex((item,index,a)=>{ return item>7; }); console.log(a); //得到的结果就是3
Set与Map对象
set与map也是一个类数组,它们同样具备一些数组的特性,但不具备数组的方法
Set对象(单值容器)
set对象也是存放一些数据的集合,但是它不能存相同的数据
Set的创建方式
var s=new Set();
var s1=new Set([1,2,3,4,5]); //在创建的时候,直接向里面赋值
使用Set去除数组重复元素案例
let arr=[1,3,4,6,8,2,6,7,9,9,0,2,4,1,3];
//现在上面有一个数组,请将数组当中重复的元素去掉
//ES5当中,怎么样去除数组重复元素
/*
var newArr=[];
for(var i in arr){
if(arr.indexOf(arr[i])==i){
newArr.push(arr[i]);
}
}
console.log(newArr);
*/
//在ES6当中,数组去重就非常方便了
var s=new Set(arr);
//现在只需要把s转换成数组
var newArr = Array.from(s);
console.log(newArr);
- add()方法向Set里面去添加元素(它不能够添加相同的元素)
- delete()方法向Set里面去移除元素
- has()方法判断Set里面是否存在这个元素,如果存在返回true,否则返回false
- size属性获取当前Set里面有元素个数
Set对象的遍历
-
通过values的迭代器遍历
var s=new Set(["a","b"]); var abc = s.values(); //这个方法有个返回值 ,是一个叫Iterabler的类型 Iterabler:迭代器(就是可以遍历的东西) let v; while((v=abc.next()).value!=undefined){ console.log(v.value); }
-
直接通过
forEach
进行遍历var s=new Set(["a","b"]); s.forEach(function(item){ console.log(item); //这个时候的forEach遍历,它没有索引 //Set里面不存在索引 });
Map键值对容器
Map与Set非常相像,Set是存放不重复的值,但是只存放一个,而Map不仅可以存放值(Value),还可以存放键(Key)
创建方式
var m=new Map();
m.set("key","value"); //添加的时候,一定是键值对
//注意,键不可以重复,值是可以重复的
应用场景
/**
* Map的应用场景
*/
function Student(s_id,s_name,s_sex,s_birthday,s_nation,c_id,s_addr){
this.s_id=s_id;
this.s_name=s_name;
this.s_sex=s_sex;
this.s_birthday=s_birthday;
this.s_nation=s_nation;
this.c_id=c_id;
this.s_addr=s_addr;
}
//在以前没有键值对的时候,我们怎么保存
/*
var stuArr=[
new Student("20200101","张一一","女","1967-1-12","汉族","20200101","河南郑州"),
new Student("20200202","李二二","男","1995-5-15","汉族","20200202","河南洛阳"),
new Student("20200303","罗三三","男","1995-10-15","汉族","20200303","河南安阳"),
new Student("20200404","何四四","女","1998-4-25","汉族","20200404","江西南昌")
]
*/
//怎么样在里面快速的找出学号为 20200202
//ES5怎么找????
/*
var stu = stuArr.filter((item,index,a)=>{
return item.s_id=="20200202";
});
console.log(stu);
*/
//ES6怎么找
/*
var stu=stuArr.find((item,index,a)=>{
return item.s_id=="20200202";
});
console.log(stu);
*/
var m=new Map();
m.set("20200101",new Student("20200101","张一一","女","1967-1-12","汉族","20200101","河南郑州"));
m.set("2005010101",new Student("20200202","李二二","男","1995-5-15","汉族","20200202","河南洛阳"));
m.set("2005010202",new Student("20200303","罗三三","男","1995-10-15","汉族","20200303","河南安阳"));
m.set("2005010102",new Student("20200404","何四四","女","1998-4-25","汉族","20200404","江西南昌"));
var stu = m.get("20200202").s_name;
console.log(stu);
//map的取值不受顺序的影响,只是根据key来取value的值,这个地方的value可以是任何类型
map的常用方法
- set(“key”,“value”)添加键值对
- get(“key”)根据键返回值,如果没有找到返回undefined
- size描述当前容器中键值对的个数
- delete(“key”)根据一个键删除一个记录
- has(“key”)判断某一个键是否存在,如果存在则返回true,否则返回false
- clear()清除里面所有的元素
迭代方法
-
keys()迭代,返回一个Iterable的对象,它是一个迭代对象【返回键】
let m=new Map(); m.set("NA001","陈五五"); m.set("NA002","尚六六"); m.set("NA003","艾七七"); let m_keys= m.keys(); let k; while((k=m_keys.next()).value!=undefined){ console.log(k.value); }
-
values()迭代,返回一个Iterable的对象,它是一个迭代对象【返回值】
let m_values = m.values(); let v; while((v=m_values.next()).value!=undefined){ console.log(v.value); }
-
entries()迭代,返回一个Iterable的对象,它是一个迭代对象【返回键与值】
var m_entries = m.entries(); var first = m_entries.next(); console.log(first.value); // [‘NA001’,‘陈五五’]
-
forEach的方法直接去遍历对象
//直接调用forEach的方式去遍历 m.forEach((value,key,a)=>{ console.log(key+"-------"+value); //第三个参数a代表的是m });
迭代器(Iterable)的遍历新方法
在ES里面,有一些内置的集合都是有迭代器,Array,NodeList,Set,Map,arguments,其它的类数组这些都有迭代器,但是HTMLCollection
它没有迭代器
只要是具备了迭代器的特性的,都可以使用新方法去遍历 for...of...
for…in…与for…of…的区别
for…in…去遍历的时候,它是需要索引的,如果是对象,则没有必要要索引
for…of…即例没有索引,也可以进行遍历(Map,Set这像种集合它就没有索引)
var arr=["a","b","c","d","e","f"];
//ES5里面的方法
/*
for(var i in arr){
console.log(arr[i]);
}
*/
//ES6里面呢
for(var item of arr){
console.log(item);
}
重点:通过for…of…遍历出来的是每一个元素的值
下面的代码是一个反例
var obj={
s_name:"何八八",
s_sex:"男",
s_age:20
}
/*
for(var i in obj){
console.log(i); //属性名
console.log(obj[i]); //改性值
}
*/
//对外obj能否使用for...of...
for(var item of obj){
console.log(item); //报错
}
说明:obj它只是一个普通的对象,它不是一个迭代器(Array,NodeList,Set,Map,arguments,其它的类数组这些都有迭代器),所以它不能够使用for…of…
使用for…of…去遍历Map与Set
遍历Set
let s=new Set(["a","b","c","d"]);
for(let item of s){
console.log(item);
}
遍历Map
let m=new Map();
m.set("NA001","陈五五");
m.set(("NA002","尚六六");
m.set("NA003","艾七七");
for(let item of m){
//item就是遍历出来的每一个键值对,一个元素是键,第二个元素是值
console.log(item[0]+"------"+item[1]);
}
ES6中的展开运算符
ES6里面添加了一个新的运算符叫展开运算符使用
...
来表示,它可以展开我们具备迭代器特性的数组或类数组
展开运算符的使用
//var arr=["a","b","c","d","e"];
// console.log("a","b","c","d","e");
//...arr "a" "b" "c" "d" "e"
//console.log(...arr);
function abc(x,y,z){
console.log(x+y+z);
}
//abc(1,5,8);
var arr=[3,6,9,7];
abc(...arr); //18
var arr1=[4,7,9,3,2,6];
// var max=Math.max(4,7,9,3,2,6); //得到结果9
var max=Math.max(...arr1); //得到结果9
console.log(max);
ES6中对象的扩展
-
定义方法的时候发生了些变化
/* let obj={ s_name:"张三", s_sex:"男", s_age:18, sayHello:function(){ console.log("hello world"); } } */ let obj={ s_name:"张三", s_sex:"男", s_age:18, sayHello(){ //直接写一个方法名 //切记在这里,不要使用箭头函数 console.log(this.s_name,this.s_age,this.s_sex); } } obj.sayHello();
-
ES6的对象里面,添加了一种get/set的属性访问器方法
属性访问器不是ES6独有的东西,在以前的ES5里面也有,只是现在在ES6当中进行了一个简便化的操作,对比如下
ES5当中的属性访问器
/** * 属性访问器ES5的写法 */ var stu={ firstName:"张", lastName:"三" }; Object.defineProperty(stu,"userName",{ get:function(){ return this.firstName+this.lastName; }, set:function(v){ this.firstName=v[0]; this.lastName=v.substr(1); }, configurable:false, //不能使用delete删除 enumerable:true //可以遍历出来 }); stu.userName="示例一"; console.log(stu.userName);
ES6当中的属性访问器
/** * ES6的属性访问器方法 */ /** 定义一个学生对象 * 学生对象里面包含 “姓”,还包含一个“名” */ var stu={ firstName:"张", //描述它的姓 lastName:"三", //去描述它的名 //现在在这里,我们定义了一个方法 get UserName(){ //取值的时候的访问器 return this.firstName+this.lastName; }, set UserName(v){ //赋值的时候的访问器,这个的赋的值就是参数v this.firstName=v[0]; this.lastName=v.substr(1); } } //var str=stu.getUserName(); var str = stu.UserName; //取值 console.log(str); // stu.setUserName("示例一"); stu.UserName="示例二"; //它能不能去赋值 console.log(stu.UserName);
-
ES6当中对象封装的简便化(解构封装)
在ES6里面,我们可以通过解构,快速的将一个数组里面的元素拿出来赋值,也可以快速的将对象里面的属性的属性值拿出来
正因为这种操作方式非常方便,在封装对象的时候,我们可以反向的利用这种思维
/* let [a,b,c]=[1,2,3]; let obj={ s_name:"张三", s_age:18, s_sex:"男" } let {s_name,s_sex,s_age}=obj; //正常的解构赋值 */ let s_name="张三"; let s_age=18; let s_sex="男"; //ES6当中反向的利用解构进行封装 let obj={s_name,s_sex,s_age}; //如果在ES5里面 let obj={ s_name:s_name, //前面的代表属性名,后面的代表属性值 s_sex:s_sex, s_age:s_age } console.log(obj);
ES6中对象的创建(Class)
在之前的ES5里面,我们学过很多种方式去创建对象
- 使用
{}
直接创建 - 使用new Object()创建
- 使用构造函数创建
现在在ES6里面,ES6推荐使用新的关键字class
进行定义对象
/**
* ES5与ES6对象创建的区别点
*/
//ES5使用原来的function来创建构造函数
/*
function Person(name,sex){
this.name=name;
this.sex=sex;
this.sayHello=function(){
// console.log("大家好,我是"+this.name+",我的性别是:"+this.sex);
console.log(`大家好,我是${this.name},我的性别是${this.sex}`);
}
}
let p=new Person("张三","男");
p.sayHello();
*/
//使用ES6的方式创建
//强制规定:class 后面的名称,首字母一律大写 如Person,Student,StudentInfo,ScoreInfo
class Person{
constructor(name,sex){
//它是一个构造函数,在new的一瞬间会自己执行,如果你不写,系统会自动添加一个
this.name=name;
this.sex=sex;
}
sayHello(){
console.log(`大家好,我是${this.name},我的性别是${this.sex}`);
}
}
let person=new Person("帅哥","男");
person.sayHello();
对象里面的静态方法
静态方法指的是通过static来修饰的方法
/**
* Class里的静态方法
*/
class Person{
constructor(firstName,lastName){
this.firstName=firstName;
this.lastName=lastName;
}
//get访问器与set访问器在class里面一样适用
get UserName(){
return this.firstName+this.lastName;
}
//普通方法
sayHello(){
console.log(`大家好,我的姓名是${this.firstName}${this.lastName}`);
}
//静态方法
static abc(){
console.log("我是一个abc的方法");
console.log(this.firstName); //undefined 静态方法的this不指向当前对象
}
}
let p=new Person("张","帅哥");
p.sayHello();
//sayHello是一个普通的方法,我如果要调用这个方法,必须先new出一个对象 ,然后再用这个对象去调用
//p.abc(); //报错
Person.abc();
//abc是一个static的静态方法,它不需要我们去new对象就可以调用,直接通过类名.方法名()调用
//静态方法里面的this不指向当前对象
上在的abc就是一个静态方法
- 静态方法直接通过类名叫用,不需要new出一个对象
- 静态方法的this默认不指向当前对象
总结:
- class当中有一个构造函数constructor,它在new的一瞬间自己执行
- class里面可以通过get与set来设置访问器属性
- class里面可以添加静态方法
ES6中Class对象的继承
在ES6的class对象里面,如果要实现对象的继承,需要使用一个新的关键字叫extends
无参数的继承
/**
* ES6的对象继承
* 1.无参数的继承
* 2.有参数的继承
*/
//默认情况之下,我们所有的class都会有一个构造函数,只是我们看不到
//人的对象
class Person{
constructor(){
console.log("我是Person对象");
}
sayHello(){
console.log("我是人");
}
}
//学生的对象
//要让当前的学生Student继承上在的人Person
class Student extends Person{
constructor(){
//构造一个学生出来
//但是在构造学生的时候,它必须先是一个人
//先构造一个人出来
console.log("我是Student对象");
super();
}
study(){
console.log("我在学习");
}
}
let s=new Student(); //打印Student是必然的
//当我们去new一个子类的时候,子类的构造函数会默认先优调用父类的构造函数
//现实生活当中,如果你存在了,说明 你爸爸肯定存在过
//正常情况,是先有你爸爸,再有你
有参数的继承
/**
* ES6的对象继承
* 1.无参数的继承
* 2.有参数的继承
*/
//人
class Person{
constructor(name,sex,age){
this.name=name;
this.sex=sex;
this.age=age;
}
}
//学生
//学生也想实现继承关系,并实现传值到父级对象
class Student extends Person{
constructor(s_id,name,sex,age){
super(name,sex,age);
this.s_id=s_id; //构造函数里,如果使用了this,this一定要放在super后面
// super(...Array.from(arguments).slice(1)); //优化方式
}
}
let s=new Student("H18020001","张三","男",19);
console.log(s.name,s.age,s.sex,s.s_id);
子类方法的重写(override)
当子类的方法与父类的方法相同的时候,对象优先调用子类的方法,依据这个原理,我们可以实现方法的重写
/**
* ES6的对象继承,实现方法重写
*/
//人
class Person{
constructor(name,sex,age){
this.name=name;
this.sex=sex;
this.age=age;
}
sayHello(){
console.log(`大家好,我是${this.name},我是父类的Person的方法`);
}
}
//学生
//学生也想实现继承关系,并实现传值到父级对象
class Student extends Person{
constructor(s_id,name,sex,age){
super(name,sex,age);
this.s_id=s_id; //构造函数里,如果使用了this,this一定要放在super后面
// super(...Array.from(arguments).slice(1)); //优化方式
this.name="李四";
}
sayHello(){
//写上一个同名的方法
console.log(`大家好,我是${this.name},我是子类Student里面的方法`);
//在调用子类的方法的时候,还可以继承调用父类的方法
super.sayHello();
}
}
let s=new Student("NA004","张三","男",20);
s.sayHello(); //子类是可以调到父类里面的方法的
说明: 上面的代码当中,我们会看到子类与父类都使用了一个sayHello的方法,同时在子类的sayHello里面,又通过
super
关键字实现父类同名方法的调用
上面的写法是ES6的新语法,如果使用ES5的语法去写,则如下所示
/**
* ES6的对象继承,实现方法重写
*/
//人
class Person{
constructor(name,sex,age){
this.name=name;
this.sex=sex;
this.age=age;
}
sayHello(){
console.log(`大家好,我是${this.name},我是父类的Person的方法`);
}
static abc(){
console.log("我是Person的静态方法ABC"); //子类能否去调用到这个静态方法
}
}
//学生
//学生也想实现继承关系,并实现传值到父级对象
class Student extends Person{
constructor(s_id,name,sex,age){
super(name,sex,age);
this.s_id=s_id; //构造函数里,如果使用了this,this一定要放在super后面
// super(...Array.from(arguments).slice(1)); //优化方式
this.name="李四";
}
sayHello(){
//写上一个同名的方法
console.log(`大家好,我是${this.name},我是子类Student里面的方法`);
//在调用子类的方法的时候,还可以继承调用父类的方法
super.sayHello();
}
static abc(){
console.log("Student里面的ABC");
//再去调父级元素的abc呢
// super.abc(); ====> Person.abc(); //不要把它跟其它的编程语言弄混了
}
}
let s=new Student("NA005","张三","男",20);
//s.sayHello(); //子类是可以调到父类里面的方法的
//Person.abc(); //没毛病
Student.abc(); //也可以
上面的打印结果是"张三"
🔥 注意事项:静态方法也可以实现继承,也可以实现重写,也可以在重写的时候调用super关键字
总结:class里面,到底多了些什么东西
- 继承方式改用extends
- 创建方式改用class关键字
- 多了一个static静态方法与方法重写
- 多了一个构造函数
- 多了一个super关键字,这个关键字指向父级对象
Reflect反射
可以暂时先把反射理解成对象的另一种操作方式
var obj={
name:"张三"
}
-
Reflect.get()获取对象的属性
Reflect.get(obj,"name"); //返回 obj 对象中 name 的属性值
-
Reflect.set()设置对象的属性值
Reflect.set(obj,"sex","男"); //设置 obj 对象中的sex属性值为 男 Reflect.set(obj,"sayHello",function(){ console.log("我是ABC方法,匿名"); console.log(this.name); });
-
定义特殊的对象属性defineProperty()
Reflect.defineProperty(obj,"sex",{ value:"男", configurable:false, enumerable:true, writable:true });
-
deleteProperty()删除某一个属性
//delete obj.name; //删除一个属性 Reflect.deleteProperty(obj,"name");
-
has()方法判断某个属性是否存在
Reflect.has(s,"name"); //判断s对象里面是否有name这个属性,不管是当前对象还是其父级对象
-
setPrototypeOf()为某一个对象设置它的原型对象
-
getPrototypeOf()获取某一个对象的原型对象
-
apply()方法
//父级对象 let person={ name:"张三", sex:"男", sayHello(){ console.log(this.name); } }; //子级对象 let stu={ s_id:"NA006", name:"我是StuName", sayHello(){ console.log(this.name); } } person.sayHello.apply(stu); Reflect.apply(person.sayHello,stu,[]);
可以把这个方法就看成是以前我们学习的call和apply方法
-
construct()方法
执行某一个构造方法
//ES5 构造方法 /* function Student(name,sex){ this.name=name; this.sex=sex; } */ //ES6的语法 class Student{ constructor(name,sex){ this.name=name; this.sex=sex; } } // let s=new Student("张三","男"); 这个时候会得到s对象 let s = Reflect.construct(Student,["张三","男"]); console.log(s);
-
ownKeys()方法
获取当前对象的属性,它与
Object.keys()
和Object.getOwnPropertyNames()
方法一样
思考:
Reflect.has()
是否可以获取到enumerable:false
的属性 //true可以Reflect.ownKeys()
是否可以获取到enumerable:false
的属性- 对比Object里面的方法和Reflect里面的方法
总结:
Reflect.has(),Reflect.ownKeys(),Object.hasOwnProperty(),Object.keys(),Object.getPropertyNames(),关键字in
等的要同点和不同点
那些是获取当前对象属性的,那些是获取父级对象属性的,那些是判断不可遍历属性的
let obj={
name:"张三"
}
let stu={
s_id:"123"
}
Reflect.setPrototypeOf(stu,obj);
Reflect.defineProperty(stu,"sex",{
value:"女",
enumerable:false
});
console.log(`enumerable:false的属性通过Reflect.has(stu,"sex"):${Reflect.has(stu,"sex")}`);
console.log(`父级属性通过子级的去获取Reflect.has(stu,"name"):${Reflect.has(obj,"name")}`);
console.log(`enumerable:false的属性通过Reflect.ownKeys(stu):${Reflect.ownKeys(stu)}`);
console.log("------------------");
//for...in可以获取父级对象的属性名,但不能获取enumerable:false的属性名
for(let i in stu){
console.log(i);
}
console.log("-------------------")
console.log(`父级属性判断"name" in stu:${"name" in stu}`);
console.log(`enumerable:false属性判断"sex" in stu:${"sex" in stu}`);
console.log(`只能获取当前对象属性,并且不能获取enumrable:false。Object.keys(stu):${Object.keys(stu)}`)
结果如下图
🉐 根据对比,可以将上面的操作分为四个等级,由小到大
-
Object.keys() 获取自身属性,不可获取enumrable:false属性
-
for…in… 获取自身属性与父级属性,不可获取enumrable:false属性
-
Reflect.ownKeys(),Object.getOwnPropertyNames() 获取自身所有属性包括enumrable:false属性,不可获取父级
-
Reflect.has(),
in
关键字 获取自身和父级属性,包括enumrable:false属性 【只做判断,存在返回true,不存在返回false】
模块化
在NodeJS里面,因为没有DOM,所以不能使用标签 ,不能使用标签,所以就就没有<script>
,没有<script>
就不能导入JS文件,这个时候,NodeJS当中的JS文件相互引用应该怎么处理呢?
在NodeJS里面,提供了一套特殊文件导入导出机制,使用两个关键字来完成module.exports
用来导出一个对象
require
用来导入一个对象,下面请看例子
Person.js文件
let person={
name:"张三",
sex:"男",
age:18
}
//将当前JS文件里面的person对象导出去
module.exports=person;
Student.js文件
let person = require("./Person.js");
/**
* 学生对象
*/
let stu={
s_id:"NA006",
sayHello(){
console.log("学生对象");
}
}
console.log(person.name);
导出的时候是什么,那么在导入的过程当中,我们就要接收什么
module.exports在这里,可以 导出对象,可以导出方法,也可以导出class,那么,在导出的过程当中,它有没有一些注意事项呢或一些技巧呢
**注意:**在NodeJs里面,一个JS文件只允许使用module.exports
导出一次
当我们需要导入或导出多个对象的时候,可以利用ES6当中的解构特性来完成
Person.js文件
let person={
name:"张三",
sex:"男",
age:18
}
let teacher={
t_id:"T1001",
t_name:"李四"
}
//现在想把teacher与person都导出去,怎么办
// let obj={
// person:person,
// teacher:teacher
// }
// let obj={person,teacher};
//解构的赋值
module.exports={
person,
teacher
}
Student.js文件
//解构的取值
let {teacher,person}=require("./Person.js");
/**
* 学生对象
*/
let stu={
s_id:"NA006",
sayHello(){
console.log("学生对象");
}
}
console.log(person.name);
console.log(teacher.t_name);