前言
此篇文章是我二刷es6的一些想法和总结,笔记。仅用于个人以后复习,有的地方不太详细因为感觉自己已经了解了,所以不会详细写,只重点写一些区别和面试题,初学者勿入。
ES6是JavaScript语言的一次重大更新(2015),引入了很多新的语法特性,解决了一些bug
有的语法特性在日常并不会用到,我们也就少记一些,以免增加大脑负担,在这里我只列举es6常用的语法
不常用的:字符串的编码扩展,正则的扩展,数值的扩展,Symbol,Map
let和const
var
- 存在变量提升
- 存在函数作用域
- 可以声明同一个变量(覆盖),可以覆盖掉window上带的属性
let
- 不存在变量提升
- 存在块级作用域,let声明的变量只在块级作用域内有效
- 用于声明变量,不能重复声明变量
const
- 不存在变量提升
- 存在块级作用域,const声明的变量只在块级作用域内有效
- 用于声明常量,声明的常量一般大写,且不能被修改,用const声明引用数据类型时,可以修改内部数据
变量的解构赋值
数组的解构赋值
let [a,b] = [1,2,3]
console.log(a,b,c)//1,2,undefind
let [a,b,c=100] = [1,2,3]
console.log(a,b,c)//1,2,3
对象的解构赋值
对象的key值需要对应
let {a,b} = {a:1,b:2}
console.log(a,b)//1,2
字符串的解构赋值
let [a,b,c] = 'hello'
console.log(a,b,c)//h,e,l
函数参数的解构赋值
function sum([a,b]){
return a+b
}
sum([10,20])//30
字符串的扩展
锦上添花,实用性不高,适当记
模板字符串
let name = '嘟噜小脾气'
let str= `你好${name}`
console.log(str)//你好嘟噜小脾气
字符串的新增方法
includes()
查找字符串内是否包含该参数字符串
- 以前:indexOf()==> 返回 -1 || 下标
- 新增:includes()==> 返回布尔值 false || true
let name = '嘟噜小脾气'
let str = `你好${name}`
console.log(str)//你好嘟噜小脾气
console.log(name.indexOf('小'));//2
console.log(name.indexOf('小脾气'));//2
console.log(name.indexOf('小脾Q'));//-1
console.log(name.indexOf('哈哈'));//-1
console.log(name.includes('小'));//true
console.log(name.includes('小脾气'));//true
console.log(name.includes('小脾Q'));//false
console.log(name.includes('哈哈'));//false
startsWith(),endsWith()
- startsWith():查找该参数字符串是否存在于字符串开头
- endsWith():查找该参数字符串元素是否存在于字符串结尾
以上2个方法平时可能不太常用,因为以前的方法可能会更好用
startsWith('a')
str[0] == 'a'
endsWith('a')
str[str.length-1] == 'a'
repeat()
重复该字符串多少次
let name = '嘟噜小脾气'
console.log(name.repeat(3));//嘟噜小脾气嘟噜小脾气嘟噜小脾气
padStart(),padEnd()
在字符串前,后填充参数,补充字符串
let name = '嘟噜小脾气'
console.log(name.padStart(10, '0'));//00000嘟噜小脾气
console.log(name.padEnd(10, '0'));//嘟噜小脾气00000
使用场景:
给newDate()出来的时间补0
trimStart(),trimEnd()
- trim():去除字符串前后的空格
- trimStart():去除字符串前面的空格
- trimEnd():去除字符串后面的空格
replaceAll()
替换字符串
let name = '嘟噜小脾气'
console.log(name.replace(/小/g, '大大'));//嘟噜大大脾气
console.log(name.replaceAll('小', '大大'));//嘟噜大大脾气
数值的扩展
es6本身对数值操作的方法例如parseInt(),parseFloat()是挂载在window对象上的,es6认为它的分类并不合理,便把它挂载到了Number对象上,现在还能在window对象使用,也能用Number.parseInt()
函数的扩展
箭头函数
箭头函数和普通函数的区别:
- 箭头函数不能new
- 箭头函数内部没有arguments对象
- 函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。
也就是说一般箭头函数的this都指向window
普通函数this,谁调用这个函数,this就代表谁
函数扩展
- 可以添加函数参数的默认值
- ...操作符(可以用来拆分和合并数组)
面试题:合并数组的方法有哪些
let arr1 = [1, 2]
let arr2 = [3, 4, 5]
console.log(arr1.concat(arr2));//[1,2,3,4,5]
console.log([...arr1, ...arr2]);
数组的扩展
扩展运算符...
可以用来替代apply()方法
Math.max.apply(null,[1,5,3])
Math.max(...[1,5,3])
Array.from()
用于将两类对象转为真正的数组:
- 类似数组的对象(array-like object)
eg:用dom操作获取到的DOM节点对象
let lis = document.getElementsByTagName('li')
- 可遍历(iterable)的对象(包括 ES6 新增的数据结构 Set 和 Map)。
let obj = {
a: 1,
1: 2,
length: 3
}
console.log(Array.from(obj));//[ undefined, 2, undefined ]
Array.of()
用于将一组值,转换为数组。
console.log(Array.of(1, 5, 7, 9, 5, 4));//[ 1, 5, 7, 9, 5, 4 ]
find()和findIndex()
- find():返回查找到的符合条件的第一个数组元素 || undefined
- findIndex():返回查找到的符合条件的第一个数组元素下标 || -1
数组的includes()
查到返回true,没查找到返回 false
flat()
扁平化数组,默认只扁平化2层的数组,要扁平化多层数组的话,有多少层就调用几次这个方法,或者给flat()传递参数flat(num)
let arr = [1, [2, [3]]]
console.log(arr.flat().flat());
console.log(arr.flat(2));//[1,2,3]
对象的扩展
对象内的方法写法改进
let obj = {
fun:function(){
return 'fun'
}
}
let obj = {
fun(){
return 'fun'
}
}
简化对象
let obj = {str:str}
let obj = {str}//key和value一样时可以直接简化
遍历对象方法
keys(),values(),entries()
for in
循环遍历对象自身的和继承的可枚举属性(不含 Symbol 属性)
let obj = {
a: 1,
b: 2,
c: 3
}
for (let v in obj) {
console.log(v);//a b c
}
for of
适用于遍历数组元素
let obj = {
a: 1,
b: 2,
c: 3
}
for (let k of Object.keys(obj)) {
console.log(k);//a b c
}
for (let v of Object.values(obj)) {
console.log(v);//1 2 3
}
for (let [k, v] of Object.entries(obj)) {
console.log(k, v);//a 1 b 2 c 3
}
对象新增的方法
Object.is()
用来判断是否相等,主要是解决ES5之前的:==,===的问题
console.log(NaN == NaN);//false
console.log(NaN === NaN);//false
console.log(Object.is(NaN, NaN));//true
console.log(Object.is({}, {}));//false
console.log([1, 2, 3] == [1, 2, 3]);//false
console.log(Object.is([1, 2, 3], [1, 2, 3]));//对象永远不可能相等
Object.assign()!!!非常重要
用于对象的合并
let obj1 = {
a: 1
}
let obj2 = {
b: 2
}
Object.assign(obj1, obj2)//把obj2合并到obj1里
console.log(obj1, obj2);//{ a: 1, b: 2 } { b: 2 }
合并的对象是用浅拷贝,obj2值的改变会影响到合并完的obj1
let obj1 = {
a: 1
}
let obj2 = {
a: 2,
m: {
a: "111"
}
}
obj2.m.a = "222"
Object.assign(obj1, obj2)
console.log(obj1, obj2);
//{ a: 2, m: { a: '222' } } { a: 2, m: { a: '222' } }
使用场景:
配置默认参数对象后合并传过来的参数对象
let objDefault = {
inputText: '用户名xxx',
inputPwd: '密码xxx'
}
let obj = {
inputPwd: '这不是密码',
inputSum: '这是yyyy'
}
let setting = Object.assign(objDefault, obj)
console.log(setting.inputText);
Class
面向对象的三大特性:
封装
隐藏对象的属性和实现细节,仅对外提供公共访问方式,将变化隔离,便于使用,提高复用性和安全性
继承
提高代码复用性,继承是多态的前提
多态
父类或接口定义的引用变量可以指向子类或具体实现类的实例对象。提高程序的扩展性
class的基本格式
class 类名称{
constructor(){
this.属性名 = 值
}
方法1(){}
方法2(){}
}
//es5
function Person1(name) {
this.name = name
}
Person1.prototype.run = function () {
return this.name + "run"
}
let person1 = new Person1('张三')
console.log(person1.name);//张三
console.log(person1.run());//张三run
//es6
class Person2 {
constructor(name) {
this.name = name
}
run() {
return this.name + 'run'
}
}
let person2 = new Person2('李四')
console.log(person2.name);//李四
console.log(person2.run());//李四run
类继承
class 子类 extends 父类{
constructor(){
super()
this.属性名 = 值
}
方法1(){}
方法2(){}
}
class Parent {
constructor() {
this.money = '父亲的钱'
}
fatherFun() {
return '父亲的方法'
}
}
class Child extends Parent {
constructor() {
super()
this.age = 18
}
childFun() {
return '这是子类的方法'
}
}
let children = new Child()
console.log(children.money);
console.log(children.fatherFun());
私有方法和私有属性
私有方法和私有属性,是只能在类的内部访问方法和属性,外部不能访问。这是常见需求,有利于代码的封装,但ES6不提供,只能通过变通方法模拟实现。
私有方法:
-
- 在方法名前加下划线(约定俗称,但实际上还是非私有的)
-
- 使用Symbol
Symbol(基本上不会用)
Symbol是一个独一无二的值,是原始数据类型
let s = Symbol('描述')
console.log(s);//Symbol(描述)
let ss = Symbol.for('描述')//获取这个Symbol
Set和Map(Map基本上不会用)
Set()
没有重复的值,可以使用Set去重
let set = new Set([1, 2, 5, 4, 5, 1, 7, 8, 4, 4,])
console.log(set);//Set(6) { 1, 2, 5, 4, 7, 8 }
有几种去重方式:
- ES6的new Set()
- filter
- 判断逻辑的形式
Module的语法!!!
引入:import
- 全部引入:
import './script'
- 按需引入:
import {a,b,c,fun} from './script'
- 自定义名称引入:
import xxx from './script'
console.log(xxx.a)
抛出:export
export let a = 10
let fun = ()=>{}
export {fun}
let a = 10
let b = 20
export {a,b}
export default {a,b}
Promise!!!
Promise是异步编程的一种解决方案
-
功能:写异步的代码,同步地执行出来
-
好处:让代码更好地维护或者易读
-
同步:只有前一个任务执行完毕了,才能继续执行下一个任务
-
异步:不进入主线程,进入的是任务队列,只有任务队列通知主线程某个异步任务可以执行,该任务才会进入主线程。
Promise有哪几种状态:
Promise对象代表一个异步操作,有三种状态:pending(进行中),fulfilled(已成功),rejected(已失败)
async和await!!!
async函数返回的是Promise
一般async和await配合起来用:如果单独使用await会报错,await需要在有async的地方用(没有async就没有await)
await就是等待(async wait)的意思,要等标记await的语句执行完才会接着往下执行
简洁:异步编程的最高境界就是不关心它是否是异步。async、await很好的解决了这一点,将异步强行转换为同步处理。
async/await与promise不存在谁代替谁的说法,因为async/await是寄生于Promise,Generater的语法糖。
和Promise的区别
- 函数前面多了一个async关键字。await关键字只能用于async定于的函数内。async函数会隐式地返回一个Promise,该promise的resolve值就是return的值。示例中resolve的值就是字符串"aaa"
- await的是有使用限制的,await关键字只能用于async定于的函数内,如果未使用async而直接使用await就会抛SyntaxError。
为什么async/await更好?
- 使用async函数可以让代码简洁很多,不需要想Promise一样需要then,不需要写匿名函数处理Promise的resolve的值,也不需要定义多余的data变量,还避免了嵌套代码。
- async/await 让 try/catch可以同时处理同步和异步的错误。