ES6语法笔记
1、let变量声明以及声明特性
-
变量不能重复声明
-
块级作用域 (let)
作用域:全局、函数、eval(严格模式下)、块级作用域
块级作用域:if、else、while、for
{
let a = 1
}
console.log(a) // ReferenceError is not defined
- 不存在变量提升
console.log(a) // ReferenceError Cannot not access 'a' before initialization
let a = 1
- 不影响作用域链
// fn运行,但是函数作用域中没有,所以向上一级查找,找到a再输出
{
let a =1
function fn(){
console.log(a)
}
fn()
}
案例
2、const声明常量以及特点
- 一定要赋初始值
const A // SyntaxError Missing initializer in const declaration
- 一般常量使用大写
- 常量值不能修改
TypeError Assigment to constant variable
- 块级作用域
- 对于数组和对象的元素修改,不算对常量的修改,不会报错
const ALPHBAT = ['A','B']
ALPHBAT.push('C') // 不会报错,常量指向的地址没有改变
3、变量解构赋值
允许按照一定模式从数组和对象中提取值,对变量进行赋值
- 数组的解构
const FRUITS = ['苹果','香蕉','草莓']
let [apple,banana,strawberry] = FRUITS
console.log(apple) // 苹果
console.log(banana) // 香蕉
- 对象的解构
const student = {
name:'xiaoming',
age:0,
gender:0,
play:function(){
console.log('快乐小学生')
}
}
let {name, age,gender,play} = student
console.log(name) // xiaoming
play() // 快乐小学生
4、模板字符串 ``
- 内容中可以直接出现换行符
let str = `<div>
<p>模板字符串</p>
</div>`
- 变量拼接
let age = 18
let str = `xiaoming今年${age}岁`
console.log(str) // xiaoming今年18岁
5、对象简化
允许在大括号内,直接写入变量和函数,作为对象的属性和方法
let name = 'xiaoming'
let play = function(){
console.log('快乐小学生')
}
const student = {
name,
play,
improve(){
cosole.log('函数声明简化')
}
}
6、箭头函数以及声明特点
- 声明函数
let fn = function(){}
let fn = (a,b) => {
return a + b
}
- this是静态的,this始终指向函数声明时所在作用域的this的值
function getName(){
console.log(this.name)
}
let getName2 = () => {
console.log(this.name)
}
window.name = 'window'
const student = {
name:'xiaoming'
}
// 直接调用
getName() // window
getName2() // window
// call方法调用
getName.call(student) // xiaoming
getName2.call(student) // window
- 不能作为构造实例化对象
let Person = (name, age)=>{
this.name = name,
this.age = age
}
let me = new Person('xiaoming',18)
console.log(me) // TypeError Person is not a constructor
-
不能使用arguments变量
arguments保存实参
let fn = ()=>{
console.log(arguments) // ReferenceError arguments is not defined
}
fn(1,2,3)
- 箭头函数的简写
- 省略小括号,当形参有且只有一个
let add = n =>{
return n+n
}
console.log(add(9))
- 省略花括号,当代码体只有一条语句,此时语句中的return也必须省略。语句的执行结果就是函数的返回值
let pow = (n) => n*n
console.log(pow(9))
案例
适合与
this
无关的回调,定时器,数组的方法对象回调不适合与
this
有关的回调,事件回调,对象的方法
7、函数参数默认值的设置
- 形参的初始值,具有默认值的参数,一般位置要靠后
function add(a,b,c=10){
return a + b + c
}
let result = add(1,2)
console.log(result) // 13
// 默认值参数放中间没有意义
function add(a,c=10,b){
return a + b + c
}
let result = add(1,2)
console.log(result) // NaN undefined + number
- 与解构赋值结合
function connect({host,username,password,port}){
console.log(host) // localhost
console.log(username) // root
}
connect({
host:'localhost',
username:'root',
password:'root',
port:"3306"
})
// 给属性赋初始值,如果调用时host属性没有传,就使用默认值
function connect({host='127.0.0.1',username,password,port}){
console.log(host) // 127.0.0.1
console.log(username) // root
}
connect({
username:'root',
password:'root',
port:"3306"
})
8、rest参数
引入rest参数,用于获取函数的实参,用来代替arguments
- 获取参数,rest参数必须放到参数最后
// ES5
function fruit(){
console.log(arguments) // 对象
}
fruit('apple','banana','strawberry')
// ES6
function fruit(...args){
console.log(args) // 数组,可以使用数组方法
}
fruit('apple','banana','strawberry')
// rest参数必须放到参数最后
function fn(a,b,...args){
console.log(a) // 1
console.log(b) // 2
console.log(args) // [3,4,5,6]
}
fn(1,2,3,4,5,6)
function fn(a,...args,b){
console.log(a) // SynataError Rest parameter must be last formal parameter
}
fn(1,2,3,4,5,6)
9、扩展运算符
将数组
转换为逗号分隔的参数序列
const fruits = ['apple','banana','strawberry']
function fruit(){
console.log(arguments)
}
fruit(fruits) // arguments对象,参数只有一个,fruits在数组中
fruit(...fruits) // arguments对象,参数有三个,相当于fruit('apple','banana','strawberry')
应用
- 数组合并
const fruits = ['apple','banana','strawberry'] const animals = ['cat','dog'] const fa = fruits.concat(animals) console.log(fa) // ['apple','banana','strawberry','cat','dog'] const fa = [...fruits,...animals] console.log(fa) // ['apple','banana','strawberry','cat','dog']
- 数组克隆(浅拷贝)
const fruits = ['apple','banana','strawberry'] const Fruits = [...fruits] consoloe.log(Fruits) // ['apple','banana','strawberry']
- 将伪数组转为真正的数组
const divs = documents.querySelectorAll('div') console.log(divs) // __proto__:Object const divArr = [...divs] console.log(divArr) // []
10、Symbol介绍与创建
引入原始数据类型,表示独一无二的值。JavaScript第七种数据类型。
- 特点
- Symbol的值是唯一的,用来解决命名冲突的问题
- Symbol的值不能与其他数据进行运算
- Symbol定义的对象属性不能使用for...in循环遍历,但是可以使用Reflect.ownKeys来获取对象的所有键名
- 创建Symbol,值是唯一的
let s = Symbol()
console.log(s, typeof s) // Symbol() "symbol"
let s2 = Symbol('fruits')
let s3 = Symbol('fruits')
console.log(s2 === s3) // false
let s4 = Symbol.for('fruits')
let s5 = Symbol.for('fruits')
console.log(s4 === s5) // true
- 不能与其他数据进行运算
let result = s + 100 // TypeError cannot convert a Symbol value to a number
U | S | O | N | B |
---|---|---|---|---|
undefined | string | object | null | boolean |
symbol | number |
- 给对象添加属性和方法
// game内容未知
let game = {
name:'xiaoming',
up(){},
down(){}
}
// 安全的向game对象中添加方法
let methods = {
up:Symbol(),
down:Symbol()
}
game[methods.up] = funtion(){
console.log('up')
}
game[methods.down] = funtion(){
console.log('down')
}
console.log(game) // game原有的属性和方法 + Symbol() + Symbol()
let game = {
name:'狼人杀',
[Symbol('say')]:function(){
console.log('发言')
}
}
console.log(game) // Symbol(say)
- Symbol内置值
【Symbol内置值】作为Symbol的属性,【Symbol.Symbol内置值】作为对象的属性
// hasInstance 自己控制类型检测
class Person{
static [Symbol.hasInstance](param){
console.log(param)
console.log('检测类型')
// return true 则输出为true
}
}
let o = {}
console.log(o instanceof Person) // {} 检测类型 false
// isConcatSpreadable 控制concat是否可以展开
const arr = [1,2,3]
const arr2 = [4,5,6]
console.log(arr.concat(arr2)) // [1,2,3,4,5,6]
arr2[Symbol.isConcatSpreadable] = false
console.log(arr.concat(arr2)) // [1,2,3,[4,5,6]]
11、迭代器
是一种接口,为各种不同的数据结构提供统一的访问机制,任何数据结构只要部署Iterator接口(对象中的一个属性Symbol.interator),就可以完成遍历操作。
-
ES6创造了一种新的遍历命令
for...of
循环,Iterator接口主要提供for...of
循环 -
原生具备Iterator接口的数据
Array、Arguments、Set、Map、String、TypedArray、NodeList
// 数组
const fruits = ['apple','banana','strawberry']
// of 键值
for(let v of fruits){
console.log(v) // apple banana strawberry
}
// in 键名
for(let k in fruits){
console.log(v) // 0 1 2
}
-
工作原理
1)创建一个指针对象,指向当前数据结构的起始位置
2)第一次调用对象的next方法,指针自动指向数据结构的第一个成员
3)接下来不断调用next方法,指针一直往后移动,直到指向最后一个成员
4)每调用next方法返回一个包含value和done属性的对象
注:需要自定义遍历数据的时候,要想到迭代器
自定义遍历数据
let fruits = { name:'水果', content:['apple','banana','strawberry'], [Symbol.iterator](){ let index = 0 let _this = this return { next:function(){ if(index < _this.content.length){ const result = {value:_this.content[index],done:false} index++ return result }else{ return {value:undefined,done:true} } } } } } // 遍历对象 for(let v of fruits){ console.log(v) }
12、生成器
生成器函数是ES6提供的一种异步编程解决方案
- 声明与调用
function * fn(){
console.log("hello generator")
}
let generator = fn()
console.log(generator) // 迭代器对象,没有输出函数中的console
generator.next() // hello generator
// yield 函数代码的分隔符,3个yield分为4个块
function * fn(){
console.log('块1')
yield '1111'
console.log('块2')
yield '2222'
console.log('块3')
yield '3333'
console.log('块4')
}
let generator = fn()
generator.next() // 块1
generator.next() // 块2
for(let v of fn()){
console.log(v) // 1111 2222 3333 yield后的值
}
- 参数传递
function * fn(){
yield 1111
yield 2222
yield 3333
}
let generator = fn()
console.log(generator.next()) //{value:111,done:false}
function * fn(arg){
console.log(arg) // AAA
let one = yield 1111
console.log(one) // BBB
let two = yield 2222
console.log(two) // CCC
let three = yield 3333
console.log(three) // DDD
}
// 整体函数传参
let generator = fn('AAA')
console.log(generator.next()) //返回结果yield后的值 {value:1111,done:false}
// next传入实参,作为上一个`yield`(yield 1111)的返回结果
// 第2次调用,返回yield 1111的结果
console.log(generator.next('BBB')) //{value:2222,done:false}
// 第3次调用,返回yield 2222的结果
console.log(generator.next('CCC')) //{value:3333,done:false}
// 第4次调用,返回yield 3333的结果
console.log(generator.next('DDD')) //{value:undefined,done:true}
实例-异步编程
- 需求:1s后输出111,2s后输出222,3s后输出333
function one(){ setTimeout(()=>{ console.log(111) // 111 iterator.next() },1000) } function two(){ setTimeout(()=>{ console.log(222) // 222 iterator.next() },2000) } function three(){ setTimeout(()=>{ console.log(333) // 333 iterator.next() },3000) } // 生成器 function * gen(){ yield one() yield two() yield three() } // 调用 let generator = gen() generator.next()
- 模拟获取 用户数据—>订单数据—>商品数据,存在先后顺序
function getUsers(){ setTimeout(()=>{ let data = '用户数据' // 第2次调用next,data将作为第一个yield返回结果 generator.next(data) // 调用next传参 },1000) } function getOrders(){ setTimeout(()=>{ let data = '订单数据' generator.next(data) },1000) } function getGoods(){ setTimeout(()=>{ let data = '商品数据' generator.next(data) },1000) } // 生成器 function * gen(){ let users = yield getUsers() console.log(users) // 用户数据 let orders = yield getOrders() console.log(orders) // 订单数据 let goods = yield getGoods() console.log(goods) // 商品数据 } // 调用 let generator = gen() generator.next()
13、Promise
异步编程,语法上Promise是一个构造函数,用来封装异步操作并可以获取其成功或失败的结果,避免回调地狱
通过resolve
和reject
来改变Promise状态,再根据状态调用then方法的不同函数
实现Promise可以看这里✨手写Promise过程记录
- 基本使用
// 实例化
const p = new Promise(function(resolve,reject){
setTimeout(function(){
let data = '数据库中的用户数据'
resolve(data)
},1000)
})
// 调用then方法
p.then(function(value){
console.log(value) // 数据库中的用户数据
},function(reason){})
// 实例化
const p = new Promise(function(resolve,reject){
setTimeout(function(){
let error = '数据库读取失败'
reject(error)
},1000)
})
// 调用then方法
p.then(function(value){
console.log(value) // 数据库中的用户数据
},function(reason){
console.error(reason) // 数据库读取失败
})
- Promise封装读取文件
// 1. 引入fs
const fs = require('fs')
// 2.调用方法读取文件
fs.readFile('./resources/文件.txt',(error,data)=>{
// 失败,抛出错误
if(error) throw error
// 成功,输出内容
console.log(data.toString()) // 文件内容
})
// 3. 使用Promise封装
const p = new Promise(function(resolve,reject){
fs.readFile('./resources/文件.txt',(error,data)=>{
// 失败,抛出错误
if(error) reject(error)
// 成功,输出内容
resolve(data)
})
p.then(function(value){
console.log(value.toString()) // 文件内容
},function(reason){
console.log('读取失败')
})
- Promise封装Ajax请求
// 1. 创建对象
const xhr = new XMLHttpRequest()
// 2. 初始化
xhr.open('GET','https://api.apiopen.top/getoke')
// 3. 发送
xhr.send()
// 4. 绑定事件,处理响应结果
xhr.onreadystatechange = function(){
// 判断
if(xhr.readyState === 4){
// 判断响应状态码 200-299
if(xhr.status >= 200 && xhr.status < 300){
console.log(xhr.response)
}else{
console.err(xhr.status)
}
}
}
const p = new Promise((resolve,reject)=>{
const xhr = new XMLHttpRequest()
xhr.open('GET','https://api.apiopen.top/getoke')
xhr.send()
xhr.onreadystatechange = function(){
if(xhr.readyState === 4){
if(xhr.status >= 200 && xhr.status < 300){
resolve(xhr.response)
}else{
reject(xhr.status)
}
}
}
})
p.then(function(value){
console.log(value)
},function(reason){
console.log(reason)
})
- Promise.prototype.then方法
返回结果是Promise对象,对象状态由回调函数的执行结果决定。可以链式调用,解决回调地狱。
①回调函数返回非Promise类型(undefined,string....)
then的返回Promise对象的状态为成功,返回值为对象的成功的值
②回调函数返回Promise类型
内部Promise的返回状态决定then的返回Promise对象的状态,
const result = p.then(function(value){
return new Promise((resolve,reject)=>{
resolve('ok')
})
})
console.log(result) // resolved ok
const result = p.then(function(value){
return new Promise((resolve,reject)=>{
reject('error')
})
})
console.log(result) // rejectd error
③抛出错误,状态为rejected,值为抛出的错误值
- 多文件内容读取
const fs = require('fs')
fs.readFile('./resources/文件1.txt',(error,data1)=>{
fs.readFile('./resources/文件2.txt',(error,data2)=>{
fs.readFile('./resources/文件3.txt',(error,data3)=>{
let result = data1 + data2 + data3
console.log(result) // 3个文件的内容
})
})
})
//使用Promise封装
const p = new Promise(function(resolve,reject){
fs.readFile('./resources/文件1.txt',(error,data)=>{
resolve(data)
})
}
p.then(value=>{
return new Promise(function(resolve,reject){
fs.readFile('./resources/文件2.txt',(error,data)=>{
resolve([value,data])
})
}
}).then(value=>{
return new Promise(function(resolve,reject){
fs.readFile('./resources/文件3.txt',(error,data)=>{
value.push(data)
resolve(value)
})
}
}).then(value=>{
console.log(value.join('')) // 3个文件的内容
}
- catch
14、Set
集合类似于数组,但是成员的值是唯一的。
实现了iterator接口,可以使用扩展运算符
和for...of
进行遍历。
let s = new Set()
let s2 = new Set(['apple','banana','apple'])
console.log(s2) // {'apple','banana'}
// size 元素个数
console.log(s2.size) // 2
// add 添加元素
s2.add('strawberry')
console.log(s2) // {'apple','banana','strawberry'}
// delete 删除元素
s2.delete('apple')
console.log(s2) // {'banana','strawberry'}
// has 检测
s2.has('strawberry')
console.log(s2) // true
// clear 清空
s2.clear()
console.log(s2) // {}
for(let v of s2){
console.log(s2) // apple banana
}
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 arr2 = [4,5,6,5,6] let result = [...new Set(arr)].filter(item =>{ let s2 = new Set(arr2) // 4 5 6 if(s2.has(item)){ return true }else return false }) // 化简 let result = [...new Set(arr)].filter(item => new Set(arr2).has(item)) console.log(result) // [4 5]
- 并集
let arr2 = [4,5,6,5,6] let union = [...new Set([...arr,...arr2])] console.log(union) // [1,2,3,4,5,6]
- 差集
let arr2 = [4,5,6,5,6] let diff = [...new Set(arr)].filter(item => !(new Set(arr2).has(item))) console.log(diff) // [1,2,3]
15、Map
map类似于对象,也是键值对的集合。但是键的范围不限于字符串,各种类型的值(包括对象)都可以当作键。
实现了iterator接口,可以使用扩展运算符
和for...of
进行遍历。
let m = new Map()
// 添加元素
m.set('name','xiaoming')
m.set('change',function(){
console.log('change')
})
let key ={name:'fruit'}
m.set(key,['apple','banana'])
console.log(m)
// size 元素个数
// delete 删除
// get 获取
console.log(m.get('name')) // xiaoming
// clear 清空
// for...of
16、class类
class Phone{
// 构造方法 名字不能修改
constructor(brand,price){
this.brand = brand
this.price = price
}
// 方法必须使用该语法,不能使用ES5的对象
call(){
console.log('可以打电话')
}
}
let onePlus = new Phone('1+',1999)
console.log(onePlus)
- 静态成员
function Phone(){}
Phone.name='手机'
Phone.change = function(){
console.log('我可以改变世界')
}
let nokia = new Phone()
console.log(nokia.name) // undefined
nokia.change() // TypeError is not a function
Phone.prototype.size = '5.5inch'
console.log(nokia.size) // 5.5inch
Phone.name
和Phone.change
属于函数对象,不属于实例对象。
将Phone.name
和Phone.change
称为静态成员
实例对象的属性和构造函数的原型相通
class Phone{
static name = '手机'
}
let nokia = new Phone()
console.log(nokia.name) // undefined
console.log(Phone.name) // 手机
static 标注的name
属于类而不属于实例对象
- class的类继承
// ES5构造函数继承
// 手机
function Phone(brand, price){
this.brand = brand;
this.price = price;
}
Phone.prototype.call = function(){
console.log("我可以打电话");
}
// 智能手机
function SmartPhone(brand,price,color,size){
Phone.call(this, brand,price);
this.color = color;
this.size = size;
}
// 设置子级构造函数的原型
SmartPhone.prototype = new Phone;
SmartPhone.prototype.constructor = smartPhone;
// 声明子类的方法
SmartPhone.prototype.photo = function({
console.log("我可以拍照")
}
SmartPhone. prototype.playGame = function({
console.log("我可以玩游戏")
}
const chuizi = new SmartPhone('锤子',2499,'黑色','5.5inch')
console.log(chuizi)
// ES6
class Phone{
// 构造方法
constructor(brand,price){
this.brand = brand;
this.price = price;
}
// 父类的成员属性
call({
console.log('我可以打电话!!');
}
}
class SmartPhone extends Phone{
// 构造方法
constructor(brand,price,color,size){
super(brand,price);// Phone.call(this,brand,price)
this.color = color;
this.size = size;
}
photo(){
console.log('拍照');
}
playGame(){
console.log('玩游戏');
}
}
const xiaomi = new SmartPhone('小米',799,'黑色','4.7inch')
console.log(xiaomi)
- 子类对父类方法的重写
- getter和setter
// get 和 set
class Phone{
get price(){
console.log("价格属性被读取了");
return 'iloveyou';
}
set price(newVal){
console.log('价格属性被修改了');
}
}
// 实例化对象
let s = new Phone();
console.log(s.price);// iloveyou
s.price = 'free';
17、数值扩展
-
Number.EPSILON
是JavaScript表示的最小精度EPSILON属性的值接近于 2.2204468492503130808472633361816E-16
function equal(a,b){
return Math.abs(a-b) < Number.EPSILON
}
console.log(0.1 + 0.2 === 0.3) // false
console.log(equal(0.1 + 0.2, 0.3) // true
- 二进制和八进制
Number.isFinite
检测一个数值是否为有限数
console.log(Number.isFinite(100)) // true
console.log(Number.isFinite(100/0) // false
console.log(Number.isFinite(Infinity)) // false
Number.isNaN
检测一个数值是否为NaN
console.log(Number.isNaN(123)) // false
Number.parseInt
字符串转整数,Number.parseFloat
console.log(Number.parseInt('5211314love')) // 5211314
console.log(Number.parseFloat('3.1415926神奇')) // 3.1415926
Number.isInteger
判断一个数是否为整数
console.log(Number.isInteger(5)) // true
console.log(Number.isInteger(2.5)) // false
Math.trunc
将数字的小数部分抹掉
console.log(Math.trunc(3.5)) // 3
Math.sign
判断一个数到底为正数负数还是零
console.log(Math.sign(100)) // 1
console.log(Math.sign(0)) // 0
console.log(Math.sign(-200)) // -1
18、对象方法扩展
Object.is
判断两个值是否完全相等
console.log(Object.is(120,121)) // false
console.log(Object.is(120,120)) // true
console.log(Object.is(NaN,NaN)) // true
console.log(NaN === NaN) // false
Object.assign
对象的合并
如果重名,第二个参数会覆盖第一个参数
const config1 = {
host: 'localhost',
port:3306,
name: 'root',
pass: 'root',
test:'test'
}
const config2 = {
host: 'http: //atguigu.com',
port: 3306,
name: 'atguigu.com',
pass: 'iloveyou',
test2:'test2'
}
console.log(Object.assign(config1,config2)) // config2覆盖了config1,test保留,test2不保留
Object.setPrototypeof
设置原型对象Object.getPrototypeof
const school = {
name:'尚硅谷'
}
const cities = {
xiaoqu:['北京','上海','深圳']
}
Object.setPrototypeof(school, cities)
console.log(school) // __proto__ cities{}
console.log(Object.getPrototypeof(school)) // cities{}
19、模块化
- 好处
- 防止命名冲突
- 代码复用
- 高维护性
- 语法
export
:规定模块的对外接口
import
:输入其他模块提供的功能
<script src="./src/js/app.js" type="module"></script>
- 导出
export
// 分别暴露
export a = 1
export function change(){
console.log('change')
}
import * as m1 from './js/m1.js'
// 统一暴露
export {a,b}
import * as m2 from './js/m2.js'
// 默认暴露
export default{
//....
}
import * as m3 from './js/m3.js'
m3.defalut.change()
- 引入
import
通用导入
import * as m1 from './js/m1.js'
解构赋值
import {a,change} from './js/m1.js'
import {a as A} from './js/m2.js'
import {defalut as m3} from './js/m3.js'
// 直接使用
简便形式(针对默认暴露)
import m3 from './js/m3.js'
- babel对ES6模块化代码转换