ES6--ES11
- ES6
- 1. let 关键字
- 1.1 声明变量
- 1.2 块级作用域
- 1.3 不存在变量提升
- 1.4 不影响作用域链
- 1.5 案例实践 -- 点击 DIV 换色
- 2. const 关键字
- 2.1 声明常量
- 2.2 不允许重复声明
- 2.3 值不允许修改
- 2.4 块儿级作用域
- 2.5 对于数组和对象的元素修改, 不算做对常量的修改
- 3. 变量的解构赋值
- 3.1 数组的结构
- 3.2 对象的解构
- 4. 模板字符串
- 4.1 模板字符串声明
- 4.2 字符串中可以出现换行符
- 4.3 可以使用 ${xxx} 形式输出变量
- 5. 简化对象写法
- 6. 箭头函数
- 6.1 箭头函数 this 指向声明时所在作用域下 this 的值
- 6.2 箭头函数不能作为构造实例化对象
- 6.2.1 函数this指向
- 6.3 不能使用 arguments 变量
- 6.4 箭头函数的简写
- 6.4.1 如果形参只有一个,则小括号可以省略
- 6.4.2 函数体如果只有一条语句,则花括号可以省略
- 6.5 箭头函数实践
- 6.5.1 点击 div 2s 后颜色变成『粉色』
- 6.6 箭头函数小结
- 7. 函数参数默认值
- 7.1 形参初始值
- 7.2 与解构赋值结合
- 8. rest 参数
- 8.1 rest 参数必须要放到参数最后
- 9. spread 扩展运算符
- 9.1 数组的合并
- 9.2 数组的克隆
- 9.3 将伪数组转为真正的数组
- 10. Symbol
- 10.1 创建Symbol
- 10.2 不能与其他数据进行运算
- 10.3 Symbol 创建对象属性
- 10.4 Symbol 内置值
let 关键字用来声明变量,使用 let 声明的变量有几个特点:
// 可以使用let声明单个变量
let a;
// 可以在声明变量的同时赋值
let b = 100;
// 可以一次同时声明多个变量
let c,d,e;
// 可以声明多个变量同时赋值
let f = 'abc', g = 123, h = {}, i = [];
// 变量不能重复声明
// let a = 100;
var 可以重复声明变量:
var j = 100;
var j = [];
console.log(j)
{
let age = 12;
}
console.log(age)
var 不具有块级作用域
{
var username = 'zs'
}
console.log(username)
var 声明变量,相当于在最前面声明一个变量。
console.log(myage)
var myage = 20
上面代码相当于:
var myage
console.log(myage)
myage = 20
由于声明了没有赋值,所以输出为undefined
而 let 声明变量不会存在变量提升,会直接报错
console.log(myage)
let myage = 20
{
let myname = '张三'
{
// 在该作用域下没有 myname,会向上一级作用域寻找 myname
// 找到输出
console.log(myname)
// 没有找到报错
console.log(myage)
}
}
使用 var 声明变量 i ,并且使用 items[i] 修改样式会报错
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Documenttitle>
<style>
.item {
width: 100px;
height: 50px;
border: solid 1px rgb(42, 156, 156);
float: left;
margin-right: 10px;
}
style>
head>
<body>
<div>
<div class="item">div>
<div class="item">div>
<div class="item">div>
div>
<script>
// 获取元素
let items = document.querySelectorAll('.item')
// console.log(items)
// 给元素绑定事件
for( var i=0; i<items.length; i++ ) {
items[i].onclick = function() {
//修改当前元素的背景颜色
// 使用 items[i] 进行修改
items[i].style.background = 'pink'
}
}
script>
body>
html>
原因使用 var 声明 i 相当于在全局声明了一个变量 i 每次 i++,相当于对全局的 i ++,事件绑定完成后 i 为3,当点击元素触发事件会导致数组下标越界。
使用 let 则不会,因为 let 存在块级作用域
// 获取元素
let items = document.querySelectorAll('.item')
// console.log(items)
// 给元素绑定事件
for( let i=0; i<items.length; i++ ) {
items[i].onclick = function() {
//修改当前元素的背景颜色
// 使用 items[i] 进行修改
items[i].style.background = 'pink'
}
}
const 关键字用来声明常量,const 声明有以下特点
// 声明常量
// 声明必须赋初始值
// 标识符一般为大写(潜规则), 不大写也不报错, 只是常量习惯性大写
const PI = 3.14
console.log(3.14)
声明常量不赋初始值会报错
const A
const PI = 3.14
const PI = 3.1415
const PI = 3.14
PI = 3.1415
const PI = 3.14
{
// 在该作用域下没有声明PI, 会向上一级作用域寻找
console.log(PI)
}
数组和对象变量中保存的为数组和对象的地址,改变对象和数组中的元素没有修改其地址,所以相当于没有修改数组和对象变量,不会报错
const ARR = ['a', 1, 2, 3, 45]
ARR[0] = 0
console.log(ARR)
声明对象类型使用 const,非对象类型声明选择 let
ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构赋值。
const arr = ['apple', 'banner', 'orange']
// 相当于声明了三个变量 fruit1, fruit2, fruit3
// 将数组 arr 中的值按下标顺序赋值给三个变量
// 数组的解构使用 []
let [fruit1, fruit2, fruit3] = arr
console.log(fruit1)
console.log(fruit2)
console.log(fruit3)
const person = {
name: 'zs',
age: 13,
sayHello: function() {
console.log('Hello!')
}
}
// 对象的解构是按照对象中的属性名将属性值赋值给相同名对应的变量
let {name, age, sayHello} = person
console.log(name)
console.log(age)
sayHello()
// 对象解构赋值的用处
// person.sayHello() 麻烦
// 解构出来,直接调用,不用每次写对象名
sayHello()
// 对象解构可以单独解构出其中的属性
let {sayHello} = person
sayHello()
频繁使用对象方法、数组元素,就可以使用解构赋值形式
模板字符串(template string)是增强版的字符串,用反引号( ` )(键盘esc键下面)标识,特点:
let str = `声明了一个模板字符串`
console.log(str)
let str = `
`
console.log(str)
let name = 'zs'
let age = 13
let str = `我的名字是${name}, 我的年龄为${age}`
console.log(str)
当遇到字符串与变量拼接的情况使用模板字符串
ES6 允许在大括号里面,直接写入变量和函数,作为对象的属性和方法。这样的书写更加简洁。
let name = 'zs'
let age = 12
let sayHello = function() {
console.log('你好!')
}
let person1 = {
name,
age,
sayHello,
sayHello2() {
console.log('hello!')
}
}
// 相当于
// let person2 = {
// name: name,
// age: age,
// sayHello: sayHello,
// sayHello2: function() {
// console.log('hello!')
// }
// }
console.log(person1.name)
console.log(person1.age)
person1.sayHello()
person1.sayHello2()
ES6 允许使用箭头(=>)定义函数。
// 定义一个普通的函数
let f1 = function() {
console.log('hello')
}
// 定义箭头函数
let f2 = () => {
console.log('world')
}
f1()
f2()
箭头函数的 this 是静态的. this 始终指向函数声明时所在作用域下的 this 的值
function getThis1() {
console.log(this)
}
let getThis2 = () => {
console.log(this)
}
// 直接调用
getThis1()
getThis2()
function getThis1() {
console.log(this.name)
}
let getThis2 = () => {
console.log(this.name)
}
window.name = 'window'
const NAME = {
name: 'NEW_NAME'
}
// 使用call方法改变this指向
getThis1.call(NAME)
getThis2.call(NAME)
let Person = (name, age) => {
this.name = name
this.age = age
}
let p = new Person('zs', 12)
console.log(p)
函数this的指向在函数定义的时候是不能确定的,在调用执行函数时才能明确函数this的指向。
函数名()
调用函数,this指向window对象名.函数()
this指向调用这个函数的对象 // 普通函数可以使用 arguments 变量获取传入的参数值
let fn1 = function() {
console.log(arguments)
}
// 箭头函数不能使用 arguments 变量获取传入的参数值
let fn2 = () => {
console.log(arguments)
}
fn1(1,2,3)
fn2(1,2,3)
let fn3 = n => {
console.log(n)
}
fn3(100)
函数体如果只有一条语句,则花括号可以省略,函数的返回值为该条语句的
执行结果
let fn4 = n => console.log(n)
fn4(100)
let fn5 = n => n*n
// 相当于:
// let fn5 = n => {
// return n*n
// }
let res = fn5(100)
console.log(res)
普通函数实现:
// 获取元素
let div = document.querySelector('div')
// 绑定事件
div.addEventListener( 'click', function() {
// 回调函数使用普通函数
// 由于定时器函数的this指向window
// 需要保存this,事件处理函数指向触发事件的元素
let _this = this
setTimeout( function() {
_this.style.background = 'pink'
}, 2000 )
} )
箭头函数实现:
// 获取元素
let div = document.querySelector('div')
// 绑定事件
div.addEventListener('click', function () {
// // 回调函数使用普通函数
// // 由于定时器函数的this指向window
// // 需要保存this,事件处理函数指向触发事件的元素
// let _this = this
// setTimeout(function () {
// _this.style.background = 'pink'
// }, 2000)
// 箭头函数实现
// 由于箭头函数this指向定义函数作用域下的this,所以箭头函数this指向触发事件的元素
setTimeout(() => {
this.style.background = 'pink'
}, 2000)
})
ES6 允许给函数参数赋值初始值
// 具有默认值的参数, 一般位置要靠后
let f1 = function( a, b, c=10 ) {
console.log(a+b+c)
}
// 没有给有默认值的参数传值时,参数的值为默认值
f1(1,2,3)
f1(1,2)
// 有默认值的参数放前面,当传入的参数个数不等于参数的总数时会出现错误
// 一般位置要靠后
let f2 = function( a, b=12, c ) {
console.log(a+b+c)
}
// 1 会赋值给 a
// 2 会赋值给 b
// c 没有传入值,三个数相加会出现错误 NaN
// c 为undefined
f2(1,2)
function connect({host="127.0.0.1", username,password, port}){
console.log(host)
console.log(username)
console.log(password)
console.log(port)
}
connect({
username: 'root',
password: 'root',
port: 3306
})
将 对象中的值解构 出来赋值给对应的变量,username: ‘root’ 对应 username,… ,由于对象中没有属性 host ,所以 host 会使用默认值
ES6 引入 rest 参数,用于获取函数的实参,用来代替 arguments
let f1 = function (...args) {
// rest 参数 会将传入的多个参数转为一个数组
// 转为数组后可以使用数组的方法 filter some every map ...
console.log(args)
}
f1(1, 2, 3)
// 箭头函数也可以使用 rest 参数
let f2 = (...args) => {
console.log(args)
}
f2(4, 5, 6)
rest 参数相当于一个参数接收多个参数值转成一个数组,如果不放在最后那么相当于前的参数值都被rest 参数获取,后面的参数将不会有参数传入,所以rest 参数必须放在最后。
let f3 = (a, ...args, b)=> {
console.log(a)
console.log(args)
console.log(b)
}
f3(1,2,3,4,5,6,7)
rest 参数非常适合不定个数参数函数的场景
扩展运算符(spread)也是三个点(…)。它好比 rest 参数的逆运算,将一个数组转为用逗号分隔的参数序列,对数组进行解包。
const arr = [1,2,3,4,5]
console.log(arr)
console.log(...arr)
先将两个或多个数组进行拓展然后再组合成一个数组
const arr1 = [1,2,3,4,5]
const arr2 = [6,7,8,9,10]
const newArr = [...arr1, ...arr2]
console.log(newArr)
使用该方法进行数组的克隆为浅拷贝
const arr1 = [1,2,3,4,5]
const arr2 = [...arr1]
console.log(arr2)
<div>div>
<div>div>
<div>div>
const divs = document.querySelectorAll('div')
console.log(divs)
const divArr = [...divs]
console.log(divArr)
ES6 引入了一种新的原始数据类型 Symbol,表示独一无二的值。它是JavaScript 语言的第七种数据类型,是一种类似于字符串的数据类型。
Symbol 特点
let s1 = Symbol()
console.log(s1, typeof s1)
// 使用 Symbol() 创建的 Symbol 每次都不一样
let s2 = Symbol('hello') // 'hello'只是一个对于 Symbol 的描述,类似对Symbol的注释
let s3 = Symbol('hello')
console.log(s2 === s3)
// Symbol.for 创建
// 第一次创建会直接创建一个新的 Symbol
// 第二次创建会先查看是否已经传建了,已经有了则不在创建,没有就创建
let s4 = Symbol.for('hello')
let s5 = Symbol.for('hello')
console.log(s4 === s5)
Symbol.for()与Symbol()的区别:都会生成新的Symbol,但是调用多次的Symbol()会生成多个不同的Symbol,调用多次的Symbol.for()只会生成一个Symbol。
let result = s + 100
let result = s > 100
let result = s + s
let game = {
name: '俄罗斯方块',
up: function () {},
down: function () {},
}
假设不知道该对象里面有什么属性和方法,向其中添加 up 和 down 方法,但是把确定其中是否有 up 和 down 方法,此时可以使用 Symbol
// 声明一个对象
let methods = {
up: Symbol(),
down: Symbol()
};
// 向对象中添加方法
game[methods.up] = function(){
console.log("我可以改变形状");
}
game[methods.down] = function(){
console.log("我可以快速下降!!");
}
// 调用
game[methods.up]()
game[methods.down]()
console.log(game);
let youxi = {
name: '狼人杀',
[Symbol('say')]: function () {
console.log('我可以发言')
},
[Symbol('zibao')]: function () {
console.log('我可以自爆')
},
}
console.log(youxi)
如上无法调用,每次Symbol(‘say’)的值都不一样
要使如上的方法能够进行调用,需要进行如下修改:
// 先在外面声明 Symbol 变量
let say = Symbol('say')
let zibao = Symbol('zibao')
let youxi = {
name: '狼人杀',
[say]: function () {
console.log('我可以发言')
},
[zibao]: function () {
console.log('我可以自爆')
},
}
console.log(youxi)
youxi[say]()
youxi[zibao]()
除了定义自己使用的 Symbol 值以外,ES6 还提供了 11 个内置的 Symbol 值,指向语言内部使用的方法。可以称这些方法为魔术方法,因为它们会在特定的场景下自动执行。
class Person {
static [Symbol.hasInstance](param) {
console.log(param)
console.log('我被用来检测类型了')
return false
}
}
let o = {}
console.log(o instanceof Person)
const arr = [1, 2, 3]
const arr2 = [4, 5, 6]
arr2[Symbol.isConcatSpreadable] = false
console.log(arr.concat(arr2))