分类: web前端 | 标签: js es6

es6笔记

发表于: 2022-06-23 08:57:28 | 字数统计: 5.1k | 阅读时长预计: 25分钟

b站视频教程1:尚硅谷ECMAScript教程(ecmascript详解含es5、es6)

b站视频教程2:ES6从入门到精通系列(全23讲)

笔记只是记录开发中常用的,详细的可参考文档:

阮一峰: ECMAScript 6 入门

菜鸟教程:菜鸟教程es6

es简介

ECMAScript 6.0(以下简称 ES6)是 JavaScript 语言的下一代标准,已经在 2015 年 6 月正式发布了。它的目标,是使得 JavaScript 语言可以用来编写复杂的大型应用程序,成为企业级开发语言。

es5-严格模式

语法和行为改变:

  • 必须用var声明变量
  • 禁止自定义的函数中的this指向window
  • 创建eval作用域(不会污染全局作用域)
  • 对象不能有重名的属性

使用:

  • 在全局或函数的第一条语句定义为'user strict';
  • 如果浏览器不支持,只解析为一条简单的语句,没有任何副作用

【案例】

//'use strict' //开启严格模式
//1.必须用var声明变量
//a = 1 //报错 
//2.对象不能有重名的属性
// var obj = {
//     name: 'tom',
//     name: 'jerry'
// }
//3.禁止自定义的函数中的this指向window
// function Person(name,age){
//     console.log(this) //undefined
//     this.name = name
//     this.age = age
// }
// Person('tom','1')
//4.创建eval作用域-关闭严格模式打印两个30
var b = 20
eval('var b = 30;console.log("eval:",b)') //30
console.log(b) //20

es5-JSON扩展

  • JSON.stringify(obj/arr): json对象转字符串

  • JSON.parse(jsonStr): json字符串转json对象 注意json的属性名要加双引号

【案例】

//1.json对象转字符串
var obj = {name:'tom',age:'18'}
var jsonStr = JSON.stringify(obj)
console.log(jsonStr)
console.log(typeof jsonStr) //string
//2.json字符串转json对象 注意json的属性名要加双引号
var jsonStr = '{"name":"tom","age":18,"gender":"男"}';
var json = JSON.parse(jsonStr)
console.log(json)
//console.log(typeof json)

es5-Object对象方法扩展

image-20220623101229029

【案例】

//================1.Object.create
var obj = {name:'tom',age:18}
var obj1 = Object.create(obj,{
    sex: {
        value: '男',
        writable: true, //是否可修改
        configurable: true,//是否可删除
        enumerable: true//是否能用for in枚举对象
    }
})
obj1.sex = '女'
//delete obj1.sex
//console.log(obj1.sex)
//=================2.Object.defineProperties
var obj2 = {firstname: 'jerry',lastname: 'jack'}
Object.defineProperties(obj2,{
    fullname: {
        get: function(){ //获取扩展属性的值
            console.log('get调用了');
            return this.firstname+" "+this.lastname
        },
        set: function(data){//设置扩展属性的值
            console.log('set调用了',data);
            var rs = data.split(' ')
            this.firstname = rs[0]
            this.lastname = rs[1]
        }
    }
})
console.log(obj2.fullname)
obj2.fullname = 'qin yang'
console.log(obj2.fullname)

image-20220623103422946

【案例】

var obj3 = {
    firstname: 'lucy',
    lastname: 'lu',
    get fullname(){//获取扩展属性的值
        console.log('get调用了');
        return this.firstname+" "+this.lastname
    },
    set fullname(data){//设置扩展属性的值
        console.log('set调用了',data);
        var rs = data.split(' ')
        this.firstname = rs[0]
        this.lastname = rs[1]
    }
}
console.log(obj3.fullname)
obj3.fullname = 'zhang san'
console.log(obj3.fullname)

es5-数组的扩展

image-20220623104111791

除上面五个扩展还有两个扩展

  • some: 遍历数组,如果有至少一个满足条件,就返回true,否则返回false
  • every: 遍历数组,当所有的元素返回true,才返回true,否则返回false

【案例】

//返回指定元素第一次出现的下标
var arr1 = [1,2,3,2,5]
console.log(arr1.indexOf(2)) //1 从前取
console.log(arr1.lastIndexOf(2))//3 从后取
//遍历数组
arr1.forEach(function(value,index,arr){
    console.log(value,index,arr);
})
//加工数组
var arr2 = arr1.map(function(v,i,arr){
    return '我是:'+v
})
console.log(arr2) //['我是:1', '我是:2', '我是:3', '我是:2', '我是:5']
//过滤数组 比如取出大于2的
var arr3 = arr1.filter(function(v,i,arr){
    return v > 2
})
console.log(arr3) // [3, 5]

es5 bind call apply

image-20220623105247328

【案例】

function showThis(a,b){
    console.log('hello:',this.name+"-"+this.age+"参数:"+a,b)
}
//直接调用 this为window
showThis() //hello: -undefined参数:undefined undefined
function Person(name,age){
    this.name = name
    this.age = age
}

var person= new Person('tom',18)
//通过call,把this改成了person,call和apply的区别是传参的方式不同
//showThis.call(person)//hello: tom-18参数:undefined undefined
//showThis.apply(person)//hello: tom-18参数:undefined undefined
showThis.call(person,'a','b')//hello: tom-18参数:a b
showThis.apply(person,['a','b'])//hello: tom-18参数:a b

//通过bind改变this,bind不会立即执行而是需要调用一下
showThis.bind(person)()//hello: tom-18参数:undefined undefined
showThis.bind(person,'a','b')()//hello: tom-18参数:a b

es6 let和const

let

image-20220623110845037

【案例】

//1.不能重复声明
let a = 1
//let a = 1 //Identifier 'a' has already been declared
//2.不存在变量提升
// console.log(b) //UncaughtReferenceError: b is not defined
// let b = 2
//3.在块作用域有效
if(true){
    //var c = 1
    let c = 1
}
//console.log(c) //UncaughtReferenceError: c is not defined

const

image-20220623111524970

【案例】

//不能修改
const PI = 3.14
//PI = 3.1415 //Uncaught TypeError: Assignment to constant variable.
//对象可修改属性,不可修改引用
const obj = {name:'tom',age: 16}
//obj = {} ////Uncaught TypeError: Assignment to constant variable.
obj.name = 'jerry'
console.log(obj)

es6 对象的解构赋值

image-20220623112143166

【案例】

//对象的解构
let obj = {name: 'tom',age: 18}
let {name,age} = obj
console.log(name,age) //tom 18
//数组的解构
let arr = [1,2,3,true]
let [a,b] = arr
console.log(a,b) //1 2
let [,,c,d] = arr 
console.log(c,d)//3 true
//解构应用
function showObj({name,age}){
    console.log(name,age)
}
showObj(obj) //tom 18

es6 模板字符串

image-20220623112822655

var obj = {name: 'tom',age: 18}
var str = `我叫${obj.name},今年${obj.age}岁了`
console.log(str);

es6 对象的简写

image-20220623113115363

【案例】

var name = 'tom'
var age = 18
// var obj = {
//     name: name,
//     age: age,
//     sayHello: function(){
//         console.log(this.name,this.age)
//     }
// }
//简写
var obj = {
    name,
    age,
    sayHello(){
        console.log(this.name,this.age)
    }
}
console.log(obj)
obj.sayHello()

es6 形参默认值

ES6 允许为函数的参数设置默认值,即直接写在参数定义的后面。

【案例】

function add(a=1,b=1){
    console.log(a+b)
}
add() //2
//add(1,2) //3

es6 箭头函数

image-20220623120217016

【箭头函数基础案例】

let f = v=>v;
//等同于
let f = function(v){
    return v;
}

// 有一个参数
let add = value => value;

// 有两个参数
let add = (value,value2) => value + value2;
//等同于
let add = (value1,value2)=>{
    
    return value1 + value2;
} 
// 无参数
let fn = () =>  console.log("hello world")
//等同于
let doThing = () => {
    console.log("hello world")
}
//如果箭头函数直接返回一个对象,必须在对象外面加上括号,否则会报错。
let getId = id => ({id: id,name: 'mjj'}) //注意
let obj = getId(1);

【箭头函数this案例】

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title></title>
    </head>
    <body>
        <button id="btn1">btn1</button>
        <script>
            //箭头函数内部没有this,会一直往外找找到非箭头函数的this,如果没有则指向window
            let btn1 = document.getElementById('btn1')
            // let obj = {
            //     name: 'tom',
            //     age: 18,
            //     sayHello: function(){
            //         btn1.onclick = function(){
            //             console.log(this) //btn1
            //         }
            //     }
            // }
            
            // let obj = {
            //     name: 'tom',
            //     age: 18,
            //     sayHello: function(){
            //         btn1.onclick = () => console.log(this) //obj
            //     }
            // }
            
            let obj = {
                name: 'tom',
                age: 18,
                sayHello: () => {
                    btn1.onclick = () => console.log(this) //window
                }
                
            }
            obj.sayHello()
        </script>
    </body>
</html>

【补充】

箭头函数中没有arguments对象

var getVal = (a,b) => {
    console.log(arguments);
    return a + b;
}
console.log(getVal(1,2)); //arguments is not defined

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

let Person = ()=>{}
let p1 = new Person();// Person is not a constructor

es6 Symbol

不常用,了解即可

image-20220623230202509

对象的Symbol.iterator属性,指向该对象默认的遍历器方法

【案例】

//1.创建Symbol,symbol表示独一无二的值,不用new关键字
let symbol1 = Symbol()
let symbol2 = Symbol()
console.log(symbol1 == symbol2);//false
//2.symbol作为对象的属性,要用[]存取
var obj = {}
obj[symbol1] = '123'
//console.log(obj.symbol1) //undefined
console.log(obj[symbol1])
//3.传参标识-这种一般用来定义常量
let symbol3 = Symbol('a')
let symbol4 = Symbol('b')
console.log(symbol3)
console.log(symbol4)
const PREFIX = Symbol('PREFIX')
console.log(PREFIX)

es6 iterator 遍历器

和Java的迭代器差不多

image-20220623232906861

【案例】

/*对象的Symbol.iterator属性,指向该对象默认的遍历器方法
            数组、字符串、arguments、set/map 
            已经自带的这个属性所以可以使用 for of方法遍历*/
var arr  = ['a','b','c']
for (let s of arr) {
    console.log(s);
}
var str = 'efg'
for (let s of str) {
    console.log(s);
}
//使用迭代器
var it = arr[Symbol.iterator]()
//每调用一次指针就会往后移动一次,到最后的时候done会变为true
console.log(it.next());//{done: false,value: "a"}
console.log(it.next());//{done: false,value: "b"}
console.log(it.next());//{done: false,value: "c"}
console.log(it.next());//{done: true,value: "undefined"}

es6 generator

了解即可,后面的async/await可以简化这个

image-20220624094806654

【案例】

//可以把异步任务放到yield后面,从而解决回调地狱问题
function* myGenerator(){
    console.log('开始执行了')
    let a = yield 'hello'
    yield console.log(111)
    let b = yield 'hi'
    console.log('执行结束了')
}
//此处函数体内代码还不会执行,需要使用next方法
let mg = myGenerator()
//使用next方法执行,就是前面迭代器
console.log(mg.next())//{value: 'hello', done: false}
mg.next() //111
console.log(mg.next())//{value: 'hi', done: false}
console.log(mg.next())//{value: undefined, done: true}

【案例2-next方法传参数】

image-20220624101808142

es6 promise

  • 理解
1.Promise对象:代表了未来某个将要发生的事件,通常是一个异步操作。有了Promise对象,可以将异步操作以同步的流程表达出来,避免了层层调用的回调函数(俗称”回调地狱“)
ES6的Promise是一个构造函数,用来生成promise实例
promise对象的三个状态
* pending:初始化状态
* fullfilled: 成功状态
* rejected: 失败状态

2.使用promise基本步骤
第一步:创建promise对象
let promise = new Promise((resolve,reject) =>{
    //初始化promise状态为pending
    //执行异步操作...
    if(异步操作成功){
        resolve(value) //修改promise的状态为fullfilled
    }else{
        reject(errMsg) //修改promise的状态为rejected
    }
})
第二步: 调用promise的then(成功的回调,失败的回调)
promise.then(function(){
    result => console.log(result),
    errorMsg => alert(errorMsg)
})
【.then的返回值仍然是promise,意味着可以链式调用then,从而解决回调地狱问题】

3.应用
* 使用promise实现超时处理
* 使用promise封装ajax请求

【promise原理代码】

console.log('111')
var flag = false
let promise = new Promise((resolve,reject) => {
    //自动初始化promise状态为pending
    console.log('222')
    //执行异步任务
    setTimeout(()=>{
        if(flag){
            resolve('哈哈') //成功,修改promise的状态为fullfilled
        }else{
            reject('哦嘛噶') //失败,修改promise的状态为rejected
        }
    },2000)
})
console.log('333')
promise.then((res)=>{//成功的回调
    console.log(`${res} success`)
},(err)=>{//失败的回调
    console.log(`${err} error`)
})

【链式调用例子】~解决回调地狱问题!!!

const p = new Promise(function(resolve,reject){
  resolve(1);
}).then(function(value){ // 第一个then // 1
  console.log(value);
  return value * 2;
}).then(function(value){ // 第二个then // 2
  console.log(value);
}).then(function(value){ // 第三个then // undefined
  console.log(value);
  return Promise.resolve('resolve'); 
}).then(function(value){ // 第四个then // resolve
  console.log(value);
  return Promise.reject('reject'); 
}).then(function(value){ // 第五个then //reject:reject
  console.log('resolve:' + value);
}, function(err) {
  console.log('reject:' + err);
});

es6 async

image-20220623222054560

【案例】

var flag = true
function f1() {
    return new Promise((resolve, reject) => {
        //自动初始化promise状态为pending
        console.log('222')
        //执行异步任务
        setTimeout(() => {
            if (flag) {
                resolve('哈哈') //成功,修改promise的状态为fullfilled
            } else {
                reject('哦嘛噶') //失败,修改promise的状态为rejected
            }
        }, 2000)

    })
}
//async就不用写.then了
async function test() {
    try{
        console.log('test start');
        let result = await f1() //获取resolve和reject传来的参数
        console.log('test end',result);
    }catch(e){
        /*await可以直接获取到后面Promise成功状态传递的参数
        但是却捕捉不到失败状态,所以通过try catch来处理*/
        console.log(e)
    }
}
test()

es6 三点运算符

image-20220623114113668

【案例】

//扩展运行符-函数参数
function fun(...args){ //三点运算符作为参数,必须放到最后
    // arguments.forEach(function(v,i,arr){
    //     console.log(v)
    // })
    args.forEach(function(v,i,arr){
        console.log(v)
    })
}
fun(1,2,3)
//扩展运算符-数组
var arr = [1,2,3]
var arr2 = [4,6,...arr]
arr2[2] = 8
console.log(arr,arr2)
//扩展运算符-对象
var obj = {name:'tom',age: 18}
var obj2 = {...obj,sex: '男'}
obj2.name = 'jerry'
console.log(obj,obj2)

es6 class类使用

image-20220623145804030

【案例】

class Person{//class 定义类
    constructor(name,age){ //定义构造方法
        this.name = name
        this.age = age
    }
    sayHello(){
        console.log(this.name,this.age)
    }
}
let person = new Person('tom',16) //创建实例
console.log(Person.prototype.constructor == Person) //true
console.log(person)

class Student extends Person{ //实现继承
    constructor(name,age,sex){
        super(name,age) //调用父类的构造方法
        this.sex = sex
    }
    //重写父类方法
    sayHello(){
        console.log(this.name,this.age,this.sex)
    }
}
let stu = new Student('jerry',16,'女')
console.log(stu)
stu.sayHello()

es6 字符串和数值的扩展

字符串扩展

image-20220623162158471

【案例】

let str = 'abcdedddd';
console.log(str.includes('a'))//true
console.log(str.startsWith('a'));//true
console.log(str.endsWith('e'));//true
console.log(str.repeat(2));//abcdeddddabcdedddd

数值扩展(了解)

image-20220623162633567

es6 数组方法的扩展

image-20220623162800442

【案例】

//1.Array.from()
function fun(){
    let arr = Array.from(arguments) //将伪数组转化成数组
    console.log(arr instanceof Array) //true
}
fun(1,2,3)
//2.Array.of(...)
let arr = Array.of('a',1,2,true)
console.log(arr instanceof Array) //true
console.log(arr);//['a', 1, 2, true]
//3.find和findIdex
let nums = [1,2,3,4,5]
let rs = nums.find((v,i,arr) => v>3)//找出第一个大于3的数字
console.log(rs) //4
var rsIndex = nums.findIndex( v => v>3) //找出第一个大于3的数字的下标
console.log(rsIndex);//3

es6 对象方法的扩展

image-20220623170827016

【案例】

console.log(0 == -0) //true
console.log(NaN == NaN) //false
//下面两个正好和上面两个相反
console.log(Object.is(0,-0)) //false
console.log(Object.is(NaN,NaN));//true
//***将对象的属性复制到目标对象上,并把目标对象返回
let obj = {}
let obj1 = {username: 'tom',age: 18}
let rs1 = Object.assign(obj,obj1,{sex:'男'}) //返回目标对象
console.log(rs1 == obj) //true
console.log(obj == obj1) //false
let rs2 = Object.assign({},obj1,{sex:'女'}) //返回目标对象
console.log(rs2)
//直接操作原型
let obj2 = {}
obj2.__proto__ = obj1
console.log(obj2.username)

es6 深拷贝和浅拷贝

image-20220624111713688

【浅拷贝案例】

//===========浅拷贝
var obj = {name: 'tom',age:18,hobby:[1,2,3]}
var obj1 = Object.assign({},obj) //数组对象拷贝的是引用
console.log(obj == obj1)//false
obj1.name = 'jerry'
obj1.hobby[0] = 'a'
console.log(obj,obj1);

var arr = ['a',1,{name: 'tom',age:18}]
var arr1 = arr.concat()
console.log(arr == arr1); //false
arr1[2].name = 'jerry' //对象拷贝的是引用
console.log(arr,arr1);

var arr2 = ['a',1,{name: 'tom',age:18}]
var arr3 = arr2.slice(0)
console.log(arr2 == arr3) //false
arr3[2].name = 'jerry' //对象拷贝的是引用
console.log(arr2,arr3);

【深拷贝案例】

//====深拷贝
            
//首先知道:利用Object.prototype.toString.call(target) 来判断类型
console.log(Object.prototype.toString.call('a')); 
console.log(Object.prototype.toString.call(1));
var arr = [1,2,3]
var obj = {}
console.log(Object.prototype.toString.call(arr))//[object Array]
console.log(Object.prototype.toString.call(obj))//[object Object]
//最终用下面的写法来判断类型
console.log(Object.prototype.toString.call(obj).slice(8,-1))//Object
console.log(Object.prototype.toString.call(arr).slice(8,-1))//Array

//深拷贝代码实现,重点是实现对象和数组的拷贝
//a.判断类型
function checkType(target){
    return Object.prototype.toString.call(target).slice(8,-1)
}
//b.for in + 递归实现深拷贝
function clone(target){
    let rs;
    let type = checkType(target)
    if(type === 'Object'){
        rs = {}
    }else if(type === 'Array'){
        rs = []
    }else{//如果拷贝其它类型,直接返回
        return target
    }
    //遍历每一项
    for (let i in target) {//对象:i为属性名  数组:i为下标
        let val = target[i]//取出每一项
        if(checkType(val) === 'Object' || checkType(val) === 'Array'){
            //如果是则递归拷贝
            rs[i] = clone(val)
        }else{
            rs[i] = val
        }
    }
    return rs
}
var arr = ['a',1,{name: 'tom',age:18}]
var arr1 = clone(arr)
console.log(arr == arr1);
arr1[2].name = 'jerry'
console.log(arr,arr1);

//当然,最简单是JSON.parse(JSON.stringify(json对象)) 来拷贝
// var arrs =  ['a',1,{name: 'tom',age:18}]
// var arrs1 = JSON.parse(JSON.stringify(arrs))
// console.log(arrs == arrs1);
// arrs1[2].name = 'jerry'
// console.log(arrs,arrs1);

es6 set和map容器

image-20220623172257632

【案例】

//1.set
//let set = new Set()
let set = new Set(['a','b','c','a']) //实例化
set.add(1) //加数据
set.add(2)
set.add(1)
console.log(set) //{'a', 'b', 'c', 1, 2}
console.log(set.has('a')) //true
console.log(set.size) //5
set.delete(1) //删除数据
console.log(set)
set.clear() //清空set
console.log(set)
//2.map
//let map = new Map()
//注意:如要初始化数据,需要使用二维数组初始化
let map = new Map([['hobby','篮球'],['school','清华大学']])
map.set('username','tom')
map.set('password','123')
map.set('age',10)
console.log(map)
console.log(map.get('hobby'))//篮球
console.log(map.has('age'));//true
console.log(map.size);//5
console.log(map.delete('age'))//true 删除数据
console.log(map)
map.clear() //清空set
console.log(map);

es6 for of

image-20220623174123119

【案例】

//遍历数组
var arr = ['a','b','c']
for (let s of arr) {
    console.log(s);
}
//遍历set
let set = new Set(['s1','s2','s3'])
//console.log(set)
for (let s of set) {
    console.log(s)
}
//遍历字符串
let str = 'es6'
for(s of str){
    console.log(s);
}
//遍历伪数组
function fun(){
    for (let s of arguments) {
        console.log(s);
    }
}
fun('w','s','z')

es7 幂运算和数组包含

  • 指数运算符-幂运算 **

  • Array.prototype.includes(value) 判断数组中是否包含指定的value

【案例】

//指数运算
let rs = 3 ** 3
console.log(rs) //27
//数组包含指定value
var arr = ['abc','c','d',true]
console.log(arr.includes('a')) //false
console.log(arr.includes('abc')) //true

模块化

历史上,JavaScript 一直没有模块(module)体系,无法将一个大程序拆分成互相依赖的小文件,再用简单的方法拼装起来。其他语言都有这项功能,比如 Ruby 的require、Python 的import,甚至就连 CSS 都有@import,但是 JavaScript 任何这方面的支持都没有,这对开发大型的、复杂的项目形成了巨大障碍。

在 ES6 之前,社区制定了一些模块加载方案,最主要的有 CommonJS 和 AMD 两种。前者用于服务器,后者用于浏览器。ES6 在语言标准的层面上,实现了模块功能,而且实现得相当简单,完全可以取代 CommonJS 和 AMD 规范,成为浏览器和服务器通用的模块解决方案。

ES6 模块的设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonJS 和 AMD 模块,都只能在运行时确定这些东西。比如,CommonJS 模块就是对象,输入时必须查找对象属性。

export和import

模块功能主要由两个命令构成:exportimportexport命令用于规定模块的对外接口,import命令用于输入其他模块提供的功能。

一个模块就是一个独立的文件。该文件内部的所有变量,外部无法获取。如果你希望外部能够读取模块内部的某个变量,就必须使用export关键字输出该变量

  • 新建一个index.js模块,内容如下
//导出属性
// export const name = 'tom'
// //导出方法
// export function sayHello(){
//     console.log('hello')
// }

//也可以按下面这样
const name = 'tom'
function sayHello(){
    console.log('hello')
}
export {name,sayHello} //导出
  • html页面导入模块
<!-- 别忘了加上 type="module" 否则会报错 -->
<script type="module">
    import {name,sayHello} from './module/index.js'
    console.log(name) //tom
    sayHello() //hello
</script>

export default和import

使用export default命令为模块指定默认输出

  • 新建一个export-default.js模块,内容如下
export default function foo(){
    console.log('foo...')
}
//或者按下面这样写
// function foo(){
//     console.log('foo...')
// }
// export default foo
  • html页面导入模块
<script type="module">
    import customFoo from './module/export-default.js'
    customFoo() //foo...
</script>

参考

ES6 入门教程

------ 本文结束,感谢您的阅读 ------
本文作者: 贺刘芳
版权声明: 本文采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。