ECMAScript是欧洲计算机协会提出的规范,我们JS的语法就是用这个语法,Javascript分为三部分ECMAScript + DOM + BOM
IE8浏览器支持ES3,IE9之后支持ES5,没有ES4,因为ES4的修改过于激进,所以没有成为标准。2015年ES2015标准提出。从这个标准之后,ES版本都是以年份作为描述,前端界还会以数字称呼,称为ES6。每一年ECMAScript都会新增一些新的api,但是新增的内容都比较少,所以我们统称后续的ECMAScript为ES6+。
新出的标准浏览器兼容性不好,有一个插件应运而生Babel
,可以帮助我们把ES6代码转换成ES5代码,基本上,这个转换过程是无感的。所以我们在开发时,可以肆无忌惮使用ES6语法,也推荐大家使用ES6语法。
箭头函数
箭头函数是函数的一个新的写法,它能够带来更简便的函数写法。
let fn = () => {}
把原本的function删除,然后在()后面添加了=>
,所有的箭头函数都是匿名函数。
箭头函数可以简化的写法
我们使用箭头函数函数时会遇到一下简化写法
- 只有一个参数
- 只有返回值
- 只有返回值且返回值是一个对象
只有一个参数
只有一个参数时,不需要添加 ()
可以省略
正常函数
let fn = function (参数) {}
转成箭头函数
let fn = (参数) => {}
简化后
let fn = 参数 => {}
只有返回值
正常函数
let fn = function () {
return "返回值"
}
箭头函数
let fn = () => {
return "返回值"
}
简化后
let fn = () => "返回值"
返回值如果是对象
普通函数
let fn = function () {
return {key: "value"}
}
箭头函数
let fn = () => {
return {key: "value"}
}
简化
let fn = () => ({key: "value"})
注意!!!!!推荐大家使用箭头函数,但是对象中的方法不要用箭头函数,事件函数不要用箭头函数,因为箭头函数的this指向和普通函数不一样。
箭头函数的this指向问题
箭头函数中的this指向谁!!!声明箭头函数时,箭头函数所在作用域的this指向。
箭头函数的这个特性解决了什么问题呢???
btn.onclick = function () {
this.innerText = "加载中……"
// let _this = this
axios.get("https://cnodejs.org/api/v1/topics").then((res) => {
console.log(this)
// 使用了箭头函数,我们就不需要再事先获取到this,然后再使用新的变量
this.innerText = "加载完成"
// _this.innerText = "加载完成"
})
}
分析之前的代码
fetch(url).then(res => res.json())
let arr = [1,2,3,4]
arr.map(function (num) {
return num + 1
})
arr.map(num => num + 1)
let fn = () => () => () => () => 1
Promise
承诺,在声明函数时承诺好,如果成功就执行resolve,如果失败就执行reject。定义resolve和reject函数的人,是使用这个Promise对象的人。
Promise有三个状态,分别时等待中,成功,失败。
注意!!!!三个状态只能成等待中变成成功,或者从等待中变成失败,如果已经成功成功或者失败,就不能再改变状态
function 异步函数 () {
return new Promise((resolve, reject) => {
setTimeout(() => {
// 模拟几率
if (Math.random() > 0.5) {
// 来了
resolve()
} else {
reject()
}
}, 3000)
})
}
异步函数().then(() => {
// resolve函数
console.log("一起走")
}).catch(() => {
console.log('自己走')
})
为什么有些函数调用之后,可以调用.then方法
axios.get().then(res => {}).catch(() => {})
因为这些函数在封装时,上来返回了一个new Proimise
。promise对象中有三个方法。
• then(() => {}) 异步操作成功时执行的函数
• catch(() => {}) 异步操作失败时执行的函数
• finally(() => {}) 不管成功还是失败都会执行的函数。
我们今天的目标是,未来看到.then或者.catch时,知道这个函数内部返回了一个promise对象。后一个.then里的参数,是前一个.then return的结果
函数().then(() => {
return 1
})
.then(num => {
// num就是1
})
Promise中所有的方法.then.catch.finally调用之后,都会返回这个promise对象。并且会按链式的顺序执行对应的then中的方法
数组方法
forEach map filter find findIndex some every reduce reduceRight
filter
筛选,调用后得到一个数组,这个数组的内部结构和原数组相同,长度大概率不同,会变少。
arr.filter((item) => {
return true 或者 false
// 如果上面返回true 则item就会被放在新数组中,如果为false,则不执行任何操作。
// 所谓的新数组,是我们对应的filter的返回值
})
some
判断某个数组中是否有符合条件的值。返回值为布尔值
arr.some(item => true或者false)
只要有一个返回值为true,则停止遍历,返回some的结果为true,如果都返回false,则some结果为false
every
判断某个数组中是否全部符合条件的值。返回值为布尔值
与some相反,只要有一个不符合条件,结果为false,如果全部都符合条件,则结果为true
arr.every(item => true 或者 false)
find
用来查找第一个符合条件的值
返回值是第一个符合条件的值,只要第一次函数中返回true,则会把正在遍历的item给作为返回值返回
arr.find(item => true 或者 false)
findIndex
用来查找第一个符合条件的值的下标
arr.find(item => true 或者 false)
reduce/reduceRight
每次遍历都会保留上一次的返回值
arr.reduce((prevValue, curValue, index, arr) => {
return 值 // 这个值是下一次函数执行的prevValue
}, 初始值) // 初始值是第一次函数执行的prevValue
flat
扁平化数组,没有flat函数我们需要自己封装递归函数进行扁平化。
arr.flat(层数)
如果我们不知道有几层的情况下,可以使用Infinity
arr.flat(Infinity)
fill
在数组对应的位置范围填充相同的值
arr.fill(值, 开始下标, 结束下标) // 后两个值可选
Array.of()
可以把参数变成数组
Array.of(1, 2, 3) // [1,2,3]
静态方法
直接设置给构造函数上的方法,叫静态方法,不需要new直接使用构造函数调用即可。数组中有三个静态方法
• Array.from
• Array.isArray
• Array.of
解构赋值
解构赋值,可以实现直接从数组或者对象中取值赋值给变量
let [变量名, 变量名2, , 变量名4] = [值, 值2, 值3, 值4]
// 相当于
let 变量名 = 数组[0]
let 变量名2 = 数组[1]
let 变量名4 = 数组[3]
let { 变量名, 变量名2 } = { 属性名: "值", 变量名2(本质是属性名): "值" } // 属性名和变量需要相同
let { a, b } = { a: 1, b: 2 }
解构赋值很容易遇到一种情况,我们要使用的名字和已有变量名冲突(重复)了
let name = "张三"
let {name, age} = {
name: "李四",
age: 18
}
// 因为name已经声明了,所以第三行的name就会报错:变量已经声明
// 我们可以使用别名解决这个问题
let {name: 别名, age} = {
name: "李四",
age: 18
}
// 别名就是变量名
参数解构
我们可以把函数的形参进行解构,在函数内部可以直接使用解构后的值,更方便使用
function fn (obj) {
console.log(obj.x, obj.y)
}
fn({x: 1, y: 2})
function fn2 ({x, y}) {
console.log(x, y)
}
fn({x: 1, y: 2})
function fn3 ([x, y]) {
}
fn3([1,2])
扩展运算符
扩展运算的作用可以简单理解为:把数组的中括号,和对象的{}直接去掉。直接去除后,里面的数据格式,我是无法在JS的环境的中独立存在。扩展运算符的操作需要被放置在合适的位置上。
let arr = [1,2,3]
let _arr = [...arr] // [1,2,3] 这是一个浅拷贝过程
let obj = {a: 1, b: 2, c: 3}
let _obj = {...obj} // {a: 1, b: 2, c: 3} 这是一个浅拷贝过程
let arr2 = [...arr, ...arr] // [1,2,3,1,2,3]
在我们使用map操作时,可以保留原有数据的基础上,添加新的属性
let arr = [{name: "张三"}, {name: "李四"}]
arr.map(stu => {
return {
...stu,
classroom: "王屋山"
}
})
rest参数
这个运算符可以用来代替arguments。当遇到不定参数时,可以使用rest。
function sum (...nums) {
nums就是一个数组,传参时我们需要传很多个单个的值,这些值会被放在一个数组中,这个数组就是nums
}
sum(1,2,3) // nums就是[1,2,3]
可以把剩余没有声明形参的参数收集变成数组
function fn (a, b, c, ...args) {}
fn(1, 2, 3, 4, 5, 6) // a 1 b 2 c 3 args是[4,5,6]
对象中的新写法
属性写法
当属性的名字和变量相同时,我们可以只写一个属性名即可
let name = "张三"
let obj = {
// name: name
name
}
方法写法
当我们要设置方法时,可以直接省略 :function
let obj = {
// fn: function () {},
fn () {
}
}
async await
异步函数,可以把函数内部的异步操作转换为同步写法。
async function 函数名 () {
let res = await 原始值或者Promise对象
// 下面的代码,会等上面的异步处理结束后才执行。
}
async getData () {
let res = await axios.get("接口地址", {params: {参数}})
// 处理res
}
因为变成了同步操作,同时代码有可能得到错误信息,如果要处理错误,可以是try catch
async getData () {
try {
let res = await axios.get("接口地址", {params: {参数}})
// 处理res
} catch (err) {
// 处理错误操作
}
}
重要!!!!async函数,返回值是一个promise对象。所以我们如果要得到函数里的自己的返回值,需要使用.then获取
async function fn () {
return 10
}
fn().then(res => {
// res 就是 10
})
// 或者如果还要用async 就需要再封装一个函数(比较麻烦)
async function init () {
let num = await fn()
console.log(num) // 10
}
init()
函数
函数参数默认值
ES6中提出了函数参数默认值的写法
function fn (形参="默认值") {
console.log(形参)
}
fn() // 默认值
如果不传参,则默认就是对应的默认值
下面的知识不属于ES6
arguments
表示函数调用时实际传入的参数的集合,是一个类数组对象。一般函数不定数量参数时可以使用arguments
arguments.callee
这个就表示函数本身,在一些匿名函数完成递归调用时使用。
模块
这个知识点很重要。
模块化发展历史单独引入js->CommonJS(nodejs)->AMD(require.js)->CMD(sea.js)->UMD->ESModule(现在主流的)
前置条件
想要在浏览器中使用模块化,需要在script标签添加type="module"
,并且需要在服务器环境下打开。
<script type="module"></script>
导出模块
导出模块有两种方法
- export
- export default
export default
默认导出(最方便的导出方式),这种导出方式只能导出一次,如果想要多次导出,就需要使用export进行导出。这种方式不需要我们考虑命名的问题。
export default 任意值
所谓的任意值,可以是任意的数据类型。
export
可以导出多个模块
// 第一种方法
export let 变量名 = 任意值
// 第二种方法
let 变量名 = 任意值
export {
// 如果变量名和模块名一致
变量名,
变量名 as 模块名 // 一般导出时不改名字
}
导入模块
导入模块使用import 有三种导入方式
import 导入默认导出的模块
这种操作最简单,不需要考虑名字,自己自定义一个即可。
import 自定义的名字 from "模块文件路径"
注意!!!如果模块中没有 export default,则会报错。
import 导入 export导出的模块
使用export导出的模块,在导入时,名字需要和导出时的名字一致
import { 模块名 } from "模块文件路径"
// 如果有多个模块需要导入,则使用 "," 隔开
import { 模块名, 模块名2, 模块名n } from "模块文件路径"
// 如果要导入的模块名和现有变量有冲突,我们可以改名字
import { 模块名 as 别名, 模块名n } from "模块文件路径"
// 如果我们不知道里面有什么模块,想要全部导出作为对象查看
import * as 自定义的名字 from "模块文件路径"
当一个模块中既有默认导出,又有普通导出,导入可以一起写
import 自定义名字 from "模块路径"
import {模块名} from "模块路径"
// 可以写在一起
import 自定义名字, {模块名} from "模块路径"