ES6

88 阅读8分钟

课程目标

  • ES6新增API
  • 面试点
  • babel

知识点

  • 前身今世
  • API

前身今世

ES6一般是指ES2015也是泛指下一代JavaScript语言。

  • 历史
  1. ECMAScript1.0是1997年发布的
  2. 中间发过多次版本
  3. 2015年6月,ECMAScript6正式通过。
  • 目标 使得JavaScript语言可以用来编写复杂的大型应用程序,成为企业级开发语言

ECMAScript和JavaScript的关系

ECMAScript是JavaScript的规格,JavaScript是ECMAScript的实现,在日常场合中,两者是可以互换的

ES6和ECMAScript2015的关系

ES6的第一个版本在2015年6月发布,正式名称是《ECMAScript2015标准》,新版本会在每年的6月份发布,不需要以前的版本号,只要用年份标记就可以。以此类推。ES6也是一个泛指,含义是5.1版以后的JavaScript的下一代标准,涵盖了ES2015,ES2016,ES2017等。

API

1、常量

const,常量不可修改,不可重复定义

  • ES5怎么定义一个常量
Object.defineProperty(window,'arg',{
    value: 'A',
    writable: false
})

2、块极作用域

比如下面一段代码:

if(true){
    var arg = 'a'
}
console.info('arg',arg);//a

if(true){
    const arg1 = 'b'
}
console.info('arg1',arg1);//报错
  • arg变量能正常输入是因为var定义。相当于调用window.a,var定义的变量存在变量提升,可以随意修改,不受作用域限制。
  • const是在if作用块里内定义的,外部是调用不到的,无变量提升--先声明再使用。const不在window中。

3、dead zone(死锁)

如果在var申明变量前调用,输出的值就是undefined,如果是const申请之前调用就报错。

if(true){
    console.info('arg',arg);//undefined
    var arg = 'a'
}

4、let or const

  • 引用型 const
const obj = {
    a : 'a',
    b : 'b'
}
obj.a = 'c'//没问题不会报错

如果把引用类型改成常量,不可修改

const obj = {
    a : 'a',
    b : 'b'
    c : ['fr','ee','ze']
}
obj.a = 'c'//没问题不会报错
//冻结
object.freeze(obj);
obj.c[0] = 'success'//也是可能修改的
//freeze只能冻结第一层,如果下级是引用类型的话,还是可以修改的。
//解决方法:嵌套引用类型需要遍历递归
function deepFreeze(){
    //2.确定主执行步骤
    Object.freeze(obj);
    //3.逐级深入
    //如果用for in 来解决要判断hasOwnProperty   两个要成双成对出现
    (Object.keys(obj) || []).forEach(key => {
        let innerObj = obj[key]
        if(typeof innerObj === 'object'){
            //1.递归模式确定
            deepFreeze(innerObj)
        }
    });
}
//lodash: clone deepclone equal deepequal

5、箭头函数

先看一下传统函数:

function test(a,b){
    return a + b;
}
const test1 = function(a,b){
    return a + b
}

箭头函数:

    const test3 = (a,b) => {
        return a + b;
    }
    //简写
    const test3 = (a,b) => a + b;
    const test4 = x => {
        //content
    }
    const test5 = () => {
        //content
    }
上下文
//es6 context
const obj = {
    teacher: '小张老师',
    leader: '小王老师',
    getTeacher: function(){
        console.info(this.teacher);
        return this.teacher
    },
    getLeader: () => {
        console.info(this.leader);
        return this.leader
    }
}
obj.getTeacher()//小张老师
obj.getLeader()//undefined

上下文形成的,箭头函数并不会形成独立上下文,内部this指向了window

  1. 场景一:dom操作callback时
    const btn = document.querySelector('#btn');
    btn.addEventListener('click', function(){
        this.style.color = '#fff'
    });
  1. 场景二: 类操作 箭头函数无法成为完整构造类
function obj1(teacher, leader){
    this.teacher = teacher;
    this.leader = leader;
}
const obj1 = (teacher, leader) => {
    this.teacher = teacher;
    thid.leader = leader;
}
//传统函数是能正常获取参数的
//箭头函数报错提示没有constructor
const o1 = new obj1('小王', '小张');
console.info(o1);

  1. 场景三: 箭头函数无法构造原型方法
function obj1(teacher, leader){
    this.teacher = teacher;
    this.leader = leader;
}
//传统函数和箭头函数
obj1.prototype.learn = function(){
    console.info(this.teacher,this.leader);
}
obj1.prototype.learn = () => {
    //this 指向window
   console.info(this.teacher,this.leader);
}

const o1 = new obj1('小王', '小张');
//传统函数没问题  箭头函数undefined
console.info(o1.learn);
  1. 箭头函数的参数特性
const test = function(teacher){
    console.info(arguments);
}
const test = teacher => {
    console.info(arguments);
}

传统函数输出结果:

image.png

箭头函数输出结果:

image.png

  1. 箭头函数不能用call

6.class

class助力Js更面向对象。- 类

//传统对象
function Course(teacher, course){
    this.teacher = teacher;
    this.course = course;
}
Course.prototype.getCourse = function(){
    reutrn `teacher is:${this.teacher}, course:${this.course}`
}
const course = new Course("小王老师","ES6");
course.getCourse();
//es6
class Course {
    //构造方法。实例时会默认执行
    constructor(teacher, course){
        this.teacher = teacher;
        this.course = course;
    }
    getCourse() {
        reutrn `teacher is:${this.teacher}, course:${this.course}`
    }
    
}
const course = new Course("小王老师","ES6");
course.getCourse();
  1. class 的类型是:
console.info(typeOf Course);//function
  1. class的prototype是什么
console.info(Course.prototype);//两种方式都挂载了prototype上。有区分,但本质类型相同

class的prototype输出:

image.png

functioin的protype输出:

image.png

  1. class & 函数对象 属性 :相同
console.info(course.hasOwnProperty('teacher'));//
  1. 属性定义: 构造函数 & 顶层定义。两种定义方式
class Course {
    //构造方法。实例时会默认执行
    constructor(teacher, course){
        this.teacher = teacher;
        this.course = course;
    }
    getCourse() {
        reutrn `teacher is:${this.teacher}, course:${this.course}`
    }
    set teacher(val) {
        //留有空间
        this.teacher = val
    }
    get teacher() {
        //留有空间
        return `teacher is:${this.teacher}`
    }
    
}

修改只读变量,会报错吗?--无法改变但不会报错

  1. js如何建立一个私有属性
//方法一
class Course {
    //构造方法。实例时会默认执行
    constructor(teacher, course){
        this._teacher = teacher;
        let _course = 'es6';
        this.getCourse = () => {
            return _course;
        }
    } 
}
//方法二
class Course {
    #course = 'es6'
    //构造方法。实例时会默认执行
    constructor(teacher, course){
        this._teacher = teacher;
    } 
    get course(){
        return this.#course;
    }
    set course(val){
        if(val){
            this.#course = val;
        }
    }
}
  1. 封闭核心 - 适配器模式
class Utils {
    constructor(core){
        this._main = core;
        this._name = 'my-utils'
    } 
    //fullName:{firstname:'',name:''}
    get name() {
        return {
            ...this._main.fullName,
            ...{
                name: `${this._name}`
            }
        }
    } 
    set name(val){
        this._name = val;
    }
}
  1. static 静态方法 - 直接挂载在类上的方法无需实例化获取
//es5
function Course() {
    //...
}
Couse.ring = function() {
    //...
}
//es6
class Course {
    constructor() {
        //...
    } 
    static ring() {
        //...
    }
}

Course.ring();//解决全局对象变量问题,所有的实例共用一个方法或属性
  1. 继承
//es5
function Course() {
    //...
}
Couse.ring = function() {
    //...
}
Course.prototype.send = function(){
    //...
}
function Child() {
    Course.call(this,'小王',‘小张’);
    this.run = function() {
        //...
    }
}
Child.prototype = Course.prototype;

//es6
class Course {
    constructor() {
        //...
    } 
    static ring() {
        //...
    }
    send() {}
}
//工厂模式
class Child extends Course{
    constructor() {
        //初始化
        super('小王',‘小张’);
    } 
    static run() {
        //...
    }
}

7.解构

const msg = {
    teacher: {
        name:'',
        age:18
    },
    leader:'',
    name:'es6'
    
}
//别名
const {
    teacher: {
        name:'',
        age:18
    },
    leader,
    name:className
} = msg

  1. 解构使用场景
  • 形参结构
const sum = arr => {
    let res = 0;
    arr.forEach(each => {
        res += each;
    })
}

const sum = ([a,b,c]) => {
    return a + b +c;
}
  • 解构初始值
const course = ({ teacher, leader, course = 'msg'}) => {
    //...
}
course({
    teacher: '小王',
    leader: '小张'
})
  • 返回值
const getCourse = () => {
    return {
        teacher: '',
        leader:''
    }
}
const {teacher, leader} = getCourse();
  • 变量交换
let a = 1;
let b = 2;
[b, a] = [a, b]

  • json处理
const json = '{"teacher": '小王',“leader”: '小张'}'
const obj = JSON.parse(json);
const {
    teacher,
    leader
} = JSON.parse(json);
  • ajax
const {
    code,
    data,
    msg
} = response;

8.Proxy

为什么需要Proxy?

  1. 被代理的对象不想直接被访问。
  2. 控制和修改被代理对象的行为(调用属性、属性赋值、方法调用等),使之可以进行访问控制和增加功能
  • 参考网站 Proxy函数用于创建Proxy对象,也可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问都必须选通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。
  • 语法:
new Proxy(target, handler)
  • 参数:
  • target:原对象,目标对象,它可以是任何类型的对象,包括原生数组、函数、甚至是另一个代 理。要为其创建代理的对象。
  • handler:一个对象,是定义代理的自定义行为的对象。
handler API:

handler的作用就是用来自定义代理对象的各种可代理操作,本身一共有13种方法。

  • handler.getPrototypeOf();

在读取代理对象的原型时触发该操作,比如在执行Object.getPrototypeOf(proxy)时;

  • handler.setPrototypeOf();

在设置代理对象的原型时触发该操作,比如在执行Object.setPrototypeOf(proxy, null)时;

  • handler.isExtensible();

在判断一个代理对象是否是可扩展时触发该操作,比如在执行Object.isExtensible(proxy)时;

  • handler.preventExtensions();

在让一个代理对象不可扩展时触发该操作,比如在执行Object.preventExtensions(proxy)时;

  • handle.getOwnPropertyDescriptor();

在获取代理对象某个属性的属性描述时触发该操作,比如在执行 Object.getOwnPropertyDescriptor(proxy)时;

  • handler.defineProperty();

在定义代理对象某个属性时的属性描述时触发该操作,比如在执行 Object.defineProperty(proxy, "foo", {})时;

  • handler.has();

在判断代理对象的某个属性时触发该操作,比如在执行“foo” in proxy时;

  • handler.get();

在读取代理对象的某个属性时触发该操作,比如在执行proxy.foo时;

  • handler.set();

在给代理对象的某个属性赋值进触发该操作,比如在执行proxy.foo = 1时;

  • handler.deleteProperty();

在删除代理对象的某个属性时触发该操作,比如在执行delect proxy.foo时;

  • handler.ownKeys();

在获取代理对象的所有属性键时触发该操作,比如在执行Object.getOwnPropertyNames(proxy) 时;

  • handler.apple()

在调用一个目标对象为函数的代理对象时触发该操作,比如在执行 proxy()时;

  • handle.construct()

在给一个目标对象为构造函数的代理对象构造实例时触发该操作,比如在执行new proxy()时;

应用场景
  • 私有变量不能修改
let target = {
   name: 'poetries',
   _age: 22
}

let logHandler = {
  get: function(target,key){
      // startsWith()方法用来判断当前字符串是否是以另外一个给定的子字符串“开头”的,根据判
      //断结果返回 true 或 false。
      // 参数:
      // str.startsWith(searchString [, position]);
      // searchString
      // 要搜索的子字符串。
      // position
      // 在 str 中搜索 searchString 的开始位置,默认值为 0,也就是真正的字符串开头处。
    if(key.startsWith('_')){
      console.log('私有变量age不能被访问')
      return false
    }
    return target[key];
  },
  set: function(target, key, value) {
     if(key.startsWith('_')){
      console.log('私有变量age不能被修改')
      return false
    }
     target[key] = value;
   }
} 
let targetWithLog = new Proxy(target, logHandler);
// 私有变量age不能被访问
targetWithLog._age; 
 
// 私有变量age不能被修改
targetWithLog._age = 'others'; 

image.png

  • 中断代理 Proxy支持随时取消对target的代理,这一操作常用于完全封闭对数据或接口的访问。使用Proxy.revocable方法可以用来创建一个可撤销的代理对象,
  • Proxy.revocable语法:
Proxy.revocable(target, handler)

返回值:

返回一个包含了代码对象本身和它的撤销方法的可撤销Proxy对象,是一个对象,其结构为 {"proxy": proxy, "revoke": revoke}

  • proxy:表示新生成的代理对象本身,跟一般方法new Proxy创建的代理对象没什么不同,只是它可以被撤销掉。
  • revoke:撤销方法,调用的时候不需要加任何参数,就可以撤销掉和它一起生成的那个代理对象。
  • 一旦某个代理对象被撤销,它将变得几乎完全不可调用,在它身上执行任何的可代理操作都会抛出。 TypeError异常(执行可代理操作有14种,除以外的操作不会抛出异常),一旦被撤销,这个代理对象便不可能被直接被恢复到原来的状态,同时和它关联的目标对象以及处理器对象都有可能被垃圾回收掉,再次调用撤销方法revoke()则不会有任何效果,但也不会报错。
let obj = Proxy.revocable({},{
    get(target, name) {
        return "[["+ name+"]]";
    }
});
let proxy = obj.proxy;
proxy.foo;
obj.revoke();
console.info(proxy.foo); //TypeError
proxy.foo = 1; //TypeError
delete proxy.foo //TypeError
typeof proxy  //object,因为typeof不属于可代理操作
  • 有代理的情况下:

image.png

  • 撤销代理后:

image.png