Es6新特性
1.let & const
1.1 let,作用与var类似,用于声明变量
特性:
1.let不能重复声明变量,var可以重复声明变量
2.块级作用域,es5中存在全局作用域、函数作用域、eval作用域;es6中引入了块级作用域,let声明的变量在块级作用域{}内有效
3.let声明的变量不存在var的变量提升问题
例子
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div class="container">
<h2 class="page-header">点击切换颜色</h2>
<div class="item">1</div>
<hr>
<div class="item">2</div>
<hr>
<div class="item">3</div>
</div>
<script>
// 获取div对象
let items = document.getElementsByClassName('item')
// 遍历并绑定事件 遍历时let i
for(let i = 0; i < items.length; i++){
items[i].onclick = function(){
items[i].style.background = 'pink'
}
}
/*
相当于在3个块级作用域内分别声明了i
{
let i = 0
items[i].onclick = function(){
items[i].style.background = 'pink'
}
}
{
let i = 1
items[i].onclick = function(){
items[i].style.background = 'pink'
}
}
{
let i = 2
items[i].onclick = function(){
items[i].style.background = 'pink'
}
}
*/
/*
// 遍历并绑定事件 遍历时var i
for(var i = 0; i < items.length; i++){
items[i].onclick = function(){
// 修改当前元素的背景颜色
this.style.background = 'pink' // 此处this指向’被绑定的元素对象‘,即调用该函数的对象
// 此处不能和上文一样使用 items[i].style.background = 'pink',
// 因为var的i不考虑块级作用域, 则相当于在全局声明了一个变量,循环结束后i=3,
// 函数执行时向上层寻找最终得到全局变量i=3,而items[3]为undefined
}
}
相当于
{
var i = 0
// ...
}
{
var i = 1
// ...
}
{
var i = 2
// ...
}
{
var i = 3
}
*/
</script>
</body>
<style>
.item{
width: 200px;
height: 50px;
border-radius: 2%;
background-color: brown;
}
</style>
</html>
1.2 const用于声明常量
注意:一定要赋初始值,一般常量使用大写(编程规范),常量值不能修改,存在块级作用域
对于数组和对象的元素修改,不算做对常量的修改,不会报错(因为引用数据类型保存的是内存地址,所以声明数组和对象时可以使用const声明,以此保证其保存的内存地址不变)
2.结构赋值
2.1数组的解构
const Web = ['html', 'css', 'javascript']
let [tool1, tool2, tool3] = Web
console.log('tool1-----', tool1) // html
console.log('tool2-----', tool2) // css
console.log('tool3-----', tool3) // javascript
2.2对象的解构
const liMing = {
name: 'liMing',
age: '22',
tell: function(){
console.log(`I am liMing`)
}
}
let {name, age, tell} = liMing
console.log(name) // 'liMing'
console.log(age) // '22'
console.log(tell) // f(){...}
tell() // I am liMing
3.简化对象写法
ES6 允许在大括号里面,直接写入变量和函数,作为对象的属性和方法(在属性名和变量名相同的情况下),这样的书写更加简洁
let name = 'LiMing'
let tell = function(){
console.log('I am LiMing')
}
const liMing = {
name,
tell,
sayHi(){
console.log('hello')
}
}
// 等效于
// const liMing = {
// name: name,
// tell: tell,
// sayHi: function(){
// console.log('hello')
// }
// }
console.log(liMing)
liMing.tell()
liMing.sayHi()
4.箭头函数
与function声明的区别:
4.1箭头函数this是静态的。
- 箭头函数内的this指向上层对象;始终指向函数声明时所在作用域下的this的值,无法被call改变
- 普通函数内的this指向调用其函数的对象
function getName(){
console.log(this.name)
}
let getName2 = ()=>{
console.log(this.name)
}
//设置window对象的name属性
window.student = 'LiMing'
const student = {
name: 'HanMei'
}
//直接调用
getName() //LiMing
getName2() //LiMing
// call方法调用
getName.call(student) // HanMei
getName2.call(student) // LiMing
4.2箭头函数不能作为构造函数实例化对象
let Person = (name, age)=>{
this.name = name
this.age = age
}
let me = new Person('LiMing', 20) // 报错:Uncaught TypeError: Person is not a constructor
4.3箭头函数不能使用arguments变量,但是可以使用....rest
let fn = ()=>{
console.log(arguments)
}
fn(1, 2, 3) // 报错:Uncaught ReferenceError: arguments is not defined
let fn2 = (...rest)=>{
console.log(...rest)
}
fn2('a','b','c') // a b c
4.4箭头函数的简写
1)当形参有且只有一个的时候,可以省略()
2)当代码体只有一条语句的时候,可以省略{},此时return必须省略,而且语句的执行结果就是函数的返回值
// 当形参有且只有一个的时候,可以省略`()`
let add = n => {
return n + n
}
console.log(add(9))
// 当代码体只有一条语句的时候,可以省略`{}`,此时`return`必须省略,而且语句的执行结果就是函数的返回值
let pow = n => n*n
console.log(pow(9))
4.5箭头函数的例子
箭头函数适合与this无关的回调,比如定时器setTimeout(()=>{...}, 2000)、数组的方法回调arr.filter((item)=>{...});
\
不适合与this有关的回调,比如dom元素的事件回调ad.addEventListener('click', function(){...}、对象内的方法定义{name: 'LiMing', getName: function(){this.name}}
例1:
// 需求-1 点击div 2s后颜色变红
// 获取元素
let ad = document.getElementById('ad')
// 绑定事件
ad.addEventListener('click', function(){
setTimeout(function(){
console.log(this) // 定时器里的this指向window
this.style.background = 'brown' // 报错
}, 2000)
})
//解决方案1
// ad.addEventListener('click', function(){
// // 保存this的值
// let _this = this // _this指向ad
// setTimeout(function(){
// console.log(_this)
// _this.style.background = 'brown'
// }, 2000)
// })
// 解决方案2
// ad.addEventListener('click', function(){
// setTimeout(()=>{
// console.log(this)
// this.style.background = 'brown' // this指向函数声明时所在作用域下this的值 即ad
// }, 2000)
// })
例2
// 需求-2 从数组中返回偶数的元素
const arr = [1, 6, 9, 10, 100, 25]
const result = arr.filter(function(item){
if(item %2 === 0){
return true
}else{
return false
}
})
// 可以用箭头函数
// const result = arr.filter(item => {
// if(item % 2 === 0){
// return true
// }else{
// return false
// }
// })
// 还可以简写为
// const result = arr.filter(item => item % 2 === 0)
console.log(result)
6.rest参数
ES6引入rest参数,用于获取函数的实参,用来代替arguments
// ES5获取实参的方式
function printStudent(){
console.log(arguments) // arguments为一个对象
}
printStudent('LiMing','HanMeimei')
// ES6获取实参的方式
function printFriend(friend1, friend2, ...rest){ // rest参数必须放在形参列表最后,否则会报错
console.log(friend1)
console.log(friend2)
console.log(rest) // 得到一个数组,可以使用数组api
}
printFriend('小猫','小狗','兔子','鸭子')
// 小猫
// 小狗
// ['兔子','鸭子']
7.扩展运算符
...能将[数组]转为逗号分隔的【参数序列】
注:虽然形式与rest参数类似,但是rest参数是用在函数定义时的形参位置,扩展运算符是用在函数实际调用时的实参位置
const STUDENTS = ['小明','小芳','小红']
function printStudent(){
console.log(arguments)
}
printStudent(STUDENTS) // 参数为一个数组,数组内包含3个元素
printStudent(...STUDENTS) // 参数为3个元素
应用场景:
7.1、数组的合并
const STUDENTS1 = ['小明','小芳','小红']
const STUDENTS2 = ['小吴', '小王']
// es5写法
const STUDENTS_ES5 = STUDENTS1.concat(STUDENTS2)
// es6写法
const STUDENTS_ES6 = [...STUDENTS1, ...STUDENTS2]
console.log('es5------',STUDENTS_ES5)
console.log('es6------',STUDENTS_ES6)
7.2、数组的克隆
const STUDENTS1 = ['小明','小芳','小红']
const PUPIL = [...STUDENTS1] // 注意:如果数组内元素是引用类型,拷贝的是内存地址,为浅拷贝
console.log('PUPIL----',PUPIL)
7.3 将伪数组转为真正的数组
const divs = document.querySelectorAll('div')
console.log(divs) // 此处得到的divs实际是一个对象
const divsArr = [...divs] // 将其转为真正的数组,从而可以使用数组的api譬如filter、map
console.log(divsArr)
8.Symbol
ES6引入了一种新的原始数据类型Symbol,表示独一无二的值。它是JavaScript语言的第7种数据类型,是一个类似字符串的数据类型
Symbol特点:
Symbol的值是唯一的,用来解决命名冲突的问题
Symbol值不能与其他数据进行运算,也不能与自己进行运算,譬如+、-、*、/、比较运算
Symbol定义的对象属性不能使用for…in遍历,但是可以使用Reflect.ownKeys来获取对象的所有键名
创建Symbol:
1、通过let s2 = Symbol('helloworld') 的方式创建Symbol,'helloworld’作为Symbol描述,作用相当于注释,这种方式创建的Symbol,即使传入的描述一致,但实际返回的值是不同的
// 创建Symbol
let s = Symbol()
console.log(s,typeof s) // Symbol() "symbol"
let s2 = Symbol('helloworld') // 'helloworld'作为Symbol描述,作用相当于注释
let s3 = Symbol('helloworld') // 即使传入的描述一致,但实际返回的值是不同的
console.log(s2 === s3) // false
2 通过Symbol.for()创建Symbol,这种方式创建Symbol,传入的描述一致,实际返回的值也一致,可以得到唯一的Symbol值
// Symbol.for创建Symbol
let s4 = Symbol.for('helloworld')
let s5 = Symbol.for('helloworld')
console.log(s4 === s5) // true
##Symbol使用场景
1、给对象添加属性和方法。由于Symbol值具有唯一性,所以可以很安全地把属性和方法加入对象中,如下所示
```java
let game = {
up: 'upp',
down: 'doown'
}
let methods = {
up: Symbol(),
down: Symbol(),
}
// 添加方法
game[methods.up] = function(){
console.log('up up up')
}
game[methods.down] = function(){
console.log('down down down')
}
console.log('game----', game)
// 调用
game[methods.up]()
let youxi = {
name: '狼人杀',
[Symbol('say')]: function(){ // 此处不能直接写 Symbol('say'): function(){...},因为Symbol('say')是动态的,和上面固定的'name'不一样
console.log('发言')
}
}
Symbol内置值
ES6除了定义自己使用的Symbol值以外,还提供了11个内置的Symbol值,指向语言内部使用的方法,比如
1、Symbol.hasInstance
当其他对象使用instanceof运算符,判断是否为该对象的实例时,会调用这个方法
class Person{
static [Symbol.hasInstance](param){
console.log('param----', param)
console.log('检测类型')
}
}
let o = {}
console.log(o instanceof Person)
// param---- {}
// 检测类型
// false
2、Symbol.isConcatSpreadable
对象的Symbol.isConcatSpreadable属性等于一个bool值,表示该对象用于Array.prototype()时,是否可以展开
const arr1 = [1,2,3]
const arr2 = [4,5,6]
arr2[Symbol.isConcatSpreadable] = false // arr2不可展开
const arr = arr1.concat(arr2)
console.log(arr) // [1,2,3,[4,5,6]]
3、Symbol.unscopables
该对象指定了使用with关键字时,哪些属性会被with环境排除
4、Symbol.match
当执行str.match(myObject)时,如果该属性存在,会调用它,返回该方法的返回值
5、Symbol.replace
当该对象被str.replace(myObject)方法调用时,会返回该方法的返回值
6、Symbol.search
当该对象被str.search(myObject)方法调用时,会返回该方法的返回值
7、Symbol.split
当该对象被str.split(myObject)方法调用时,会返回该方法的返回值
8、Symbol.iterator
对象进行for ... of循环时,会调用Symbol.iterator方法,返回该对象的默认遍历器
9、Symbol.toPrimitive
该对象被转为原始类型的值时,会调用这个方法,返回该对象对应的原始类型值
10、Symbol.toStringTag
在该对象上调用toString方法时,返回该方法的返回值
11、Symbol.species
创建衍生对象时,会使用该属性
9.迭代器
迭代器(iterator)是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署iterator接口,就可以完成遍历操作
ES6创造了一种新的遍历命令for...of循环,iterator接口主要供for...of消费
注:for...of遍历的是键值,for...in遍历的是键名
for...of不能对属性值进行修改,forEach()可以
原生具备iterator接口的数据(可用for...of遍历)
- Array
- Arguments
- Set
- Map
- String
- Typed Array
- Node List
工作原理:
创建一个指针对象,指向当前数据结构的起始位置
第一次调用对象的next方法,指针自动指向数据结构的第一个成员
接下来不断调用next方法,指针一直往后移动,直到指向最后一个成员
每调用next方法返回一个包含value和done属性的对象,done属性表示遍历是否结束
const food = ['鱼香肉丝','糖醋里脊','酸菜鱼']
for(let item of food){
console.log(item)
}
let iterator = food[Symbol.iterator]()
console.log(iterator.next()) // {value: "鱼香肉丝", done: false}
console.log(iterator.next()) // {value: "糖醋里脊", done: false}
console.log(iterator.next()) // {value: "酸菜鱼", done: false}
console.log(iterator.next()) // {value: undefined, done: true} true 表示遍历已经结束
注:需要自定义遍历数据的时候,要想到迭代器
迭代器应用-自定义遍历数据(即自己手动实现一个迭代器)
// 声明一个对象
const school = {
name: '三中',
students: [
'LiMing',
'HanMeimei',
'WangFang',
],
[Symbol.iterator](){
// 声明一个索引变量
let index = 0
return {
next: ()=>{
if(index < this.students.length){
// if(index < 3){
const result = {value: this.students[index], done: false}
// 下标自增
index++
// 返回结果
return result
}else{
return {value: undefined, done: true}
}
}
}
}
}
// 遍历这个对象
for(let item of school){
console.log(item)
}
10.生成器
生成器对象常被我们称为“生成器”(Generator),而 Generator 函数常称为“生成器函数”(Generator Function)。
由于生成器对象是实现了可迭代协议和迭代器协议的,因此生成器也是一个迭代器,生成器也是一个可迭代对象。
执行生成器函数,返回的是一个迭代器对象,通过iterator.next()调用执行函数内语句
function * gen(){
console.log('hello generator')
}
let iterator = gen() // 返回的是一个迭代器对象
// console.log(iterator)
// 通过.next()调用执行函数内语句
iterator.next() // hello generator
yield是函数代码的分隔符,结合调用iterator.next()方法,实现函数gen1的语句的分段执行
function * gen1(){
console.log('--- 1 ---')
yield '耳朵' // 函数代码的分隔符
console.log('--- 2 ---')
yield '尾巴'
console.log('--- 3 ---')
}
let iterator1 = gen1()
iterator1.next() // --- 1 ---
iterator1.next() // --- 2 ---
iterator1.next() // --- 3 ---
// 通过调用.next()方法,实现函数gen1的语句的分段执行
使用for...of遍历函数执行后返回的迭代器对象,每一次遍历的item为yield后的表达式或者自变量的值
function * gen1(){
yield '耳朵' // 函数代码的分隔符
yield '尾巴'
}
// 遍历,每一次遍历的item为yield后的表达式或者自变量的值
for(let item of gen1()){
console.log(item)
}
// 执行结果:
// 耳朵
// 尾巴
// 注:next调用和for...of调用同时存在,只会支持最先的一个
生成器函数的参数传递
function * gen(args){
console.log(args) // 'aaa'
let one = yield 111
console.log(one) // 'bbb'
let two = yield 222
console.log(two) // 'ccc'
let three = yield 333
console.log(three)
}
// 执行生成器函数获取迭代器对象
let iterator = gen('aaa')
console.log(iterator.next()) // {value: 111, done: false}
// next方法可以传入实参,传入的实参会作为上一个yield后返回的结果
console.log(iterator.next('bbb')) // {value: 222, done: false}
console.log(iterator.next('ccc')) // {value: 333, done: false}
console.log(iterator.next('ddd')) // {value: undefined, done: true}
生成器函数实例1:
1s后控制台输出111 --> 2s后控制台输出222 --> 3s后控制台输出333 ==> 总计耗时6s
// 异步编程,如文件操作、网络请求、数据库操作
// 1s后控制台输出111 --> 2s后控制台输出222 --> 3s后控制台输出333 ==> 总计耗时6s
// 用生成器函数实现
function one (){
setTimeout(()=>{
console.log(111)
iterator.next()
}, 1000)
}
function two (){
setTimeout(()=>{
console.log(222)
iterator.next()
}, 2000)
}
function three (){
setTimeout(()=>{
console.log(333)
}, 3000)
}
function * gen(){
yield one()
yield two()
yield three()
}
let iterator = gen()
iterator.next()
// 以下为回调地域做法
// setTimeout(()=>{
// console.log(111)
// setTimeout(()=>{
// console.log(222)
// setTimeout(()=>{
// console.log(333)
// }, 3000)
// }, 2000)
// }, 1000)
11.Promise
11.1Promise介绍
Promise是ES6引入的异步编程的新解决方案,语法上Promise是一个构造函数,用来封装异步操作并可以获取其成功或失败的结果
从语法上讲,promise是一个对象,从它可以获取异步操作的信息,
从本意上讲,它是承诺,承诺它过一段时间会给你一个结果。
promise有三种状态:pending进行中 fulfiled 已完成 rejected已失败
Promise的作用
用来解决回调地狱,常常第一个函数的输出是第二个函数的输入这种现象
promise可以支持多个并发的请求,获取并发请求中的数据
promise可以解决异步的问题,本身不能说明promise是异步的
2.Promise构造函数 Promise(excutor){}
Promise参数接收一个函数,有两个形参,分别是resolve和reject
resolve,异步操作成功执行后的回调函数
reject,异步操作失败执行后的回调函数
const p=new Promise(function(resolve,reject){
setTimeout(function(){
let data="数据库中的用户数据"
// 调用resolve说明读取成功
// resolve(data)
let err="数据读取失败"
reject(err)
},1000)
})
11.3promise.prototype.then方法
调用promise的then方法 两个参数:第一个返回成功得到的值,第二个返回失败的原因
p.then(function(value){
console.log(value) //数据库中的用户数据
},function(reason){
console.error(reason)
})
- 如果回调函数中返回的结果是非promise类型的属性,状态为成功,结果为对象的成功值
- 如果回调函数中返回的结果是promise结果,成功:状态成功,返回对象的结果 失败:状态失败 返回失败的返回值,因为可以返回promise,所以可以写promise链,解决回调狱的问题
- 如果抛出错误 状态失败 返回失败的结果
p.then((value) => {
}, (reason) => {
}).then((value) => {
}, (reason) => {
})
11.4Promise读取文件
引入node.js fs模块 fs模块就是文件系统模块,用于读写文件
const fs=reqire("fs")
fs.readFile异步读取文件的全部内容
fs.readFile(path[, options], callback)
const fs = require("fs")
const p = new Promise(function (resolve, reject) {
fs.readFile("./day04.md", (err, data) => {
if (err) reject(err)
resolve(data)
})
})
p.then(function (value) {
console.log(value.toString())
}, function (reason) {
console.error("读取失败")
})
11.5promise封装ajax
// promise封装
const p = new Promise((resolve, reject) => {
// 原生ajax向url发送请求
const xhr = new XMLHttpRequest()
xhr.open("GET", "https://api.apiopen.top/getJoke")
xhr.send()
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status >= 200 && xhr.status < 300) {
// 成功 调用resolve函数 修改promise对象的状态为成功
resolve(xhr.response)
} else {
// 失败
reject(xhr.status)
}
}
}
})
// 指定回调
p.then(function (value) {
console.log(value)
}, function (reason) {
console.error(reason)
})
11.6promise.prototype.catch()方法
只传入错误回调函数 返回一个promise,并处理拒绝的情况
p.catch(reason => {
console.error(reason)
})
12、Set
ES6提供了新的数据结构set(集合),本质上是一个对象。它类似于数组,但成员的值都是唯一的,集合实现了iterator接口,所以可以使用「扩展运算符」和for...of进行遍历
集合的属性和方法:
size,返回集合的元素个数
add,增加一个新元素,返回当前集合
delete,删除元素,返回Boolean值
has,检测集合中是否包含某个元素,返回Boolean值
let s = new Set(['风声','雨声','读书声','风声']) // 可以接受可迭代数据,一般传入数组
// '风声','雨声','读书声'
let size = s.size // 查看元素个数
let has = s.has('读书声') // 检测是否含该元素 true
s.add('水声') // 添加元素
s.delete('读书声') // 删除元素
let has2 = s.has('读书声') // 检测是否含该元素 false
// 遍历集合
for(let item of s){
console.log(item)
}
s.clear() // 清空集合
console.log(s, has,has2, typeof s)
set 的应用
数组去重
let arr = [1, 2, 3, 4, 5, 4, 3, 2, 1]
// 数组去重
let result = [...new Set(arr)]
console.log(result) // [1, 2, 3, 4, 5]
求交集
let arr = [1, 2, 3, 4, 5, 4, 3, 2, 1]
let arr2 = [4, 5, 6, 5, 6]
// 求交集
let result = [...new Set(arr)].filter(item => { // 对arr去重并进行遍历
let s2 = new Set(arr2) // 将arr2变为元素不重复的集合
if(s2.has(item)){ // 如果元素存在s2中
return true
}else{
return false
}
})
console.log(result) // [4, 5]
// 简化写法
let result2 = [...new Set(arr)].filter(item => new Set(arr2).has(item))
console.log(result2)
求并集
let arr = [1, 2, 3, 4, 5, 4, 3, 2, 1]
let arr2 = [4, 5, 6, 5, 6]
// 求并集:连接两个数组 => 转为元素不重复的集合 => 转为数组
let union = [...new Set([...arr, ...arr2])]
console.log(union)
// [1, 2, 3, 4, 5, 6]
求差集
let arr = [1, 2, 3, 4, 5, 4, 3, 2, 1]
let arr2 = [4, 5, 6, 5, 6]
// 求差集-arr对arr2求差集,即求arr里面有但是arr2里没有的元素,相当于求交集的逆运算
let diff = [...new Set(arr)].filter(item => !(new Set(arr2).has(item)))
console.log(diff) // [1, 2, 3]
13、Map
ES6提供了Map数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当做键。Map也实现了iterator接口,所以可以使用「扩展运算符」和for...of进行遍历
Map的属性和方法:
\
size,返回Map的元素个数
set,增加一个新元素,返回当前Map
get,返回键名对象的键值
has,检测Map中是否包含某个元素,返回Boolean值
clear,清空集合,返回undefined
// 声明Map
let m = new Map()
// 添加元素
m.set('name','LiMing') // 键名,键值
m.set('tell',function(){
console.log('I am LiMing ')
})
let friend = {
school: '三中'
}
m.set(friend,['小吴','小王','小芳'])
// 删除元素
m.delete('tell')
// 获取元素
let friends = m.get(friend)
console.log(friends)
// 获取元素个数
let size = m.size
console.log(size)
// 遍历Map
for(let item of m){
console.log('item---',item)
// 每一个item都是一个数组,第一个元素为键,第二个元素为值
}
// 清空Map
m.clear()
console.log(m)
console.log(typeof m)
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析