文章目录
- 一、前言
- 二、ES6的相关语法
- 1、let、const的用法
- 2、数组和对象的解构
- 3、模板字符串
- 4、函数的默认参数和剩余参数
- 5、箭头函数
- 6、展开语法
- 7、Symbol的使用
- 8、Set和WeakSet
- 9、Map和WeakMap
- 三、结语
一、前言
JavaScript这门语言在设计之初,其实会存在某些问题,随着JavaScript这门语言越来越流行, ECMA国际标准化组织在ES5的基础上,推出了许多新特性以补充之前不足的地方,使得JavaScript这门语言越来越完善,那么今天我就将讲述一些比较常用的ES6的新特性,(本文主要大致介绍一下有关语法,Promise,ESModule等内容需要较长篇幅,以后会单独写文章介绍)一起来看看吧。
看完本文,你将:
1、你将会了解ES6的常用的语法
2、你将会了解这些语法的常用使用规则
一起来看看吧!
二、ES6的相关语法
1、let、const的用法
在ES6之前是没有块级作用域的,而在ES5只有两个东西会形成作用域:1、全局作用域,2、函数作用域,ES6中的代码块级作用域,对let、const、function、class申明的类型是有效的。另外if、for、switch语句的代码是块级作用域。
let:
实例:加入页面上有4个button按钮
const btns = document.getElementsByTagName('button')
for (var i = 0; i < btns.length; i++) {
btns[i].onclick = function() {
console.log("第" + i + "个按钮被点击")//第4个按钮被点击
}
}
console.log(i)//4
我们可以看到,不管点击哪个按钮,输出的都是“第4个按钮被点击”,为什么会出现这种情况呢?
其中的 i 变量不在函数自身的活动对象中,因此需要向上查找,而此时for循环已经结束,因此访问到的 i 变量值均是4。
如果var 改成let:
const btns = document.getElementsByTagName('button')
for (let i = 0; i < btns.length; i++) {
btns[i].onclick = function() {
console.log("第" + i + "个按钮被点击")
}
}
则点击第一个会输出:第0个按钮被点击,点击第二个会输出:第1个按钮被点击等,达到了我们想要的效果。为什么会这样呢?
首先,let 声明的变量只在声明的{}内有效,所以function () { console.log(“第” + i + “个按钮被点击”)} 在创建时,每次都要声明一个 i 变量,也就是说这个 i 和上一次循环的 i 本质上是不同的变量,每循环一次,就会在内存中创建一个内存地址保存这次i的值,每次循环的i值是不同的变量。而之前采用var声明的时候,由于 i 变量提升,每次的循环其实就是用同一个变量来进行每次加1的操作。
const:
const本质上是传递的值不可以修改
但是如果传递的是一个引用类型(内存地址), 可以通过引用找到对应的对象, 去修改对象内部的属性, 这个是可以的
const obj = {
foo: "foo"
}
obj = {}
obj.foo = "aaa"
console.log(obj.foo)//aaa
2、数组和对象的解构
对数组的解构:
var names = ["a", "b", "n"]
// var item1 = names[0]
// var item2 = names[1]
// var item3 = names[2]
// 对数组的解构: []
var [item1, item2, item3] = names
console.log(item1, item2, item3)
// 解构后面的元素
var [, , itemz] = names
console.log(itemz)
// 解构出一个元素,后面的元素放到一个新数组中
var [itemx, ...newNames] = names
console.log(itemx, newNames)
// 解构的默认值
var [itema, itemb, itemc, itemd = "aaa"] = names
console.log(itemd)
对对象的解构:
var obj = {
name: "why",
age: 18,
height: 1.88
}
// 对象的解构: {}
var { name, age, height } = obj
console.log(name, age, height)//why 18 1.88
var { age } = obj
console.log(age)//18
var { name: newName } = obj
console.log(newName)//why
//添加默认值
var { address: newAddress = "广州市" } = obj
console.log(newAddress)
function foo(info) {
console.log(info.name, info.age)
}
foo(obj)//why 18
function bar({name, age}) {
console.log(name, age)
}
bar(obj)//why 18
3、模板字符串
模板字符串最外层用``嵌套,l=当引用变量时,用${变量名}代替,里面可以写变量,可以对变量进行基本的算术运算,也可以写函数的执行结果。实例如下:
// ES6之前拼接字符串和其他标识符
const name = "why"
const age = 18
const height = 1.88
// console.log("my name is " + name + ", age is " + age + ", height is " + height)
// ES6提供模板字符串 ``
const message = `my name is ${name}, age is ${age}, height is ${height}`
console.log(message)
const info = `age double is ${age * 2}`
console.log(info)
function doubleAge() {
return age * 2
}
const info2 = `double age is ${doubleAge()}`
console.log(info2)
另外补充一点,调用函数的方式: 标签模块字符串:
// 第一个参数依然是模块字符串中整个字符串, 只是被切成多块,放到了一个数组中
// 第二个参数是模块字符串中, 第一个 ${}
function foo(m, n, x) {
console.log(m, n, x, '---------')
}
// foo("Hello", "World")
// 另外调用函数的方式: 标签模块字符串
// foo``
// foo`Hello World`
const name = "why"
const age = 18
// ['Hello', 'Wo', 'rld']
foo`Hello${name}Wo${age}rld`//[ 'Hello', 'Wo', 'rld' ] why 18 ---------
有了模板字符串后,再也不用担心用+加双引号拼接字符串了,是不是很方便呢!
4、函数的默认参数和剩余参数
函数的默认参数:
function foo(m = "aaa", n = "bbb") {
console.log(m, n)
}
直接在函数参数那里设置即可,如果参数是undefined,便会使用默认参数。设置了默认参数后,函数的length会变
function baz(x, y, z, m, n = 30) {
console.log(x, y, z, m, n)
}console.log(baz.length)
只会输出默认参数之前的参数数量,例如本例中,输出4。
函数的剩余参数:
在函数传参数的括号里面使用,只能在括号里面末尾使用,会记录参数除前面的全部参数,以数组方式存储在定义的变量中,看一下例子:
function foo(m, n, ...args) {
console.log(m, n) //20 30
console.log(args) //[ 40, 50, 60 ]
console.log(arguments) //{ '0': 20, '1': 30, '2': 40, '3': 50, '4': 60 }
}
foo(20, 30, 40, 50, 60)
5、箭头函数
格式为()=>{}。箭头函数内部无this和arguments,输出时的值是函数外层的值。箭头函数也没有prototype。
// function foo() {
// }
// console.log(foo.prototype)
// const f = new foo()
// f.__proto__ = foo.prototype
var bar = () => {
console.log(this, arguments)
}
console.log(bar.prototype)
// bar is not a constructor
const b = new bar()
6、展开语法
使用…对数组或者对象进行展开,相当于去掉外层的数组符号或者对象符号,具体用法看下例:
const names = ["abc", "cba", "nba"]
const name = "why"
const info = {name: "why", age: 18}
// 1.函数调用时
function foo(x, y, z) {
console.log(x, y, z)
}
// foo.apply(null, names)
foo(...names)//abc cba nba
foo(...name)//w h y
// 2.构造数组时
const newNames = [...names, ...name]
console.log(newNames)//[ 'abc', 'cba', 'nba', 'w', 'h', 'y' ]
// 3.构建对象字面量时ES2018(ES9)
const obj = { ...info, address: "广州市", ...names }
console.log(obj)
// {
// '0': 'abc',
// '1': 'cba',
// '2': 'nba',
// name: 'why',
// age: 18,
// address: '广州市'
// }
补充一点:展开语法进行的是浅拷贝:
对象里面的对象,拷贝的是这个内部对象的地址。而不是新建一块空间存储这个对象。
const info = {
name: "why",
friend: { name: "kobe" }
}
const obj = { ...info, name: "coderwhy" }
// console.log(obj)
obj.friend.name = "james"
console.log(info.friend.name)//james
7、Symbol的使用
symbol的实例是唯一的不可变的, 用于确保对象的属性不重复
具体用法以及实例见下:
// 1.ES6之前, 对象的属性名(key)
// var obj = {
// name: "why",
// friend: { name: "kobe" },
// age: 18
// }
// obj['newName'] = "james"
// console.log(obj)
// 2.ES6中Symbol的基本使用
const s1 = Symbol()
const s2 = Symbol()
console.log(s1 === s2)//false
// ES2019(ES10)中, Symbol还有一个描述(description)
const s3 = Symbol("aaa")
console.log(s3.description)//aaa
// 3.Symbol值作为key
// 3.1.在定义对象字面量时使用
const obj = {
[s1]: "abc",
[s2]: "cba"
}
// 3.2.新增属性
obj[s3] = "nba"
// 3.3.Object.defineProperty方式
const s4 = Symbol()
Object.defineProperty(obj, s4, {
enumerable: true,
configurable: true,
writable: true,
value: "mba"
})
console.log(obj[s1], obj[s2], obj[s3], obj[s4])//abc cba nba mba
// 注意: 不能通过.语法获取
// console.log(obj.s1)
// 4.使用Symbol作为key的属性名,在遍历/Object.keys等中是获取不到这些Symbol值
// 需要Object.getOwnPropertySymbols来获取所有Symbol的key
console.log(Object.keys(obj))//[]
console.log(Object.getOwnPropertyNames(obj))//[]
console.log(Object.getOwnPropertySymbols(obj))//[ Symbol(), Symbol(), Symbol(aaa), Symbol() ]
const sKeys = Object.getOwnPropertySymbols(obj)
for (const sKey of sKeys) {
console.log(obj[sKey])
}
// abc
// cba
// nba
// mba
// 5.Symbol.for(key)/Symbol.keyFor(symbol)
const sa = Symbol.for("aaa")
const sb = Symbol.for("aaa")
console.log(sa === sb)//true
//获取key的值
const key = Symbol.keyFor(sa)
console.log(key)//aaa
const sc = Symbol.for(key)
console.log(sa === sc)//true如果key也相等,则两数相等
8、Set和WeakSet
Set是ES6新增的数据结构:
创建Set结构:
const set = new Set()
set.add(10)
set.add(20)
set.add(40)
set.add(333)
set.add(10)
// 2.添加对象时特别注意:
set.add({})
set.add({}) console.log(set)
对数组进行去重:
const arr = [33, 10, 26, 30, 33, 26]
const arrSet = new Set(arr)
const newArr = [...arrSet]
console.log(newArr)
size属性输出Set的key数量。
Set常用的方法:
- add(value):添加某个元素,返回Set对象本身;
- delete(value):从set中删除和这个值相等的元素,返回boolean类型;
- has(value):判断set中是否存在某个元素,返回boolean类型;
- clear():清空set中所有的元素,没有返回值;
- forEach(callback, [, thisArg]):通过forEach遍历set;
// 5.Set的方法
// add
arrSet.add(100) //Set(5) { 33, 10, 26, 30, 100 }
console.log(arrSet)
// delete
arrSet.delete(33)
console.log(arrSet) //Set(4) { 10, 26, 30, 100 }
// has
console.log(arrSet.has(100))
// clear
// arrSet.clear()
console.log(arrSet)
// 6.对Set进行遍历
arrSet.forEach(item => {
console.log(item)
})
for (const item of arrSet) {
console.log(item)
}
WeakSet的使用:
- 1.区别一: 只能存放对象类型
- 2.区别二: 对对象是一个弱引用
WeakSet常见的方法:
- add(value):添加某个元素,返回WeakSet对象本身;
- delete(value):从WeakSet中删除和这个值相等的元素,返回boolean类型;
- has(value):判断WeakSet中是否存在某个元素,返回boolean类型
–注意:WeakSet不能遍历
const weakSet = new WeakSet()
let obj = {
name: "why"
}
const set = new Set()
// 建立的是强引用
//set.add(obj)
// 建立的是弱引用
weakSet.add(obj)
对弱引用的理解:通常情况下,如果有多个变量指向同一个对象,本例中,有多个变量指向这个对象,在强引用情况下,如果obj赋值为null,即obj不指向这个对象了,那么还有set对象里面的某个变量指向这个对象,这个对象并不会被内存回收。但是,在弱引用情况下,如本例中的weakSet.add(obj),set对象由于对这个obj对象建立的是弱引用,且没有其他强引用指向obj,所以内存会回收obj这个对象,也就是说内存中的垃圾清除策略对弱引用的存在是可有可无,可以忽略的。
WeakSet的引用场景:
强制类里面的方法只能通过构造方法构建
const personSet = new WeakSet()
class Person {
constructor() {
personSet.add(this)
}
running() {
if (!personSet.has(this)) {
throw new Error("不能通过非构造方法创建出来的对象调用running方法")
}
console.log("running~", this)
}
}
let p = new Person()
p.running()
p.running.call({name: "why"})//Error: 不能通过非构造方法创建出来的对象调用running方法
9、Map和WeakMap
JavaScript中对象中是不能使用对象来作为key的,ES6新增了一种数据结构Map,可以使用对象类型作为key
Map:
Map的构建和加入元素:
const obj1 = { name: "w" }
const obj2 = { name: "e" }
const map = new Map()
map.set(obj1, "aaa")
map.set(obj2, "bbb")
map.set(1, "ccc")
console.log(map)
//Map(3) {
// { name: 'w' } => 'aaa',
// { name: 'e' } => 'bbb',
// 1 => 'ccc'
// }
Map常见的方法:
- set(key, value):在Map中添加key、value,并且返回整个Map对象;
- get(key):根据key获取Map中的value;
- has(key):判断是否包括某一个key,返回Boolean类型;
- delete(key):根据key删除一个键值对,返回Boolean类型;
- clear():清空所有的元素;
- forEach(callback, [, thisArg]):通过forEach遍历Map
const map2 = new Map([[obj1, "aaa"], [obj2, "bbb"], [2, "ddd"]])
console.log(map2)
console.log(map2.size)
// set设置属性和值
map2.set("why", "eee")
console.log(map2)
// get(key)获取属性的值
console.log(map2.get("why"))
// has(key)判断是否有属性
console.log(map2.has("why"))
// delete(key)删除某个属性以及值
map2.delete("why")
console.log(map2)
// clear清空Map
// map2.clear()
// console.log(map2)
// 4.遍历map
map2.forEach((item, key) => {
console.log(item, key)
})
for (const item of map2) {
console.log(item[0], item[1])
}
for (const [key, value] of map2) {
console.log(key, value)
}
WeakMap:
- 区别一: WeakMap不能使用基本数据类型,WeakMap的key只能使用对象,Set可以使用数字、字符串等基本数据类型
- 区别二:WeakMap的key对对象的引用是弱引用,如果没有其他引用引用这个对象,那么GC可以回收该对象
WeakMap常见的方法有四个:
- set(key, value):在Map中添加key、value,并且返回整个Map对象;
- get(key):根据key获取Map中的value;
- has(key):判断是否包括某一个key,返回Boolean类型;
- delete(key):根据key删除一个键值对,返回Boolean类型
- -注意: WeakMap也是不能遍历的
const obj = {name: "obj1"}
const weakMap = new WeakMap()
weakMap.set(obj, "aaa")
// get方法
console.log(weakMap.get(obj))
// has方法
console.log(weakMap.has(obj))
// delete方法
console.log(weakMap.delete(obj))
// WeakMap { <items unknown> }
console.log(weakMap)
三、结语
关于ES6的相关常用语法就介绍到这里了,想了解剩下的ES7~12的相关语法,请移步主页看下篇,谢谢