ES6介绍
ECMA ( European Computer Manufacturers Association ) 中文名为欧洲计算机制造商协会, 这个组织的目标是评估, 开发和认可电信和计算机标准. 1994年后该组织改名为Ecma国际.
什么是ECMAScript
- ECMAScript 是由 Ecma 国际通过 ECMA-262标准化的脚本程序设计语言.
什么是 ECMA-262
- Ecma国际制定了许多标准, 而 ECMA-262 只是其中的一个, 所有标准列表查看 www.ecma-international.org/publication…
在线版ECMA文档:tc39.es/ecma262/#se…
ECMAScript发展历史
| 第 1 版 | 1997 年 | 制定了语言的基本语法 |
|---|---|---|
| 第 2 版 | 1998 年 | 较小改动 |
| 第 3 版 | 1999 年 | 引入正则、异常处理、格式化输出等。IE开始支持 |
| 第 4 版 | 1999 年 | 过于激进,未发布 |
| 第 5 版 | 2009 年 | 引入严格模式、JSON、扩展对象,数组、原型、字符串、日期方法 |
| 第 6 版(ES6) | 2015 年 | 模块化、面向对象语法、Promise、箭头函数、let、const、数组解构赋值等等 |
| 第 7 版 | 2016 年 | 幂运算符、数组includes扩展 |
| 第 8 版 | 2017 年 | 字符串扩展、Async/await关键字 |
| 第 9 版 | 2018 年 | 对象解构赋值、正则扩展 |
| 第 10 版 | 2019 年 | 扩展对象、数组、字符串方法 |
| ES.next | 动态指向下一个版本 |
注:从ES6开始,每年发布一个版本,版本号比年份最后一位大1。
ES5之后的JS语法统称ES6!!!
谁在维护 ECMA-262
TC39(Technical Committee 39)是推进 ECMAScript 发展的委员会。 其会员都是公司。(其中主要是浏览器厂商,有苹果、谷歌、微软、因特尔等)。TC39定期召开会议,会议由会员公司的代表与特邀专家出席。
会议的内容: 制定ECMAScript标准,标准生成的流程,并实现。
一个提案在成为标准之前会经历下面的步骤:
- 草案( Sketch )(非正式地:“普通人提案”):提案特性的第一个描述。
- 提案( Proposal ):如果 TC39 认为某个特性是重要的,那么此特性就上升为官方提案状态。这并不会保证最终会成为标准,但是大大地增加了成为标准的可能性。 ES6 提案的截止日期是2011年5月,在这之后不会考虑重大的新提案。
- 实现( Implementations ):提案特性必须被实现,在理想情况下,要支持两种 JavaScript 引擎。在提案获得提升的时候,来自于社区的实现和反馈决定了提案的样子。
- 标准( Standard ):如果提案持续检验自身,并且被 TC39 接受,那么该提案将最终包含进 ECMAScript 标准的一个版本中。此时,就成了一个标准特性。
TC39会议内容: github.com/tc39/notes/…
为什么要学习 ES6
- ES6 的版本变动内容较多,具有里程碑意义。
- ES6 加入许多新的语法特性,编程实现更简单、高效
- ES6 是前端发展趋势,就业必备技能
ES6 兼容性(97%)
kangax.github.io/compat-tabl… 可查看兼容性
不兼容es6+语法的浏览器,后面会借助babel插件,将其转化es5。
常见的ES6新特性
- let、const
- 模板字符串
- 对象简写
- 解构赋值
- 剩余参数&扩展运算符
- 箭头函数
- class声明类 、extends来实现继承
- 数组、字符串、正则扩展方法
- Object扩展方法
- Symbol基本数据数据,表示独一无二的唯一值
- promise
- async/await
- ....
JS严格模式
-
严格模式通过抛出错误来消除了一些原有的静默错误
-
严格模式修复了一些导致javascript引擎难以执行优化的缺陷,严格模式通常比非严格模式下运行更快
开启严格模式:
- 整个脚本开启严格模式
<script> "use strict"; </script>- 函数内开启
function foo(){ "use strict"; }
在严格模式下,常见的变化如下
-
声明变量不加关键字var或let,会报错
-
全局中this不是window,而是指向undefined
-
不能通过delete删除普通变量。注意:但是可以删除对象中的属性
<script> 'use strict' let a = 1 var b = 2 // c = 3 // 严格模式下不加关键字声明变量会报错 function fn() { console.log(this) } fn() // 严格模式下,没有调用者的this会指向undefined而不是window let c = 1 // delete c // 不能删除普通变量,但是可以删除对象属性 const obj = { c: 1, d: 2 } delete obj.c console.log(obj) // {d:2} </script>
Symbol基本数据类型
目前基本属性类型:string,number,boolean,undfined,null,symbol。
什么是symbol
symbol是es6中新引出的一种基本数据类型,表示独一无二的值。
每一次通过Symbol()返回的symbol值都是唯一的。
let p1 = Symbol('张三')
let p2 = Symbol('张三')
console.log(p1 === p2) // false
Symbol.for(),可以创建共享的Symbol,有全局缓存的特点
// 使用Symbol.for() 创建共享的Symbol,有全局缓存特点。如果已经定义过,就会返回原来的值
let person = Symbol.for('李四')
let person1 = Symbol.for('李四')
console.log(person === person1) // true
symbol类型应用场景
最合理的用法就是用Symbol创建的值作为对象的key,防止命名冲突。
<script>
/* let users = {
张三: { age: 18, sex: '男' },
张三: { age: 20, sex: '女' },
}
console.log(users) // 产生命名冲突问题,同名的张三,后面的会覆盖前面的,只有一个 */
// 最合理的使用Symbol创建的值作为对象的key,防止命名冲突
let key1 = Symbol('张三')
let key2 = Symbol('张三')
console.log(key1) // Symbol(张三)
console.log(key2) // Symbol(张三)
let users = {
[key1]: { age: 18, sex: '男' },
[key2]: { age: 18, sex: '女' },
}
console.log(users) // 两个key都是(Symbol(张三)),各自拥有自己的值
console.log(users[key2]) // { age: 18, sex: '女' }
</script>
symbol特点
-
Symbol值不能和其他数据进行运算
let a = Symbol(1) console.log(a + 1) // 报错 -
Symbol函数不能使用new,因为Symbol不是一个构造器
// 报错:Uncaught TypeError: Symbol is not a constructor new Symbol() -
Symbol创建的属性名无法被枚举出来,即无法使用常规遍历出属性名。可以使用
Object.getOwnPropertySymbols()进行遍历,会返回所有Symbol属性的数组// Symbol创建的属性名无法被枚举,即无法被普通的遍历出来,只能通过Object.getOwnPropertySymbols()进行遍历 const obj = { a: 1, b: 2, [Symbol('c')]: 3, [Symbol('d')]: 4, } console.log(obj) // 都只能遍历自身的属性名,不能遍历Symbol创建的属性名 for (var k in obj) { console.log(k) // a b } console.log(Object.getOwnPropertyNames(obj)) // 返回数组['a','b'] console.log(Object.keys(obj)) // 返回数组['a','b'] // 通过Object.getOwnPropertySymbols()遍历,返回所有Symbol属性的数组 console.log(Object.getOwnPropertySymbols(obj)) // ['Symbol(c),Symbol(d)']
let、const关键字
let的特点
- 声明的变量属于块级作用域(block scope)
- 没有变量提升(存在TDZ),Temporal Dead Zone(暂时性死区)
暂时性死区TDZ(Temporal Dead Zone): 起始于函数的开头,终止与相关变量声明的一行。在这个范围内无法访问let或const声明的变量。这块死区就是TDZ。
- 不能声明相同的变量
- 声明的全局变量和函数不会成为全局对象window的属性
const特点
-
声明的量属于常量,常量也属于块级作用域,必须要赋予初始值,且后续不能更改
-
常量的名字一般约定为全部大写
// 常量的特点:1.必须有初始值。2.不能修改常量的值。3.属性块级作用域 // 常量名全大写 if (true) { const PI = 3.14 console.log(PI) } // console.log(PI) // 块级作用域报错
注意:修改对象属性和数组元素不会发出const错误
const obj = {
name: '张三',
age: 20,
}
const fruits = ['苹果', '梨子', '榴莲']
// obj = 3 // 报错,常量const声明的不能修改覆盖,但是可以修改属性
obj.age = 22
console.log(obj)
// fruits = [] // 报错
fruits[2] = '西瓜'
console.log(fruits) // ’['苹果', '梨子', '西瓜']‘
let与const的使用场景
-
对于需要保护(防止被覆盖)的变量使用const
-
只有确定要改变变量的值的时候用let
因为不停变化的变量值就是很多bug的源头
-
声明对象类型使用const,非对象类型使用let。
模板字符串
用一对反引号包裹起来的部分就是模板字符
`${表达式}`
let name = '张三'
let age = 18
function hello(name) {
return '你好:' + name
}
let obj = {
sex: '男',
}
let info = `my name is ${name},age is ${age},性别:${obj.sex}
欢迎语:${hello(name)}
`
console.log(info)
作用
用于字符串和变量的拼接,且支持换行操作
表达式的特点:一定会产生一个值
其表达式的值可以是变量、属性调用、函数调用、三元运算符等。
对象简写
可以进行属性简写,方法简写
当属性的key与value相同时,可以进行简写。方法也可以省略function
建议简写的属性定义在前面
let name = '张三'
let age = 18
let user1 = {
name: name,
age: age,
getName: function () {
return `my name is ${this.name}`
},
}
console.log(user1.getName())
// 上面user1等价于user2:
let user2 = {
name,
age,
getName() {
return `my name is ${this.name}`
},
}
console.log(user2.getName())
解构赋值
定义:可以对某个数据结构进行解开再把值赋给其他变量
对象的解构赋值
语法:解构时,可以设置别名和默认值,语法:
- 属性名:别名,给属性名设置别名,后续引用只能用别名
- 属性名 = 默认值,给属性设置默认值,当没有定义值会使用默认值,定义了值就使用定义的值。
let {属性名,属性名:别名,属性名=默认值} = 对象
eg:
const obj = {
name: '张三',
age: 18,
email: 'zs@qq.com',
// hobby: '打篮球',
}
// 这里可以有别名也可以有默认值
let { name: myName, age, email, hobby = '唱跳rap' } = obj
let info = `my name is ${myName},my age is ${age},my email is ${email},my hobby is ${hobby}`
console.log(info)
对象的参数进行解构
// 对对象形式参数进行解构赋值
function test({ name: myName, email, age, hobby = '跳舞' }) {
console.log(`${myName}-${age}-${email}-${hobby}`)
}
const obj = {
name: '张三',
age: 18,
email: 'zs@qq.com',
hobby: '唱歌',
}
test(obj)
数组的解构赋值
// 解构赋值,a接受数组第一个元素,b接受数组第二个元素
const arr = [1, 2]
let [a, b] = arr
console.log(a, b)
// 省略前几个数组元素
const user = ['张三', '李四', '王五']
// const [zs, ls, ww] = user
// console.log(zs, ls, ww) // '张三' '李四' '王五'
const [, , ww] = user
console.log(ww)
const [zs, ,] = user
console.log(zs)
// 交换两个变量的值
let num1 = 10
let num2 = 20
// 这里需要加要给分号!防止浏览器解析导致错误
;[num1, num2] = [num2, num1]
console.log(num1, num2) // 20 10
扩展(剩余)运算符
它的作用是将一个数组或对象展开,将其中的元素或属性逐一赋值给新的变量。
const arr = [1,2,3]
const arr2 = [...arr]
console.log(arr2) // [1,2,3]
... 三个点加参数名。作用:
- 获取函数所有的实参或部分实参,可以替换arguments
function sum(...args) {
console.log(args) // [12,11,13]
console.log(arguments) // Arguments(3)[12,11,13]
}
sum(12, 11, 13)
- 合并对象或数组
// 2 合并对象或数组
const obj = {
name: 'zs',
age: 18,
address: { city: 'sz', area: '宝安' },
}
const obj2 = {
hobby: '唱歌',
say: function () {
console.log('hello')
},
...obj,
}
console.log(obj2)
// const mergeObj = { ...obj, ...obj2 }
// console.log(mergeObj)
const arr1 = [1, 2, 3]
const arr2 = [...arr1, 4, 5, 6]
// const mergeArr = [...arr1, ...arr2]
// console.log(mergeArr)
console.log(arr2)
- 接收数组或对象剩余元素
// 3 获取剩余元素
const family = ['dad', 'mom', 'son1', 'son2', 'son3']
let [dad, mom, ...sons] = family
console.log(dad, mom, sons) // 'dad' 'mom' ['son1','son2','son3']
const user = {
name: '张三',
age: 19,
hobby1: '唱歌',
hobby2: '跳舞',
hobby3: 'rap',
hobby4: '篮球',
}
let { name, age, ...hobby } = user
console.log(name, age, hobby) // ['张三',19,Array(4)]
箭头函数
cosnt fn = ()=>{} 等价于const fn = function(){}
箭头函数和普通函数的区别
-
箭头函数中没有this指向,不会绑定this的指向,this还是保留上一层作用域中的指向
// 1. 无绑定的this指向,箭头函数中的this仍是上一层作用域的this指向,箭头函数不会更改this指向 var age = 30 const obj = { myName: '张三', age: 19, getAge: function () { console.log('age is ', this.age) }, getAge2: () => { console.log('age is ', this.age) }, getName: function () { console.log('name is ', this.myName) }, getName2: () => { console.log('name is ', this.myName) }, } obj.getAge() // age is 19 ,this为obj obj.getAge2() // age is 30 ,this为window obj.getName() // name is 张三 ,this为obj obj.getName2() // name is undefined ,this为window -
箭头函数中没有arguments,若要获取全部实参可以使用扩展运算符(...args)
const fn1 = function () {
console.log(arguments)
}
fn1(1, 2, 3) // Arguments(3)
/* const fn2 = () => {
console.log(arguments)
}
fn2(1, 2, 3) // 报错 arguments is not defined */
const fn3 = (...args) => {
console.log(args)
}
fn3(1, 2, 3) // [1,2,3]
-
箭头函数没有构造器constructor,不能使用new操作符
const Person = function (name, age) { this.name = name this.age = age } console.log(new Person('张三', 20)) // Person{name:'张三',age:20} /* const User = () => {} console.log(new User()) // 报错 User is not a constructor */
可选链运算符
可选链运算符?. 可以读取对象深层的属性值,可以省去多余的判断步骤
const obj = {
name: '张三',
age: 22,
address: {
city: '深圳',
area: '宝安',
},
}
const obj2 = {
name: '李四',
age: 20,
city: '深圳',
area: '宝安',
}
console.log(obj.address.area) // '宝安'
// console.log(obj2.address.area) // 报错 Cannot read properties of undefined (reading 'area')
console.log(obj?.address?.area) // '宝安'
console.log(obj2?.address?.area) // undefined ,不会报错
class声明类
js中没有class类的概念,es5中只能通过构造函数和原型对象结合的方式来模拟类的效果,es6中可以直接通过class来定义一个类
- es5实现构造函数
// es5实现构造器
function Person(name, age) {
this.name = name
this.age = age
}
Person.prototype.getName = function () {
console.log('my name is', this.name)
}
Person.prototype.setName = function (newName) {
this.name = newName
}
const p1 = new Person('zs', 18)
console.log(p1)
p1.getName() // 'zs'
p1.setName('李四')
p1.getName() // '李四'
- es6通过class来模拟一个类(构造函数)
// es6实现构造器
class Person1 {
// 构造函数,用于初始化操作,new的时候会立即执行
constructor(name, age) {
this.name = name
this.age = age
}
getName() {
console.log('my name is', this.name)
}
setName(newName) {
this.name = newName
}
}
console.log(Person1.prototype.constructor === Person1) // true
const p2 = new Person1('zs', 18)
console.log(p2)
p2.getName() // 'zs'
p2.setName('李四')
p2.getName() // '李四'
extends继承
// 父类
class Animal {
constructor(name) {
this.name = name
}
drink() {
console.log(this.name + '在喝水')
}
}
// 子类 extends 父类
class Dog extends Animal {
constructor(name, age) {
// 必须执行父类的构造函数
super(name)
this.age = age
}
eat() {
console.log(this.name + '吃骨头')
}
}
const dog = new Dog('大黄', 5)
console.log(dog) // Dog {name: '大黄', age: 5}
dog.drink() // 大黄在喝水
dog.eat() // 大黄吃骨头
静态属性与静态方法
- 成员(实例)属性:可以通过对象直接访问到的属性,称之为实例属性
- 静态属性/方法:由类名设置的属性和方法。作用:一般用来实现一些辅助功能
静态属性/方法不属于对象,不能通过对象操作,仅能用类名(构造函数名)操作
es5中的静态属性和静态方法
// Person是构造函数的名字
function Person(name){
this.name = name;
}
// 添加静态属性
Person.sex = '男';
// 添加静态方法
Person.say = function(){
console.log('我是静态方法say')
}
// 静态的属性或方法只能用构造函数名去调用
console.log(Person.sex) // 男
Person.say() // 我是静态方法say
const p1 = new Person('zs')
console.log(p1.sex) // undefined
// p1.say() // 报错 p1.say is not a function
es6中的静态属性和静态方法
// 静态属性只能由类名去访问或设置,不能通过对象去操作
class Person {
constructor(name) {
this.name = name
}
sex = '男'
static hobby = '唱歌'
say() {
console.log('hello')
}
static eat() {
console.log('按时吃饭')
}
}
const p1 = new Person('张三')
console.log(p1)
p1.say() // hello
console.log(p1.sex) // 男
console.log(Person.hobby) // 唱歌
console.log(p1.hobby) // undefined
Person.eat() // 按时吃饭
// p1.eat() // 报错 p1.eat is not a function
Array的静态方法
Array.from(): 将伪数组转化为真数组Array.isArray():判断某个变量是否是数组
Object常用的静态方法
Object.keys()和Object.values()
Object.keys(),常用:返回对象的所有的key到一个数组中。但不含symbol的keyObject.values():返回对象的所有value到一个数组中
const obj = {
name: '张三',
age: 19,
sex: '男',
}
// Object.keys() 与 Object.values()
console.log(Object.keys(obj)) // ['name', 'age', 'sex']
console.log(Object.values(obj)) // ['张三', 19, '男']
Object.assign()
-
Object.assign(target,source):将源对象身上中的属性复制到目标对象中,若有同名属性,将会覆盖目标对象。此方法返回修改后的目标对象
// Object.assign(target,source)
const target = { a: 1, b: 2 }
const source = { b: 3, d: 4 }
Object.assign(target, source)
console.log(target) // {a:1,b:3,d:4}
- 作用:可以合并默认参数。(即给一个默认的值,当自身有该值时则覆盖,没有时则使用默认的值)
// 可以合并默认参数
// 对指定的url地址发起get或post请求
function request(data) {
const defaultParams = {
type: 'get',
}
// 将data与默认参数合并
const params = Object.assign(defaultParams, data)
console.log(params)
}
request({ type: 'post', url: '1.1.1.1' })
request({ type: 'get', url: '1.1.1.2' })
request({ url: '1.1.1.3' }) // {type:'get',url:'1.1.1.3'}
Object.create()
Object.create()方法用于创建一个新对象,使用现有的对象来作为新创建对象的原型(prototype)
// Object.create(), 创建一个新对象,并将现有的对象作为新创建对象的原型
const Person = {
name: '张三',
getName() {
console.log('my name is ' + this.name)
},
}
const obj1 = Object.create(Person)
console.log(obj1.__proto__ === Person) // true
- 可以利用
Object.create(null)创建一个没有任何原型的对象,即非常干净的对象
//创建一个没有任何原型的对象,即非常干净的对象
const myObj = Object.create(null)
console.log(myObj) // {} No properties
- 使用es5实现
Object.create(null)源码
cosnt obj = {}
// obj.__proto__ = null // 可以实现,但不建议使用
Object.setPrototypeOf(obj,null) // 推荐
Object.is()
Object.is()判断两个值是否为同一个值,可以判断NaN等于NaN
console.log(+0 == -0) // true
console.log(+0 === -0) // false
console.log(null == undefined) // true
console.log(null === undefined) // false
console.log(NaN == NaN) // false
// Object.is() 判断两个值是否为同一个值,可以判断NaN
console.log(Object.is(+0, -0)) // false
console.log(Object.is(null, undefined)) // false
console.log(Object.is(NaN, NaN)) // true
Object.defineProperty()
Object.defineProperty(对象名,'属性名',{属性描述符})给对象添加或修改属性,对属性有更加细粒度的控制。可以控制属性是否可以被枚举、删除、修改等。
返回修改后的原对象
属性描述符:
-
数据描述符
-
value:属性值
-
enumerable: 是否可枚举,默认为false
-
writable: 是否可被修改,默认为false
-
configurable: 是否可以被删除,默认为false
const obj = { name: '张三', age: 18, } // 使用Object.defineProperty()添加属性 Object.defineProperty(obj, 'hobby', { value: '唱歌', enumerable: false, writable: true, configurable: true, })// 使用Object.defineProperty()修改属性,修改属性时,属性描述符的默认值不会生效,保持原属性的状态,但可以通过设置来改变。 Object.defineProperty(obj, 'age', { value: 28, configurable:false, }) obj.hobby = '跳舞' obj.age = 22 console.log(Object.keys(obj)) // name age // delete obj.hobby delete obj.age console.log(obj) // {name: '张三', age: 22, hobby: '跳舞'}
-
-
存储描述符
-
get:读取数据时拦截
-
set:修改数据时拦截
const obj = { name: '张三', } let age = 18 Object.defineProperty(obj, 'age', { get() { console.log('getter') return age }, set(newAge) { console.log('setter触发了') age = newAge }, }) console.log(obj) obj.age = 22 // setter触发了 console.log(obj.age) // getter触发了 22 -
注意,上面对属性设置的描述可以分为两大类:
| configurable | enumerable | value | writable | get | set | |
|---|---|---|---|---|---|---|
| 数据描述符 | 可以 | 可以 | 可以 | 可以 | 不可以 | 不可以 |
| 存取描述符 | 可以 | 可以 | 不可以 | 不可以 | 可以 | 可以 |
通过defineProperty设置symbol属性同样也是无法被枚举的,只能通过Object.getOwnPropertySymbols去获取
迭代器iterator
迭代器(Iterator)是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要实现了 Iterator 接口,就可以通过for-of实现遍历操作。
一句话:可以让不支持遍历的数据结构 -》 可遍历
原生具备iterator接口的数据(即都可用for of遍历)
- Array
- Arguments
- Set
- Map
- String
- NodeList
- ....
let str = 'abc'
for (let item of str) {
console.log(item) // a b c
}
const arr = ['red', 'green', 'blue']
for (let item of arr) {
console.log(item) // red green blue
}
function sum() {
console.log(arguments)
for (let item of arguments) {
console.log(item) // 1 2 3
}
}
sum(1, 2, 3)
// 通过迭代器对象next方法,每次获取单个值。
const iterator = arr[Symbol.iterator]()
console.log(iterator.next()) // {value: 'red', done: false}
console.log(iterator.next()) // {value: 'green', done: false}
console.log(iterator.next()) // {value: 'blue', done: false}
console.log(iterator.next()) // {value: undefined, done: true}
注意:next一次仅能获取一次值。要获取所有可以用for-of来进行迭代
迭代器工作原理
- 创建一个指针对象,指向当前数据结构的起始位置
- 第一次调用对象的next方法,指针自动指向数据结构的第一个成员
- 接下来不断调用next方法,指针一直往后移动,直到指向最后一个成员
- 每调用next方法返回一个包含value和done属性的对象
实现迭代器需要的协议
-
可迭代协议: 含有Symbol.iterator属性。
-
迭代器协议:
- 必须返回一个对象 - 返回的对象要有next方法 - next方法也要返回一个对象,并且有value 和 done两个属性 return { next(){ return { value:xxx, // value当前迭代的值 done:boolean // true 遍历完毕, false 没有遍历完毕,可继续遍历 } } }
汇总常用的循环技巧
- for/while/do-while循环: 作用:循环数组、字符串
- for-in: 循环对象可枚举的属性和原型链上的属性和方法,不包含Symbol。得到Symbol类型的属性用
Object.getOwnPropertySymbols() - forEach: 循环数组和部分伪数组(querySelectorAll)
- map/filter/reduce/some/every/find/findIndex:用于数组
- for-of: 用来迭代具有实现迭代器(Symbol.iterator)接口的对象
伪数组和真数组
-
伪数组(likeArray):和真数组差不多,可以通过下标索引取值,也有
length属性,但不能调用真数组的方法如push。如arguments,getElementsByTagName,querySelectorAll等返回都是伪数组。const obj = { 0: 'red', 1: 'green', 2: 'blue', length: 3, } for (let i = 0; i < obj.length; i++) { console.log(obj[i]) } // obj.push('yellow') // obj.push is not a function 伪数组不是数组
伪数组转真数组
- [...arrayLike],伪数组转成真数组(要求伪数组具有迭代器接口(Symbol.iterator))
- Array.from()
- Array.prototype.slice.call()
function sum() {
console.log(arguments)
// 方式1:伪数组转成真数组(要求伪数组具有迭代器接口(Symbol.iterator))
// const args = [...arguments]
// 方式2:
// const args = Array.from(arguments)
// 方式3:
// const args = Array.prototype.slice.call(arguments)
const args = [].slice.call(arguments)
args.push(4)
console.log(args)
}
sum(1, 2, 3)
Set集合
Set集合,es6提供的一个新数据结构,类似于数组,但成员的值都是唯一的,同时实现了iterator接口,所以可以使用for..of进行遍历
Set中的元素是唯一的!
let set = new Set([1,2,2,3,3,4,4,5])
console.log(set) // Set(5){1,2,3,4,5}
常用的set集合API
-
add 添加元素,支持链式操作
set.add(6).add(7).add(8) -
delete 删除
set.delete(5) -
clear 清空
set.clear() -
size 返回集合中唯一元素的个数
set.size -
has 检查集合中是否包含某个元素,返回boolean
set.has(2); -
set 的遍历
// forEach set.forEach(item=>{ console.log(item); }) // 实现了iterator接口 可以使用for of进行遍历 for(let item of set){ console.log(item); }
set应用场景
-
将集合set转为数组
Array.from(set) // 或者 [...set] -
实现数组去重
// 实现数组去重 const arr = [1, 2, 3, 2, 3, 1] // 思路:1.转成集合 2. 在转回数组 const newArr = [...new Set(arr)] console.log(newArr)
Set和数组区别
- 重复值:Set中不允许有重复的值,而数组中可以有重复的值。
- 检索:Set没有提供像数组一样的索引访问方式,它只能通过迭代器或转换为数组后进行访问。而数组可以通过索引访问任何一个元素。
- 功能:Set是一种集合类型,主要用于判断值是否在集合中存在。而数组提供了一系列的操作,例如增加、删除、查找、排序、过滤等
以下是一些常用操作的比较:
| 操作 | Set | 数组 |
|---|---|---|
| 创建 | new Set() | [] 或 Array() |
| 添加元素 | set.add() | arr.push() |
| 删除元素 | set.delete() | arr.splice() |
| 检查元素是否存在 | set.has() | arr.includes() |
| 元素个数 | set.size | arr.length |
| 转换为数组 | Array.from(set) 或 [...set] |
Map映射
介绍:
- ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合。
- Map中的key可以是任意数据类型,而对象Object中的key只能是字符串或Symbol类型。
- Map也实现了iterator接口,所以可以使用『扩展运算符』和『for…of…』进行遍历。
Map常用的API
- size 返回Map的元素个数
- set 增加一个新元素,返回当前Map
- get 返回键名对象的键值
- has 检测Map中是否包含某个元素,返回boolean值
- delete 函数对应key
- clear 清空集合
let obj = {a:3}
myMap.set('a', 'a1')
myMap.set(1, '2').set(2, '3')
myMap.set(obj, '4')
console.log(myMap.get('a')) // 'a1'
console.log(myMap.has(obj)) // true
console.log(myMap.size) // 3
console.log(myMap) // Map(4) {'a' => 'a1', 1 => '2', 2 => '3', {…} => '4'}
myMap.clear()
console.log(myMap) // Map(0) {}
map的遍历
const map1 = new Map()
map1.set('a', 'a1')
map1.set('b', 'b2')
// for..of来迭代
for (var item of map1) {
console.log(item) // ['a','a1'] , ['b','b2']
}
// 或
console.log([...map1]) // [ [ 'a', 'a1' ], [ 'b', 'b2' ] ]
// 解构赋值
for (var [k, v] of map1) {
console.log(k, v) // a a1 , b b2
}
Map 和 Object区别
- Map可以以任意类型数据作为key,而Object只能是string类型或Symbol类型。
- Map可以迭代(for-of),object不可以
- map是有序的,Object不能保证顺序
- 常用API操作
| 操作 | 对象 | Map |
|---|---|---|
| 创建 | {} 或 new Object() | new Map() |
| 添加键值对 | obj[key] = value | map.set(key, value) |
| 删除键值对 | delete obj[key] | map.delete(key) |
| 检查键是否存在 | key in obj | map.has(key) |
| 获取值 | obj[key] 或 obj.key | map.get(key) |
| 键值对数量 | Object.keys(obj).length | map.size |
| 迭代器 | Object.keys(obj) | [...map] |
扩展的字符串函数
-
str.padStart(length,val)开头填充字符串,第一个参数为需要填充的后的总长度,第二个参数为填充的值 -
str.padEnd(length,val)末尾填充字符串console.log( "5".padStart(5,0) ) // '00005' console.log( "5".padEnd(3,0) ) // '500' console.log( "5".padEnd(3,'abc') ) // '5ab' -
str.startsWith判断一个字符串是否以特定字符串开头,满足返回true,否则返回false -
str.endsWith判断一个字符串是否以特定字符串结尾,满足返回true,否则返回falseconsole.log( 'v-model'.startsWith('v-') ); // true console.log( 'modelhtmltext'.startsWith('model') ); // true console.log( 'modelhtmltext'.startsWith('text') ); // true -
str.trim去除两边连续的空格 -
str.trimStart去除开头连续的空格 -
str.trimEnd去除结尾连续的空格console.log( ' abc ' ); // ' abc ' console.log( ' abc '.trim() ); // 'abc' console.log( ' abc '.trimStart() ); // 'abc ' console.log( ' abc '.trimEnd() ); // ' abc'
扩展的数值函数
-
Math.pow(n,m): 返回以n为底的m次方。
或 n**m
-
Number.parseInt():返回转换值的整数部分
-
Number.parseFloat():返回转换值的浮点数部分
-
Number.isNaN():是否为NaN
-
Number.isInteger():是否为整数
-
Math.trunc():返回数值整数部分
扩展的数组函数[重要]
常用的有: forEach、map、filter、every, some, find, findIndex,reduce。
上面方法特点:
- 都是不可变(非破坏性)方法
- 除了forEach函数没有返回值,其他函数都有返回值。
每个函数的作用:
- forEach: 对数组中每个元素进行遍历,此方法没有返回值
- map: 对数组中每个元素进行加工处理,返回一个加工后的新数组
- filter: 对数组中的元素进行筛选,返回一个筛选后的新数组
- find: 返回数组中第一个满足指定条件的元素
- findIndex: 返回数组中第一个满足指定条件的元素下标
- every: 若数组中每个元素都满足指定的条件,才返回true,否则返回false
- some: 若数组中只要有一个元素满足指定的条件,就返回true,否则返回false
- reduce: 连续操作器
reduce函数(连续操作器)
语法:
arr.reduce(callback(accumulator,value,[index],[originArr]),[initValue])
callback四个参数:
- accumulator 累计器
- value 当前值
- index 可选,当前索引
- originArr 可选,原数组
initialValue可选,累加器函数初始值
reduce连续操作器特点
- 若传了初始化值initValue,则从数组下标0 开始循环
- 若没传初始化值initValue,则将数组下标为0的值作为初始值,从下标1开始循环
- callback函数返回的累计器的值会作为下一次循环累计器的值,reduce函数最终会得到最后一次累计器的值
const arr = [100, 200, 300]
// 不设置默认值,会将下标为0的值作为初始值,从下标1开始循环
let res = arr.reduce((accumulator, value, index, originArr) => {
// 1 200 100 2 300 300
console.log(index, value, accumulator)
return accumulator + value
})
// reduce函数最终返回最后一次累计器的值 (即 300+300)
console.log(res) // 600
const arr = [100, 200, 300]
// 设置默认值,会将默认值作为初始值,从下标0开始循环
let res = arr.reduce((accumulator, value, index, originArr) => {
// 0 100 1000 1 200 1100 2 300 1300
console.log(index, value, accumulator)
return accumulator + value
}, 1000)
// reduce函数最终返回最后一次累计器的值 (即 1300+300)
console.log(res) // 1600
flat扁平化数组
flat() :实现数组扁平化。即将多维数组变为一维数组。
flat(depth) // depth深度默认1, Infinity为任意深度
作用:扁平化嵌套数组
var arr1 = [1, 2, [3, 4]];
arr1.flat();
// [1, 2, 3, 4]
var arr2 = [1, 2, [3, 4, [5, 6]]];
arr2.flat();
// [1, 2, 3, 4, [5, 6]]
var arr3 = [1, 2, [3, 4, [5, 6]]];
arr3.flat(2);
// [1, 2, 3, 4, 5, 6]
//使用 Infinity,可展开任意深度的嵌套数组
var arr4 = [1, 2, [3, 4, [5, 6, [7, 8, [9, 10]]]]];
arr4.flat(Infinity);
// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
扩充的正则功能
字符串matchAll函数
matchAll:返回正则匹配的所有结果及捕获组的迭代器
matchAll要求正则必须有标识符g
捕获组命名:给捕获组内容起别名,方便引用
语法:(?<别名>reg)
let reg = /(?<first>1[3-9]\d\)d{8}/
let str = 'aaa13811112222sbb18733334444rqe'
let reg = /(?<first>1[3-9]\d)\d{4}(?<second>\d{4})/g
console.log(str.match(reg))
let res = str.matchAll(reg)
console.log(res)
// for (let item of res) {
// console.log(item)
// }
console.log([...res])
解决深拷贝中的互相引用问题
互相引用:一个或多个对象中某个属性互相引用
-
对象自身引用
const obj = {a:1} obj.b = obj -
对象互相引用
const obj1 = {a:1} const obj2 = {b:2} obj1.c = obj2 obj2.d = obj1
在递归深拷贝中,如果对象中存在互相引用的问题,会导致递归无法停止,从而导致调用栈溢出。
报错信息:Maximum call stack size exceeded 超过调用栈大小
解决思路:
- 创建一个map容器作为缓存容器,每次递归时将对象存储其中
- 在开始递归之前,先判断缓存中是否存有相同的对象,如果有则直接返回而不会继续执行递归
function deepCopy(target, cacheMap = new Map()) {
// 如果是基本类型则直接返回
if (typeof target !== 'object') {
return target
}
// 如果之前map容器中有相同的key则直接返回对应的value而不继续拷贝
if (cacheMap.has(target)) {
return cacheMap.get(target)
}
const data = Array.isArray(target) ? [] : {}
// 将target与data存储在一个map容器中,target作为key,data为value
cacheMap.set(target, data)
if (Array.isArray(target)) {
target.forEach((value) => {
data.push(deepCopy(value, cacheMap))
})
} else {
for (let k in target) {
data[k] = deepCopy(target[k], cacheMap)
}
}
return data
}
es6常用操作
- let const
- 解构赋值
- 扩展运算符
- 箭头函数
- 数组方法:forEach/map/filter/find/findIndex/every/some/reduce
- Object.keys(),Object.values()
- 集合Set:元素一定是唯一的
- 映射Map:可以以任意数据类型当作key