ES6

194 阅读10分钟

let关键字与const关键字

主要作用:声明变量

let命令

var的相似,都是声明变量的。var有变量提升,letconst没有

let没有变量提升

脚本编译的时候看到,var声明的变量会提到上面先声明

console.log(a);
var a=10
//等同于
var a
console.log(a)
a=10;

let是一个块级作用域

if(1===1){
    let a=10;
    console.log(a)
}
console.log(a);  

在代码块外部打印不出来,并且抛出错误。

let不能重复声明

let a=10;
let a=20;
console.log(a)

var可以重复声明,let则不能重复声明变量

const命令

constvarlet作用也是相似的都是声明变量的

const也是一个块级作用域,也没有变量提升,也不能重复声明

const  max = 30;
console.log(max)
const max= 40;
console.log(max)

letvar的不同是:const一般都是声明常量一旦修改无法改变

const  max = 30;
console.log(max)
max= 40;
console.log(max)

一般框架里面作者使用const来声明常量,这样使用者使用的时候将无法修改里面的常量。

const可以声明对象形式的常量,但是不能直接修改常量对象,但是可以修改常量中的内部属性

可以修改常量中的内部属性

const  person={
    name:"小太阳"
}
console.log(person);
person.name="小胖子";
console.log(person);

不能直接修改常量对象

const  person={
    name:"小太阳"
}
console.log(person);
person.name="小胖子";
console.log(person);
const person={
    name="小胖子"
}
console.log(person);

const和let的作用

使用更灵活

使用letconstfor循环中如果想拿到第5次循环的结果就能拿到第5次循环的结果,在var中则是把for循环执行完然后返回最终结果

for循环是个经典案例

const arr=[];
for(let i=0;i<10;i++){
    arr[i]=function(){
        return i;
    }
}
console.log(arr[5]());//5

var arr=[];
for(var i=0;i<10;i++){
    arr[i]=function(){
        return i;
    }
}
console.log(arr[5]());   //10			

使用let不会对全局造成污染

let RegExp=10;
console.log(RegExp)
console.log(window.RegExp);

window有一个RegExp函数,我声明的不会影响全局属性

一般情况下使用const,只有在知道变量需要修改的时候使用let。

模板字符串。

模板字符串就是Tab键上面的反引号,把结构包裹起来,哪里需要变量就通过${}把当前变量名插入到结构里面就可以了。

<div class="box"></div>

JS原始的字符串拼接

const oBox=document.querySelector(".box");
let id=1;
name="小太阳";
oBox.innerHTML="<ul><li><p id="+id+">"+name+"</p></li></ul>"

使用模板字符串拼接

const oBox=document.querySelector(".box");
let id=1;
    name="小太阳";
let htmlStr=`<ul>
                <li>
                    <p id=${id}>${name}</p>
                </li>
            </ul>`
oBox.innerHTML=htmlStr

函数默认值

在ES5中函数调用的时候有形参,但是没有传入实参,那么就要给形参一个默认值。

function add(a, b) {
    a = a || 10;    //a有值就赋值给aa没值就默认10
    b = b || 20;
    return a + b;
}
console.log(add())   //30

在ES6中

传值了就走你传的参数,你没传参数那么就走原来的

function add(a,b=5){
    return a+b;
}
console.log(add(10,20))    //30

默认的表达式也可以是个函数

function add(a,b=getVal(5)){
    return a+b;
}
function getVal(val){
    return val+5;
}
console.log(add(10))

剩余参数

收集剩余的参数,由三个点...和一个紧更着的具名参数指定。(...keys),如果放在函数的形参中必须放在后面。

arguments不同的是,剩余参数返回真实数组,arguments返回伪数组

function checkArgs(...keys){
    console.log(keys);
    console.log(arguments)
}
checkArgs("a","b","c")

扩展运算符

扩展运算符也是三个点,它和剩余参数不同的是

剩余参数是:把多个独立的参数合并到一个数组中 扩展运算符:将一个数组分割,并将各个项作为分离的参数传递给函数

在ES5中用apply来传递参数,代码相对于比较麻烦

var arr=[10,23,45,65,47,98,24]
console.log(Math.max.apply(null,arr))    //98

扩展运算符是将数组分割,传入Math.max中逐一比较然后返回最大值

var arr=[10,23,45,65,47,98,24]
console.log(Math.max(...arr))    //98

箭头函数(重要)

函数的简写

使用=>来定义,取缔了ES5中function(){}的方式来定义函数,等价于()=>(){}让代码更加简洁

原始写法

let add=function(a,b){
    return a+b
}
console.log(add(10,20))   //30

ES6的写法

let add=(a,b)=>{
    return a+b
}
console.log(add(10,20))   //30

//等价于
let add=(a,b)=>(a+b);       //(a+b)表示return返回的值
console.log(add(10,20))   //30

箭头函数中this的指向

在ES6中是没有this绑定的。箭头函数内部this只能通过查找作用域链来确定。如果使用箭头函数就不会发生this指向错误的问题。

在ES5中this的指向取决于调用该函数的上下文对象。ES5中this会发生指向错误的现象

let pageHandle={
    id:123,
    init:function(){
        document.addEventListener("click",function(event){
            console.log(this)
            this.doSomeThings(event.type)
        })
    },
    doSomeThings:function(type){
        console.log(`事件类型:+${type},当前ID:+{this.id}`)
    }
}
pageHandle.init()

上文给文档添加了click事件,当调用函数的时候,内部this就指向了定义函数时候的上下文document。所以这时候this发生了改变。

箭头函数是没有this指向的,箭头函数中内部的this,只能通过查找作用域链来进行确定。一旦使用了箭头函数,它只寻找当前对象。

let pageHandle={
    id:123,
    init:function(){
        document.addEventListener("click",event=>{
            console.log(this)
            this.doSomeThings(event.type)
        },false)
    },
    doSomeThings:function(type){
        console.log(`事件类型:+${type},当前ID:+{this.id}`)
    }
}
pageHandle.init()

注意:给每个方法一定不要使用箭头函数,不然会把this指向最外层作用域window。比如上文的init方法不能使用箭头函数。

使用箭头函数注意事项

1. 使用箭头函数的时候内部就不存在arguments
let getVal=(a,b)=>{
  console.log(arguments);
  return a+b;
}
console.log(getVal(2,3))

2. 箭头函数不能使用new关键字来实例化对象

function是一个对象,但是箭头函数不是一个对象,箭头函数相当于函数的语法糖

let getVal=()=>{}
let p=new getVal()
console.log(p)

此处代码不会认为箭头函数它是一个function作用域,所以就没constructor属性,所以不是一个构造函数

解构赋值

解构赋值是对赋值运算符的一种扩展。通常它是针对数组或者对象来进行操作。

优点:书写简单易读

对象解构

对象完全解构

let node={
    type:"iden",
    name:"foo"
}
let {type,name}=node;
console.log(type,name)    //iden foo

不完全解构,有的属性可忽略。

let node={
    type:"iden",
    a:{
        name:"小太阳"
    },
    b:[]
}
let {a}=node;
console.log(a)

剩余运算符解构

let node={
    type:"iden",
    a:{
        name:"小太阳"
    },
    b:[]
}
let {a,...res}=node;
console.log(a,res)

数组解构

数组完全解构

let arr=[1,2,3]
let [a,b,c]=arr
console.log(a,b,c)    //1,2,3

数组不完全解构

let arr=[1,2,3]
let [a,b]=arr
console.log(a,b)    //1  2

数组嵌套解构

let [a,[b],c]=[1,[2],3]
console.log(a,b,c)

扩展的对象的功能

ES6可以直接写入变量和函数,作为对象的属性和方法

const name="小太阳";
      age=20;
  const person={
      name,    //等价于name:name
      age,
      sayName(){
          console.log(this.name)    //小太阳
      }
}
person.sayName()

对象的方法

is 等价于三个等号(===)特殊情况下使用

比较两个值是否严格相等,既比较值又比较数据类型。解决了在===下出现的不严谨现象

在三个等号下:不严谨

console.log(NaN===NaN) //false

在is下就会严格相等

console.log(Object.is(NaN,NaN)) //true

assign() 主要是浅拷贝

用于一个对象的合并,返回合并之后的新对象

let newObj=Object.assign({},{a:1},{b:2},{c:3});
console.log(newObj)

Symbol类型

Symbol是一种原始数据类型,一旦使用symbol声明了值,它表示是独一无二的值。

const name=Symbol("name");
const name2=Symbol("name");
console.log(name===name2)    //false

最大的用途就是用来定义对象的私有变量

通过Symbol声明出来的变量名是一个独一无二的值,声明出来属性名之后可以做为对象属性表示它是独一无二的,然后变成对象的私有属性。

let s1=Symbol("小太阳");
console.log(s1);
let obj={}
obj[s1]="小胖子",
console.log(obj)   //Symbol(小太阳): "小胖子"
console.log(obj[s1])    //获取值  小胖子   

//简写
let s1=Symbol("小太阳");
console.log(s1);
let obj={
    [s1]:"小胖子"   //这里一定是[变量名]
}
console.log(obj)   //Symbol(小太阳): "小胖子"
console.log(obj[s1])    //小胖子   获取值

声明一个obj对象,给obj对象赋值一个s1,此处的s1就是obj的私有变量。使用Symbol来定义对象中的变量,取值的时候一定要用中括号来取值[变量名]

Set

Set表示一个集合,表示无重复值的有序列表

let ste=new Set();
ste.add(2)
ste.add("3")
ste.add([1,2,3])
console.log(ste)
ste.delete(2);    //删除元素
console.log(ste);
//校验数组中是否有这个元素,如果有就返回true,否则返回false
console.log(ste.has("4"))    
console.log(ste.size)    //检测数组的长度

将set转化成一个数组可使用扩展运算符

let arr=new Set([1,2,3,4,5,5,6])
let arr1=[...arr]
console.log(arr1)

Set中对象的引用无法释放

解决办法就是什么变量的时候不使用Set而是使用WeakSet

释放前的代码

let set=new Set(),obj={};
set.add(obj);
obj=null;
console.log(set)

释放后的代码没有size属性,__propo__里面也少了很多方法

let set=new WeakSet(),obj={};
set.add(obj);
obj=null;
console.log(set)

WeakSet与Set的不同
  1. 不能传入非对象类型的参数
  2. 不可迭代
  3. 没有forEach方法
  4. 没有size属性

Map

Map是一个键值对的有序列表,它的键和值可以是任意类型

let map =new Map();
map.set("name","张三");
map.set("age",20);
console.log(map);   //Map(2) {"name" => "张三", "age" => 20}
console.log(map.get("name"))   //取name的值    张三
console.log(map.has("name"))   //校验map中是否有name    true
map.delete("name")               //删除name
console.log(map.has("name"))   //校验map中是否有name    false
map.set(["a",[1,2,3]],"hello")   //可以传入不同类型的值
console.log(map)

数组from方法与of方法

from方法

将伪数组转为真的数组

function add(){
    let arr=Array.from(arguments)
    console.log(arr)
}
add(1,2,3,4,5)

通过扩展运算符将伪数组转为真正的数组

<ul>
  <li>1</li>
  <li>2</li>
  <li>3</li>
</ul>
<script>
  let arr=document.querySelectorAll("li")
  console.log([...arr])
</script>

from还可以接收第二个参数,用来对每个元素进行处理

let lis=document.querySelectorAll("li")
 //ele.textContent获取li标签中的内容返回给arr
let arr=Array.from(lis,ele=>ele.textContent) 
console.log(arr)

of方法

of方法是将任意的数据类型转换到数组

console.log(Array.of([1,2,3,4],3,4,"3","4",{a:2}))

copywithin方法

copywithin方法将数组内部将指定位置的元素复制到其他位置,并返回当前新数组

console.log([1,2,3,4,5,6].copyWithin(0,3))  //[4, 5, 6, 4, 5, 6]

从索引为3向后的数组复制到从索引为0开始后面的3个数值

find方法 查找元素

找出第一个符合条件的元素

let number=[1,2,-4,-5].find(n=>n<0)    //箭头函数的缩写
console.log(number)      //-4

findIndex方法 查找索引

找出第一个符合条件的元素的索引

let number=[1,2,-4,-5].findIndex(n=>n<0) 
console.log(number) 			//2

entries() keys() values()返回一个遍历器,可以使用for...of循环遍历

keys()

使用keys()返回一个Array Iterator {}遍历器

console.log(["a","b"].keys())

使用for...of就能把当前遍历器的索引给遍历出来

for(let index of ["a","b","c","d"].keys()){
    console.log(index);
}

values()

使用values()返回一个Array Iterator {}遍历器

console.log(["a","b","c"].values())

使用for...of就能把当前遍历器的给遍历出来

for(let index of ["a","b","c","d"].values()){
      console.log(index);
  }

entries()

使用entries()返回一个Array Iterator {}遍历器

console.log(["a","b","c","d"].entries());    //Array Iterator {}

使用for...of遍历出当前数组的键值对取出来

for(let [index,ele] of ["a","b","c","d"].entries()){
    console.log(index,ele);
}

includes()

includes返回一个布尔值,表示某个数组是否包含给定的值,可以替代indexOf

console.log(["a","b","c","d"].includes("a"));    //返回布尔值true

相似与于indexOfindexOf如果有包含的值则返回出对应值的索引继续执行代码,如果没有则返回-1终止接下来代码的执行。

console.log(["a","b","c","d"].indexOf("c"));    //返回C对应的索引   2

迭代器iterator

  1. 迭代器是一个统一的接口,作用是使各种数据结构可以快捷的访问。通过 Symbol.iterator来创建迭代器,使用next()来获取迭代过后的结果,返回值返回一个函数。返回值一个是value,一个是donedone表示是否完成,整个遍历完成返回true,否则返回false遍历继续执行
  2. 迭代器是一个遍历数据结构的指针
//使用迭代器
const items=["one","two","therr"]
// 创建新的迭代器
const ite=items[Symbol.iterator]();
console.log(ite.next());    //{value: "one", done: false}
console.log(ite.next());	//{value: "two", done: false}
console.log(ite.next());	//{value: "therr", done: false}
console.log(ite.next());	//{value: undefined, done: true}

是一种新的遍历机制。使用迭代器,当然迭代就是遍历,遍历就是迭代

生成器generator

generator函数可以通过yield关键字将函数挂起,(挂起的意思是指将函数停留在这里不再继续执行),为了改变执行流提供了可能,同时为了做异步编程提供了方案

和普通函数的区别:

  1. function后面于函数名前面有一个*号就表示是一个生成器函数。
  2. 只能在函数内部使用yield表达式,将函数挂起来(停留)
function* func() {
    console.log("one");   //one
    yield 1;

}
let fen = func()
console.log(fen);    //func {<suspended>}
console.log(fen.next())  	//{value: 1, done: false}

函数返回一个对象,里面有一个next方法,调用方法的时候执行函数,直到遇见return,如果没有return则继续执行直到代码执行完毕。否则停留在当前yield语句。

总结:generator是分段执行的,yield语句是暂停执行,而next方法是恢复执行(当碰到yield的时候代码就卡在当前位置,调用next方法的时候就会把结果返回出来,一个next对应一个yield

next方法可以传参

function* func() {
    console.log("one:");     //one:
    //x不是yield的返回值,它是next方法恢复当前yield执行函数的实参。
    let x=yield 9;
    console.log("two:"+x);     //two:5
    let y=yield 2;
    console.log("three:"+y)    //three:6
    return x+y

}
let fen = func()
console.log(fen.next(4))  	//{value: 9, done: false}
console.log(fen.next(5))  	//{value: 2, done: false}
console.log(fen.next(6))  	//{value: 11, done: true}

作用:为不具备Iterator接口的对象提供遍历操作。

生成器generator的应用

解决回到地狱繁琐的问题

回调地狱:ajax请求的时候有时候会函数套函数,一个请求里面有无限级ajax请求。代码繁琐。这时候就可以使用生成器将异步代码同步化。

function* main(){
  console.log('main');
  let res = yield request('https://free-api.heweather.net/s6/weather/now?location=beijing&key=4693ff5ea653469f8bb0c29638035976')
  console.log(res);
  // 执行后面的操作
  console.log('数据请求完成,可以继续操作');
}
const ite = main();
ite.next();
function request(url){
  $.ajax({
      url,
      method:'get',
      success(res){
          ite.next(res);
      }
  })
}

在项目开发中也会遇到loading...的加载中、loading...数据加载完成,loading...隐藏的需求,但是数据加载完成之前是需要时间的,要是数据过大,加载延迟,就会出现先执行隐藏阶段,后加载loading数据完成。

function loadUI(){
    console.log("loading...加载中")
}
function showData(){
    setTimeout(()=>{
        console.log("loading...数据加载完成")
    },1000)
}
function hideUI(){
    console.log("隐藏loading...页面")
}
loadUI();
showData();
hideUI();

Generator可以部署AJAX操作。生成器可以让异步代码同步化

// 构造一个生成器
function *load(){
    loadUI();
    // 要把showData卡在当前位置,让我执行完了再继续执行隐藏的步骤
    yield showData();
    hideUI();
}
// 创建迭代器
let itLoad=load();
// 第一次调用next   执行showData
itLoad.next();
function loadUI(){
    console.log("loading...加载中")
}
function showData(){
    // 模拟异步的状态
    setTimeout(()=>{
         //异步操作完成之后才继续执行next激活hideUI函数
        console.log("loading...数据加载完成")
        // 通过迭代器调用next,继续往下执行隐藏loading...页面
        itLoad.next()
    },1000)
}
function hideUI(){
    console.log("隐藏loading...页面")
}

  1. 语法上,首先可以把它理解成,Generator 函数是一个状态机,封装了多个内部状态。
  1. Generator 函数除了状态机,还是一个遍历器对象生成函数。 可暂停函数, yield可暂停,next方法可启动,每次返回的是yield后的表达式结果。
  2. yield表达式本身没有返回值,或者说总是返回undefined。next方法可以带一个参数,该参数就会被当作上一个yield表达式的返回值。

Promise对象

在ES6中给我们提供了三种对于异步编程的解决方案:

  1. Generator与yield
  2. Promise对象
  3. async/await

Promise就是承诺的意思,Promise相当于一个容器,里面保存着一些未来才会结束的事件,通常未来才会结束的事件是一个异步结果。比如发送一个ajax事件两秒钟才能将我的数据请求回来,那么这个时候就是一个将要结束的事件,在Promise中就保存着事件结果。

Promise对象优势

各种异步操作都可以用同一种方法进行处理,比如axios内部就是使用Promise来实现的

Promise对象特点

具有两大特点

  1. 对象的状态不受外界影响,通常处理异步操作有三个状态,进行中pending、已完成Resolved、失败Rejected。不管外界传入什么都不会影响这三个状态。
  2. 一旦状态改变就不会再次发生改变。任何时候都可以得到这个结果

Promise基本用法

//1.创建一个Promise对象
//2.promise接收一个参数,这个参数是一个回调函数
//3.在回调函数中接收两个参数,一个是成功的(resolved),一个是失败的(rejected)
// 4.当new出这个实例过后会返回出一个promise对象
let pro=new Promise(function(resolved,rejected){  //resolved,rejected两个参数是两个函数
    // 5.在这中间执行异步操作
    // 加入从后端返回一个对象
    let res={
        code:201,
        data:{
            name:"小太阳"
        },
        error:"请求失败了"
    }
    // 一段时间之后我接收这个数据
    setTimeout(()=>{
        // 在这内部我可以去判断code是不是等于200,如果是就返回成功接收的数据data,失败返回error
        if(res.code===200){
            resolved(res.data)
        }else{
            rejected(res.error)
        }
    },1000)
})
// 用then接收执行完异步方法之后返回的状态,then 里面接收一个回调函数。
// 参数就是成功回调回来的结果
// pro.then里面不光可以接收一个回调函数还可以接收多个回调函数
pro.then((val)=>{
    // 在这个回调函数内打印结果
    console.log(val)
},(err)=>{
    console.log(err)
})

基本使用是会了但是代码还是比较生硬,比如请求时间想通过调用的时候自己传进来,而不是里面写死了。

// 有一个函数timeOut,毫秒就是参数ms
function timeOut(ms){
    // timeOut(2000)成功调用后返回去一个Promise对象
    return new Promise((resolved,rejectad)=>{
        setTimeout(()=>{
            resolved("数据请求成功")
        },ms)
    })
}
// 在外界调用timeOut的时候把毫秒传过去如果成功了,然后执行then返回结果
timeOut(2000).then((val)=>{
    console.log(val)
})

resolve方法

resolve方法能将一个现有的对象直接转成一个Promise对象,然后就有它自己的状态然后就可以通过.then.catch来调用

let p=Promise.resolve("foo");
//等价于
let p=new Promise(resolve=>resolve("foo"));
  p.then((data)=>{
      console.log(data)  //foo
  })

reject

reject方法能将一个现有的对象直接转成一个Promise对象,然后就有它自己的状态然后就可以通过.then.catch来调用

let p=new Promise(reject=>reject("foo"));
  p.then((data)=>{
      console.log(data)  //foo
  })

all方法

all方法是提供了并行的执行异步操作行为

let promise1=new Promise((resolve,rejct)=>{});
let promise1=new Promise((resolve,rejct)=>{});
let promise1=new Promise((resolve,rejct)=>{});
// sange Promise执行完再返回一个新的Promise对象
let p4=Promise.all([promise1,promise2.promise3])
p4.then((resolve)=>{
    // 三个都成功才成功
}).catch(err=>{
    // 如果一个失败则都失败
})

应用:一些游戏类的素材比较多,等待图片、flash、静态资源文件都加载完成才进行页面的初始化

race方法

race()给某个异步请求设置超时时间,并且在超时过后执行相应的操作。

race方法可以用它来做一个请求超时的状态,比如请求一张图片,三秒之后还没请求下来,那么应该告诉用户请求超时了。

function requestImg(imgSrc){
// 返回一个Promise对象
return new Promise((resolve,reject)=>{
    // 创建一个img对象
    const img=new Image();
    // img方法去调用onlode方法去加载,加载的时候执行了一个函数
    img.onload=function (){
        // 加载成功就直接resolve把img对象返回出去
        resolve(img)
    }
    // 然后给img对象设置src
    img.src=imgSrc;
})
}
// 然后有一个延迟的函数,对请求图片设置一个计时
function timeout(){
// 在这里面也是return一个箭头函数
return new Promise ((resolve,reject)=>{
    // 设置延迟请求
    setTimeout(()=>{
        // 3秒还没请求下来就返回失败的信息
        reject(new Error("图片请求失败"))
    },3000)
})
}
// 接下来通过race方法进行操作
// race方法里面首先接收一个数组里面是图片的路径,第二个参数是timeout方法
// 成功就走requestImg返回图片,请求失败就走timeout返回catch失败
Promise.race([requestImg("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1599342328136&di=ea914651a520306f7ce70774b28aa6cb&imgtype=0&src=http%3A%2F%2Fd.hiphotos.baidu.com%2Fzhidao%2Fpic%2Fitem%2Fe61190ef76c6a7efd517f640fbfaaf51f3de66a6.jpg"),timeout()]).then((data)=>{
// 3秒请求成功就走这个代码
console.log(data);
document.body.appendChild(data)
}).catch((err)=>{
// 3秒失败则走这里
})

async()的用法

async方法的作用:使得异步操作更加方便。

asyncGenerator的一个语法糖。

// async function f(){}这表示当前函数有async异步操作行为
// 基本操作,只要加了async就会返回一个Promise对象,有了这个对象我们就可以通过.then、.catch这些方法来对它进行调用
async function f(){
    // 通过await等待。
    // 模拟请求一个字符串
    let a= await "hello world";
    // a请求完过后,拿着a请求回来的结果再次做出请求操作  
    // split分割字符串
    let data= await a.split('');
    // 请求结束返回出data
    return data;
}
// 因为它返回一个Promise,所以就可以通过.then返回对应的结果
f().then((v)=>{
    console.log(v)
})

await不能单独使用,它一定是在async语法中。如果async函数中有多个await,那么then函数会等待所有的await指令运行完,才会去执行

注意:Generator()、 Promise()、 async() 这三个的出现是解决回到地狱问题,使得异步操作更加简洁方便

class类的用法

ES5中造类

function Person(name,age){
    this.name=name;
    this.age=age;
}
// 创建共有的方法
Person.prototype.sayName=function(){
    // 把名字返回出来
    return this.name
}
// 实例化对象
let p=new Person("小太阳",18);
console.log(p)

ES6中class语法

class Person{
// constructor:表示在Person函数中添加一个constructor实例方法
// constructor在Person实例化的时候立即调用
constructor(name,age) {  //constructor是class原型上的一个方法
    this.name=name;
    this.age=age;
}
// 给实例赋值一些共享的方法
// 挂载第一个方法
sayName(){
    return this.name
}    //方法之间不加逗号
// 挂载第二个方法
sayAge(){
    return this.age
}
}
let p = new Person("小太阳",17)
console.log(p)
console.log(p.sayName())
console.log(p.sayAge())

通过Object.assign一次性向实例添加多个方法

class Person {
  // constructor:表示在Person函数中添加一个constructor实例方法
  // constructor在Person实例化的时候立即调用
  constructor(name, age) { //constructor是class原型上的一个方法
      this.name = name;
      this.age = age;
  }
}
// 给实例赋值一些共享的方法
// 通过Object.assign一次性向实例添加多个方法
Object.assign(Person.prototype, {
  sayName() {
      return this.name
  },
  sayAge() {
      return this.age
  }
})
let p = new Person("小太阳", 17)
console.log(p)
console.log(p.sayName())
console.log(p.sayAge())

类的继承

在ES6中要想造类的继承就要使用关键字 extends

// 创建一个父类
class Animal{
    constructor(name,age) {
        this.name=name;
        this.age=age;
    }
    sayName(){
        return this.name
    }
    sayAge(){
        return this.age
    }
}
// 创建一个子类,希望Dog继承Animal
class Dog extends Animal{
    constructor(name,age,color) {
        // 希望name和age继承父类
        super(name,age)    //super表示超类的意思它会去调用他父类中的constructor方法
        this.color=color;
    }
    // 也可以创建子类自己的方法
    sayColor(){
        return `它叫:${this.name},今年${this.age}岁了,它是${this.color}的`
    }
    // 重写父类的方法
    sayName(){
        return this.name+","+this.sayAge()+","+this.color
    }
}
let dog=new Dog("小太阳",3,"橙色")
console.log(dog)
console.log(dog.sayColor())
// 继承父类的方法
console.log(dog.sayName())

ES6的模块

一个模块就是一个独立的文件。 ES6模块功能主要有两个命令构成,export抛出和import导入

export---抛出

用于模块对外的接口,就是在当前的JS文件中通过export接口把文件抛出去,然后在另一个脚本文件中通过import引入

export const name="小太阳";
export const age=18;
export function sayName(){
	return "我是小太阳"
}

import---导入

import用于输入其他模块提供的功能,要引入export抛出的文件那么就要在<script>标签中设置type=module

import {name,age,sayName} from "./js/index.js";
console.log(name,age,sayName())