ES6 详解

111 阅读5分钟

背景

ECMA - 瑞士标准协会,负责执行国际标准 JS - Netscape 和 Sun Microsystem 创建并提交,取名ECMAScript ES6 === 官方命名 - ES2015

ES6 主要API Const - 常量 以及 块级作用域 Arrow Function - 箭头函数 Class 类 Deconstruction - 解构 Array for of 数组操作 ...

const 常量标识

常量一般使用大写字母

const LIMIT = 10;
// 正反翻译机
const OBJ_MAP = {
  a: 'A',
  A: 'a'
}
// 5 => VI
// VI => 5
const QUEUE = [1, 2, 3, 4, 5]

1. 不允许重复声明赋值

// 变量重新赋值
var arg1 = '云隐';
arg1 = '小可';

面试:不引入polyfill 如何实现常量声明???

// 常量
// ES5
Object.defineProperty(window, 'arg2', {
  value: '云隐',
  writable: false
})
arg2 = '小可';

// ES6
const arg3 = '云隐';
arg3 = '小可';
// TypeError

// const 不允许重复声明
const arg3 = '云隐';
const arg3 = '小可';
// SyntaxError

2. 块级作用域

面试题:以下代码输出什么? 变量提升 + 块级作用域

  if (true) {
    var arg1 = '云隐'
  }
  console.log('arg1:' arg1)

  if (true) {
    const arg2 = '云隐'
  }
  console.log('arg2:' arg2)

3. 无变量提升

  console.log(arg1);
  var arg1 = '云隐';
  
  // 相当于
  var arg;
  console.log(arg1);
  var arg1 = '云隐';

  // 无边变量提升 - 先声明再使用
  console.log(arg2);
  const arg2 = '云隐'; // not defined

window: 浏览器视窗,整个上下文的最外层

  // 变量是否在root  
  var arg1 = '云隐';
  console.log(window.arg1); // 云隐

面试题:const 在不在 window 中?

不在!!!不会在全局下

  const arg1 = '云隐';
  console.log(window.arg1); // undefined

4. dead zone

  if (true) {
    console.log(arg1);
    var arg1 = '云隐'
  }
  // undefined

  if (true) {
    console.log(arg1);
    const arg1 = '云隐'
  }
  // Cannot access 'arg1' before initialization

5. let or const

  • 只用是引用型的 const ->
  const obj = {
    teacher: '云隐',
    leader: '小可'
  }

  obj.leader = '部部';

  const arr = ['云隐', '小可'];
  arr[0] = 'aaa';

为什么不报错?

引用类型的原理 - 指向地址,只要地址没有改变,那么就不会报错 追问: 如果要将对象变得不可改 破局 - object.freeze()

  object.freeze(obj);

  const obj2 = {
    teacher: '云隐',
    leader: '小可',
    zhuawa: ['部部', '小样']
  }

  object.freeze(obj2);
  obj2.zhuawa[0] = '云隐';

  // freeze 只能冻结根层,嵌套引用类型需要遍历递归

实现一个深冻结 // forEach 会常出现false 因此 Object.keys(obj) || [] // for in 经常会判断 Object.hasOwnProperty

  function deepFreeze() {
    // 2. 确定主执行步骤
    Object.freeze(obj);
    // 3. 逐级深入
    (Object.keys(obj) || []).forEach(key => {
      let innerObj = obj[key]
      if (typeof innerObj === 'object') {
        // 1. 递归模式确定
    deepFreeze(innerObj);
      }
    })
  }

手写lodash:clone deepClone equal deepEqual

箭头函数

  // 传统函数
  function text(a, b) {
    return a + b;
  }
  const test2 = function (a, b) {
    return a + b;
  }

箭头函数

  // ES6
  const test3 = (a, b) => {
    return a + b;
  }
  const test3 = (a, b) => a + b

  const test4 = x => {
    // content...
  }

  const test5 = () => {
    // ....
  }

上下文 this

  // ES6 context
  const bj2 = {
    teacher: '云隐',
    leader: '小可',
    zhuawa: ['部部', '小样'],
    getTeacher: function() {
      return this.teacher;
    },
    getLeader: () => {
      return this.leader;
    }
  }

追问:上下文形成的原因

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

场景

场景1: dom 操作 cb 时

  const btn = document.querySelector('#btn');

  btn.addEventListener('click', function() {
    this.style.color = '#fff';
  })
  // this 指向的当前对象,指向btn

场景2:类操作

箭头函数无法成为完整构造类,无法生成实例

  function Obj(teacher, leader) {
        this.teacher = teacher;
        this.leader = leader;
    }

    const Obj1 = (teacher, leader) => {
        this.teacher = teacher;
        this.leader = leader;
    }

    // 实例验证
    const o1 = new Obj('云隐', '小可');
    console.log(o1);
    // Obj {teacher: '云隐', leader: '小可'}
    
    const o2 = new Obj1('部部', '小可');
    console.log(o2)
    // Obj1 is not a constructor

追问: 箭头函数无法构造原型方法

  Obj.prototype.learn = function() {
    console.log(this.teacher)
  }
  o1.learn()
  // 云隐 小可
  
  Obj.prototype.learn = () => {
        console.log(this.teacher, this.leader);
  }
  o1.learn()
  // undefined undefined

本质:this指向 window

箭头函数的参数特性 - 无法使用arguments

  const test = function (teacher) {
    console.log(arguments);
  }
  // arguments 参数 teacher

  const test = teacher => {
    console.log(arguments);
  }
  // ReferenceError: arguments is not defined

Class

class 助力 js 更面向对象 - 类

  // 传统对象 - function 面向过程
  // 利用 prototype 来进行方法拓展
  function Course(teacher, course) {
    this.teacher = teacher;
    this.course = course;
  }

  Course.prototype.getCourse = function () {
    return `teacher is ${this.teacher}, course: ${this.course}`
  }

  const course = new Course('云隐', 'ES6');
  course.getCourse();
  // teacher is 云隐, course: ES6
 // ES6 每个业务逻辑面向对象
  class Course {
    // init 实例会默认执行
    constructor(teacher, course) {
      this.teacher = teacher;
      this.course = course;
    }

    // 拓展方法
    getCourse() {
      return `teacher is ${this.teacher}, course: ${this.course}`
    }
  }

  const course = new Course('云隐', 'ES6');
  course.getCourse();

追问

class 的类型是什么?

  console.log(typeof Course);
  // function

是function!!!!!!会被bable 翻译成 传统方式的 function

class 的原型是什么?

  console.log(Course.prototype)
  // 有区分,但是本质类型相同 ??

class & 函数对象 的属性 有什么不同?

  console.log(course.hasOwnProperty('teacher')) 
  // 不论是类 还是 函数对象,属性都会挂在原型上

属性定义 构造器 & 顶层定义 两种方式

  class Course {
    // init 实例会默认执行
    constructor(teacher, course) {
      this.teacher = teacher;
      this.course = course;
    }

    // 拓展方法
    getCourse() {
      return `teacher is ${this.teacher}, course: ${this.course}`
    }

    get teacher() {
      // 留有空间
      return this.teacher;
    }

    set teacher(val) {
      // 留有空间
      console.log('检测到setter')
      this.teacher = val;
    }
  }

  // 写constructor 和 static 以及set get 变量 意义何在?
  // 1. js 如何建立只读变量
  constructor(teacher, course) {
      this._teacher = teacher;
      this.course = course;
    }

  get teacher() {
    return this._teacher;
  }
  // 修改只读变量,会报错吗? -- 无法改变,也不会报错

  // 2. js 如何建立一个似有属性
  class Course {
    constructor(teacher, course) {
      this.teacher = teacher;
      
      // 在 constructor作用域内定义一个局部变量
      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;
      }
    }
  }
  
  // 3. 封装核心 - 适配器模式
  // 底层封装中台业务core
  class utils {
    constructor(core) {
      this._main = core;
      this.name = 'my-utils';
    }

    get name() {
      return {
        ...this._main.name,
        name: `utils is ${this._name}`
      }
    }
    set name(val) {
      // valid safety
      this._name = val;
    }
  }

class 静态方法 - 直接挂载在类上的方法 无需实例化获取

  // ES5
  function Course() {
    // ...
  }
  Course.ring = function() {
    // ... about ring
  }
  // 执行
  Course.ring();

  // ES6
  class Course {
    constructor () {
      // ... 
    }

    static ring () {
      // ...
    }
  }
  // 执行
  Course.ring();
  // 通常用来解决全局变量问题,所有的实例共用一个方法

继承

  // ES5 继承
  function Course() {
    // ...
  }
  Course.ring = function() {
    // ... about ring
  }
  Course.prototype.send = function () {
    // ...
  }

  function Child() {
    Course.call(this, '云隐', 'ES6');
    // 子类方法
    this.run = function () {
      // ...
    }
  }
  // 继承:原型链相连
  Child.prototype = Course.prototype; 
  // ES6
  class Course {
    constructor () {
      // ...
    }

    static ring() {}

    send() {}
  }
  // 继承 => 工厂模式:原型车被所有的其他车型作为继承进行生产
  class Child extends Course {
    constructor() {
      super('云隐', 'ES6'); // 继承父的初始化
    }

    run () {}
  }

解构 deconstruction

  const zhaowa = {
    teacher: {
      name: '小可',
      age: 30
    },
    leader: '部部',
    name: 'es6'
  }
  // 别名
  const {
    teacher: {
      name,
      age
    },
    leader,
    name: className
  } = zhaowa;


追问:解构使用场景

形参解构

  const sum = arr => {
    let res = 0;
    arr.forEach(each => {
      res += each;
    })
  }

  // ES6
  const sum = ([a, b, c]) => {
    return a + b + c;
  }

结合初始值

  const course = ({ teacher, leader, course = 'zhoawa'}) => {
    // ...
  }

  course({
    teacher: 'yy',
    leader: '小可'
  })

返回值

  const getCourse = () => {
    return {
      teacher: '',
      leader: ''
    }
  }

  const { teacher, leader } = getCourse();

变量交换

  let a = 1; 
  let b = 2;
  [b, a] = [a, b];

json 处理

  const json = '{"teacher": "yy", "leader": "xk"}';
  const obj = JSON.parse(json);

  const {
    teacher,
    leader
  } = JSON.parse(json)

ajax 处理

  const {
    code,
    data,
    msg
  } = response;