ES6的基础语法新特性


ES6语法在面试中是频繁被提及的,相比于ES3和ES5它具备许多🆕特性,接下来,主要通过对比将从多个方面逐个介绍它的新特性。

一、常量定义

在ES3中不存在常量的定义,ES5中可以实现,但并未明确的定义,通过对象添加属性prototype实现,Object.defineProperty,在ES6中,提出了letconst

1. let

  • let作用域只限于当前代码块
  • let声明的变量作用域不会被提升
  • 在相同的作用域下不能声明相同的变量
  • for循环体现let父子作用域

2. const

  • const作用域只限于当前的代码块
  • const声明变量作用域不会被提升
  • 在相同的作用域下不能重复声明
  • 声明的同时必须赋值,声明之后的值无法改变

3. var、let、const三者对比

var let const
全局、整个函数块 块级变量 块级变量
存在变量的提升 允许重新赋值 不允许重新赋值

二、数据类型

首先回忆一下Js中的数据类型📝
基本数据类型:Number, Boolean,String,Null,Undefined (变量值存储于栈中)
引用数据类型:Array, Object,Function(对象存储于堆中,对象的地址存在栈中)
在函数调用的过程中,都是传递栈中的内容,基本数据类型传递的是变量的值,引用数据类型传递的是对象的地址

1.ES6新增的数据类型: Symbol

ES5对象中的属性名都是字符串,容易造成属性冲突;
ES6引入了新的原始数据类型Symbol,表示独一无二的值

因此设置Object对象的属性名有两种方式:一是字符串,二是Symbol

let str1 = Symbol()
let str2 = Symbol()
console.log(str1 === str2) // false,每个都独一无二
console.log(typeof(str1)) // Symbol

// Symbol主要用于对象属性的定义,且属性是唯一的
const obj = {}
obj[Symbol("name")] = "Mike"
obj[Symbol("name")] = "Casey"
console.log(obj) // {Symbol(name):"Mike",Symbol(name):"Casey"}

2.数据集合Set

Set数据集合的特性

使用示例:

let set  = new Set(['Juidy','Juidy','Lisa','Davie'])
console.log(set) // Juidy, Lisa, Davie
console.log(set.size) // 3
set.add('Gier')
set.delete('Lisa')
set.has('Yumy') // false
set.clear()

3.数据集合Map

Map数据集合的特性

使用示例:

let obj1 = {height:'cm'}
let obj2 = {weight:'kg'}
const map = new Map([
  ['name', 'Twitter'],
  ['age', 20],
  ['sex','male'],
  [obj1,183],
  [onb2,68]
])
console.log(map) 
// Map {  'name' => 'Twitter',  'age' => 20,  'sex' => 'male',  { height: 'cm' } => 183,  { weight: 'kg' } => 68 }
console.log(map.size) // 5, 即不重复的key的个数
// 设置值和获取值
console.log(map.set('age',21))
console.log(map.get('age'))
// Map自带的获取键,值以及键值对hash关系的方法
console.log(map.keys())
// [Map Iterator] { 'name', 'age', 'sex', { height: 'cm' }, { weight: 'kg' } }
console.log(map.values())
// [Map Iterator] { 'Twitter', 21, 'male', 183, 68 }
console.log(map.entries())
// [Map Iterator] {  [ 'name', 'Twitter' ],  [ 'age', 21 ],  [ 'sex', 'male' ],  [ { height: 'cm' }, 183 ],  [ { weight: 'kg' }, 68 ] }

三、解构赋值

ES6允许按照一定的模式从数组和对象中提取值,对变量进行赋值,这被称为解构💥
1. 基本用法:

let [name,age,sex] = ["Casey",23,"female"]
// 等价于,传统定义
let name = "Casey"
let age = 23
let sex = "female"

2.支持解构赋值的数据类型
1) 对象
2) 嵌套数组
3) 字符串
4) 空缺变量 let [a,b,,e] = ['Addy','Bob',[1,2],4]
5) 多余变量 let [a,b,,e,f] = ['a','b',[3,4],5]

✏️: 因为字符串有解构器,所以可以解析出来

四、块级作用域

JS中的两种作用域是:全局作用域和函数作用域
我们知道,在JS中所有通过var声明的变量都会提升到当前作用域的最前面,也就是上面提到的var所实现的变量提升。如下👇

function funC(){
  console.log(temp)
  var temp
}

正因为变量提升的存在,使得JS本身并不像类C语言一样支持块状作用域,根据我们的编码习惯,我们并不喜欢变量提升机制,而是鞥希望代码能按照顺序执行。因此神奇的ES6引入了块级作用域,并且采用了let、const来支持块级作用域。

优势: 在循环中使用let来代替var可以在循环结束的时候销毁变量,避免无用的变量影响全局,安全明了,利用块级作用域可以实现累加器

Tips: 在ES6中最好使用函数表达式来表示一个函数,ES6中允许函数在块级作用域中可以声明的条件必须是在大括号里面,否则会报错

//函数声明报错
{
  if(2 < 4){
    function fn(){
      console.log('I’m here!')
    }
  }
}
fn()  // Uncaught TypeError: fn is not a function
//正确的函数声明
{
  let fa = '111'
  let fn = function(){
    console.log('I’m here!')
  }
  console.log(fa, fn) // 111 f(){ console.log('I’m here!')}
}

使用块级绑定的最佳实践是:默认使用 const ,只在确定需要改变变量的值时,使用 let ,可以确保基本层次的不可变性,以最大化地避免错误的产生。

五、箭头函数 =>

在ES6中引入了箭头函数 (MDN 箭头函数)let func = value => value+1

1.箭头函数与普通函数的区别:

  • ❌箭头函数没有this,需要查找作用域链来确定this的值?
    (如果它被非箭头函数包含,则this绑定的就是最近一层的非箭头函数的this), 箭头函数this是父作用域的this,不是调用时的this
  • ❌箭头函数没有自己的arguments对象,但是可以访问外围函数的arguments对象,也没有caller,calee
  • ❌箭头函数不能作为构造函数,不能通过new关键词调用,同样也没有new.target值和原型属性
  • ❌箭头函数通过callapply来调用,不会改变this的指向,只会传入参数
  • ❌箭头函数不能作为Generator函数,不能使用yield关键字

此外还有一些特性:

  • 箭头函数返回对象时,要加一个小括号var func = () =>({ foo: 1 })
  • 箭头函数在ES6 class中声明的方法为实例方法,不是原型方法
  • 多重箭头函数就是高阶函数,相当于内嵌函数

提到箭头函数和this,还有一个非常重要的问题必须有姓名 this的绑定方式

2.this的四种绑定方式:

  • 默认绑定:全局环境中,this默认绑定到window对象上
  • 隐式绑定:被直接对象所包含的函数调用(方法调用)时,this隐式绑定到该直接对象上(隐式丢失是指被隐式绑定的函数丢失绑定对象,默认绑定到window)
  • 显式绑定:通过call(),apply(),bind()方法将对象绑定到this上
  • new绑定:如果函数或方法调用之前带有关键字new,就构成构造函数调用。
    构造函数绑定

关于this绑定,这一篇就不再过多赘述,想看详细的this绑定可以读一下👉this的绑定方式总结

六、扩展功能

ES6语法可以更好地支持扩展功能,包含数组、对象以及函数的扩展,此外ES6中出现了延展符…,既可以用来定义数组,还能扩展数组、对象,函数

一、数组的扩展

1.Array.from——实现非数组list的转化

let list = document.querySelectorAll("li")
console.log(Array.isArray(list)) // false
let newList = Array.from(list)
console.log(Array.isArray(newList)) // true

2.Array.of——零散元素转化为数组
转化方式:Array.of( 1, 3, 7, 9)

3. 延展符…

// 用延展符来定义数组
let str = "Kole";
let strArr =[...str];
console.log(Array.isArray(strArr));  //true

二、对象的扩展

1.ES6中key和value一样时,定义对象时只需要写一个就可以

let name = 'Peter'
let age = 19
let obj = {
  name,
  age
}
console.log(obj) // {name:'Peter', age:19}

2.assign的用法

let obj1 = {name: "Lisa"}
let obj2 = {age: 37}
let obj = {}
Object.assign(obj, obj1, obj2) // 将obj1,obj2合并到obj上
console.log(obj) // {name:"Lisa", age:37}

3. 将Set对象转为Array

主要用于数据传递数组去重,因为Set具有唯一性✔️

let myArr = [1, 2, 10, "ZS", 20, 2, 1]
console.log(new Set(myArr)) // 根据set唯一性去重复
let newArr = [...new Set(myArr)] // 将set转化为array
console.log(Array.isArray(newArr)) // true

三、函数的扩展

1. 在ES6中,形参可以设置默认值
let func = function(a = 1, b = 2){ return a+b }
2. 参数形式:可以用延展操作符…代替
🅰️可变参数…a, 在不清楚元素个数的时候可以表示如此
🅱️可以使用扩展运算符…来合并数组,如b = [10,19], a = [1,2...b]

// 在不清楚元素个数时,可以使用延展操作符代替
function sum (name, ...nums){
  console.log(name)
  let res = 0
  for(let value of nums){
    res += value
  }
  return res
}
console.log(sum("Juilian",2,34,6,19)) // Juilian 61

七、对象代理——Proxy

ES6中新添加了对象代理Proxy
Proxy是什么❓ MDN Proxy
代理Proxy是一个构造函数(target, handler),返回一个代理对象Proxy,主要用于从外部控制对对象内部的访问
本质: 元编程非破坏性数据劫持。在原对象的基础上进行功能的衍生而且又不影响原对象,符合高内聚低耦合的设计理念

  • target接收Proxy()转发的所有内部方法
  • handler可以覆写(overwrite)任意代理的内部方法。
    总之,调用Proxy的方法就会调用target上对应的方法,外界每次通过Proxy访问target对象的属性时,都会经过handler对象

总之,Proxy就是在数据外层套了一个壳,然后通过这个壳访问内部数据

1. Proxy的作用:
对对象进行拦截、实现对数据读取、修改的过滤保护

语法 对象操作
ES3 设局部变量,采用闭包方式的set和get方法
ES5 采用对象下面的Object.defineProperty() 对属性描述符进行操作
ES6 使用Proxy代理对象,设计与实现具备set和get方法的新对象

2. Proxy使用方法:
第一个参数:要拦截的对象,第二个参数:对象(内部有set和get)

get()——对象读数据的时候调用,两个参数:target(目标数据)和key(所读数据的key)
set()——对象设置数据时触发,内部三个形参:target, key 和 value

用法示例:

var Person = {
  name:'Lily',
  age: 17,
  sex: 'female'
}
var p = new Proxy(Person,{
  get(target, key){
    return target[key]
  }
  set(target, key, value){
    if(key !== 'sex'){ // 设置Person对象的sex属性不可修改
      target[key] = value
    }
  }
})
console.log(p.name, p.age, p.sex) // Lily,17,female
// 重新设置对象属性
p.name = "Lisa"
p.age = 25
p.sex = 'male' // Person对象中的sex属性不可以设置
console.log(p.name, p.age, p.sex) // Lisa,25,female

不仅是set和get, Proxy有13种数据劫持的操作:
Proxy中的处理方法

3.Proxy的应用

  • 封装高阶函数,为函数添加特定的功能
  • 代理对象的访问
  • 作为胶水桥接不同结构的对象
  • 监视对象的变化
  • Proxy对象可以拦截目标对象的任意属性,这使得它很适合来编写web服务的客户端
  • Proxy可以用来实现数据层的ORM(Object-Relationship-Model)层
  • 在Vue 3.0中使用了Proxy去实现部分核心代码
  • 在业务开发时应该注意Proxy使用场景,当对象的功能变得复杂或者我们需要进行一定的访问限制时,便可以考虑使用代理。

4. Proxy的优劣势
在ES5中,虽然使用Object.defineProperty()实现属性已经非常的简单,但是在设置对象属性为不可写属性的时候,修改并不成功。相对于ES3和ES5,ES6中使用Proxy是非常便利的,因为可以在set和get里面做出更多的逻辑处理
优势:

  • Proxy让开发者很方便的使用代理模式,使函数更加强大,业务逻辑更加清楚
  • 不但可以取代Object.defineProperty(),并且还扩增了非常多的功能
  • Proxy技术支持检测数组的push等方法操作,支持对象属性的动态添加和删除,极大地简化了响应化的代码量

缺点:

  • 运行环境必须支持ES6
  • 兼容性一般,(不过谷歌开发的proxy-polyfill目前已经支持get、set、apply、construct 到ie9了)
  • 不能直接代理一些需要this的对象,代理之后行为可能发生变化,可能需要手动绑定this
  • 性能比较差,但是因浏览器而异,proxy绝对不适合用在性能关键点的代码上,当然,也可以衡量proxy带来的遍历和可能损耗的性能,进行合理的中和,来达到最佳的开发体验和用户体验

目前浏览器没有办法判断对象是否被代理,但是在node版本10以上,可以使用util.types.isProxy来判断

八、其他

  1. 除此之外,ES6中还新增了class语法糖🍭
    作用: 让对象原型的写法更加清晰,更像面向对象的编程方式,也是构造函数的另一种写法。
//ES6之前的构造函数写法
function Person(name, age){
  this.name = name
  this.age = age
}
Person.prototype = {
  constructor: Person,
  print(){
    console.log('Hi! My name is '+this.name,',and I\'m ' + this.age + ' years old.')
  }
}
// class面向对象写法
class Person{
  constructor(name,age){
    this.name = name
    this.age = age
  }
  print(){
    console.log('Hi! My name is '+this.name,',and I\'m ' + this.age + ' years old.')
  }
}
// 定义和调用类方法
let person = new Person("Rose",24)
person.print() // Hi! My name is Rose,and I'm 24 years old
  1. ES6中也支持模板字符串 🍡
    作用: 使用 `` 符号定义模板,在里面使用${}的形式调用变量,就可以实现代码的格式化,类似render()函数, 在 Vue 中常用
let name = "dance";
let content= "Coincidance"
let html = `              
              You can really Dance!
              
                

${content}

` //这里使用${}的形式调用变量 console.log(html);

ES6语法带来了许多新的特性,目前也已经有非常成熟的使用,学习ES6语法可以帮助我们更好地了解JS😄

最后,附上一个ES6的特性小字典,来源于IQ前端的原创文章配图:
ES6特性梳理图


Author: Casey Lu
Reprint policy: All articles in this blog are used except for special statements CC BY 4.0 reprint polocy. If reproduced, please indicate source Casey Lu !
评论
 Previous
JavaScript笔试刷题小结(二) JavaScript笔试刷题小结(二)
继续整理最近的刷题记录 1. 剑指Offer 39. 数组中出现次数过半的数字 书中提到了两种复杂度均为O(n)的解法: 1)基于Partition函数: 如果这个数字存在,那它必然是数组排序后的中位数,基于随机快排中的Partition的
2020-03-10
Next 
CBU技术部面试总结(持续更新) CBU技术部面试总结(持续更新)
一、在线测评 时间: 03月05日 7:30pm-9:30pm 内容: 在上一次的《JS笔试刷题小结》中已经提到了,不再过多说明 二、一面 时间: 03月07日 8:30pm-9:00pm 题外话: 当晚事发突然,在完全没有约时间的情况下,
2020-03-08
  TOC