NodeJS、ES6新特性解析

NodeJS基础

NodeJs代码就是把JS代码运行在了本地的电脑上面,它使用谷歌的V8引擎去执行代码,不用考虑代码的兼容性,它完全支持ES5以后的版本(ES5,ES6,ES7)

NodeJS与普通的JS还是有一点的区别

  1. NodeJS在这里它不包含DOM与BOM,只有ECMAScript
  2. NodeJS它的全局对象不再是window了,而一个global对象
  3. 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是一样的

数组的扩展

  1. 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);
    
  2. Array.from方法

    将类数组转换成真正的数组

    它与我们之前学过的Array.prototype.slice.call()的用法效果一样,但是Array.from有兼容性,在浏览器里面慎用,在NodeJs里面,可以随便使用

  3. 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);
    
  4. 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只返回第一个
    
  5. 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对象的遍历

  1. 通过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);
    }
    
  2. 直接通过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的常用方法

  1. set(“key”,“value”)添加键值对
  2. get(“key”)根据键返回值,如果没有找到返回undefined
  3. size描述当前容器中键值对的个数
  4. delete(“key”)根据一个键删除一个记录
  5. has(“key”)判断某一个键是否存在,如果存在则返回true,否则返回false
  6. clear()清除里面所有的元素

迭代方法

  1. 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);
    }
    
  2. values()迭代,返回一个Iterable的对象,它是一个迭代对象【返回值】

    let m_values = m.values();
    let v;
    while((v=m_values.next()).value!=undefined){
        console.log(v.value);
    }
    
  3. entries()迭代,返回一个Iterable的对象,它是一个迭代对象【返回键与值】

    var m_entries = m.entries();
    var first = m_entries.next();
    console.log(first.value);   // [‘NA001’,‘陈五五’]
    
  4. 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中对象的扩展

  1. 定义方法的时候发生了些变化

    /*
    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();
    
  2. 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);
    
  3. 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里面,我们学过很多种方式去创建对象

  1. 使用{}直接创建
  2. 使用new Object()创建
  3. 使用构造函数创建

现在在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默认不指向当前对象

总结

  1. class当中有一个构造函数constructor,它在new的一瞬间自己执行
  2. class里面可以通过get与set来设置访问器属性
  3. 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里面,到底多了些什么东西

  1. 继承方式改用extends
  2. 创建方式改用class关键字
  3. 多了一个static静态方法与方法重写
  4. 多了一个构造函数
  5. 多了一个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()方法一样

思考

  1. Reflect.has()是否可以获取到enumerable:false的属性 //true可以
  2. Reflect.ownKeys()是否可以获取到enumerable:false的属性
  3. 对比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)}`)

结果如下图


在这里插入图片描述


🉐 根据对比,可以将上面的操作分为四个等级,由小到大

  1. Object.keys() 获取自身属性,不可获取enumrable:false属性

  2. for…in… 获取自身属性与父级属性,不可获取enumrable:false属性

  3. Reflect.ownKeys(),Object.getOwnPropertyNames() 获取自身所有属性包括enumrable:false属性,不可获取父级

  4. 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);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值