b站视频教程1:尚硅谷ECMAScript教程(ecmascript详解含es5、es6)
b站视频教程2:ES6从入门到精通系列(全23讲)
笔记只是记录开发中常用的,详细的可参考文档:
阮一峰: ECMAScript 6 入门
菜鸟教程:菜鸟教程es6
es简介
ECMAScript 6.0(以下简称 ES6)是 JavaScript 语言的下一代标准,已经在 2015 年 6 月正式发布了。它的目标,是使得 JavaScript 语言可以用来编写复杂的大型应用程序,成为企业级开发语言。
es5-严格模式
语法和行为改变:
- 必须用var声明变量
- 禁止自定义的函数中的this指向window
- 创建eval作用域(不会污染全局作用域)
- 对象不能有重名的属性
使用:
- 在全局或函数的第一条语句定义为
'user strict';
- 如果浏览器不支持,只解析为一条简单的语句,没有任何副作用
【案例】
//'use strict' //开启严格模式
//1.必须用var声明变量
//a = 1 //报错
//2.对象不能有重名的属性
// var obj = {
// name: 'tom',
// name: 'jerry'
// }
//3.禁止自定义的函数中的this指向window
// function Person(name,age){
// console.log(this) //undefined
// this.name = name
// this.age = age
// }
// Person('tom','1')
//4.创建eval作用域-关闭严格模式打印两个30
var b = 20
eval('var b = 30;console.log("eval:",b)') //30
console.log(b) //20
es5-JSON扩展
JSON.stringify(obj/arr): json对象转字符串
JSON.parse(jsonStr): json字符串转json对象 注意json的属性名要加双引号
【案例】
//1.json对象转字符串
var obj = {name:'tom',age:'18'}
var jsonStr = JSON.stringify(obj)
console.log(jsonStr)
console.log(typeof jsonStr) //string
//2.json字符串转json对象 注意json的属性名要加双引号
var jsonStr = '{"name":"tom","age":18,"gender":"男"}';
var json = JSON.parse(jsonStr)
console.log(json)
//console.log(typeof json)
es5-Object对象方法扩展
【案例】
//================1.Object.create
var obj = {name:'tom',age:18}
var obj1 = Object.create(obj,{
sex: {
value: '男',
writable: true, //是否可修改
configurable: true,//是否可删除
enumerable: true//是否能用for in枚举对象
}
})
obj1.sex = '女'
//delete obj1.sex
//console.log(obj1.sex)
//=================2.Object.defineProperties
var obj2 = {firstname: 'jerry',lastname: 'jack'}
Object.defineProperties(obj2,{
fullname: {
get: function(){ //获取扩展属性的值
console.log('get调用了');
return this.firstname+" "+this.lastname
},
set: function(data){//设置扩展属性的值
console.log('set调用了',data);
var rs = data.split(' ')
this.firstname = rs[0]
this.lastname = rs[1]
}
}
})
console.log(obj2.fullname)
obj2.fullname = 'qin yang'
console.log(obj2.fullname)
【案例】
var obj3 = {
firstname: 'lucy',
lastname: 'lu',
get fullname(){//获取扩展属性的值
console.log('get调用了');
return this.firstname+" "+this.lastname
},
set fullname(data){//设置扩展属性的值
console.log('set调用了',data);
var rs = data.split(' ')
this.firstname = rs[0]
this.lastname = rs[1]
}
}
console.log(obj3.fullname)
obj3.fullname = 'zhang san'
console.log(obj3.fullname)
es5-数组的扩展
除上面五个扩展还有两个扩展
- some: 遍历数组,如果有至少一个满足条件,就返回true,否则返回false
- every: 遍历数组,当所有的元素返回true,才返回true,否则返回false
【案例】
//返回指定元素第一次出现的下标
var arr1 = [1,2,3,2,5]
console.log(arr1.indexOf(2)) //1 从前取
console.log(arr1.lastIndexOf(2))//3 从后取
//遍历数组
arr1.forEach(function(value,index,arr){
console.log(value,index,arr);
})
//加工数组
var arr2 = arr1.map(function(v,i,arr){
return '我是:'+v
})
console.log(arr2) //['我是:1', '我是:2', '我是:3', '我是:2', '我是:5']
//过滤数组 比如取出大于2的
var arr3 = arr1.filter(function(v,i,arr){
return v > 2
})
console.log(arr3) // [3, 5]
es5 bind call apply
【案例】
function showThis(a,b){
console.log('hello:',this.name+"-"+this.age+"参数:"+a,b)
}
//直接调用 this为window
showThis() //hello: -undefined参数:undefined undefined
function Person(name,age){
this.name = name
this.age = age
}
var person= new Person('tom',18)
//通过call,把this改成了person,call和apply的区别是传参的方式不同
//showThis.call(person)//hello: tom-18参数:undefined undefined
//showThis.apply(person)//hello: tom-18参数:undefined undefined
showThis.call(person,'a','b')//hello: tom-18参数:a b
showThis.apply(person,['a','b'])//hello: tom-18参数:a b
//通过bind改变this,bind不会立即执行而是需要调用一下
showThis.bind(person)()//hello: tom-18参数:undefined undefined
showThis.bind(person,'a','b')()//hello: tom-18参数:a b
es6 let和const
let
【案例】
//1.不能重复声明
let a = 1
//let a = 1 //Identifier 'a' has already been declared
//2.不存在变量提升
// console.log(b) //UncaughtReferenceError: b is not defined
// let b = 2
//3.在块作用域有效
if(true){
//var c = 1
let c = 1
}
//console.log(c) //UncaughtReferenceError: c is not defined
const
【案例】
//不能修改
const PI = 3.14
//PI = 3.1415 //Uncaught TypeError: Assignment to constant variable.
//对象可修改属性,不可修改引用
const obj = {name:'tom',age: 16}
//obj = {} ////Uncaught TypeError: Assignment to constant variable.
obj.name = 'jerry'
console.log(obj)
es6 对象的解构赋值
【案例】
//对象的解构
let obj = {name: 'tom',age: 18}
let {name,age} = obj
console.log(name,age) //tom 18
//数组的解构
let arr = [1,2,3,true]
let [a,b] = arr
console.log(a,b) //1 2
let [,,c,d] = arr
console.log(c,d)//3 true
//解构应用
function showObj({name,age}){
console.log(name,age)
}
showObj(obj) //tom 18
es6 模板字符串
var obj = {name: 'tom',age: 18}
var str = `我叫${obj.name},今年${obj.age}岁了`
console.log(str);
es6 对象的简写
【案例】
var name = 'tom'
var age = 18
// var obj = {
// name: name,
// age: age,
// sayHello: function(){
// console.log(this.name,this.age)
// }
// }
//简写
var obj = {
name,
age,
sayHello(){
console.log(this.name,this.age)
}
}
console.log(obj)
obj.sayHello()
es6 形参默认值
ES6 允许为函数的参数设置默认值,即直接写在参数定义的后面。
【案例】
function add(a=1,b=1){
console.log(a+b)
}
add() //2
//add(1,2) //3
es6 箭头函数
【箭头函数基础案例】
let f = v=>v;
//等同于
let f = function(v){
return v;
}
// 有一个参数
let add = value => value;
// 有两个参数
let add = (value,value2) => value + value2;
//等同于
let add = (value1,value2)=>{
return value1 + value2;
}
// 无参数
let fn = () => console.log("hello world")
//等同于
let doThing = () => {
console.log("hello world")
}
//如果箭头函数直接返回一个对象,必须在对象外面加上括号,否则会报错。
let getId = id => ({id: id,name: 'mjj'}) //注意
let obj = getId(1);
【箭头函数this案例】
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<button id="btn1">btn1</button>
<script>
//箭头函数内部没有this,会一直往外找找到非箭头函数的this,如果没有则指向window
let btn1 = document.getElementById('btn1')
// let obj = {
// name: 'tom',
// age: 18,
// sayHello: function(){
// btn1.onclick = function(){
// console.log(this) //btn1
// }
// }
// }
// let obj = {
// name: 'tom',
// age: 18,
// sayHello: function(){
// btn1.onclick = () => console.log(this) //obj
// }
// }
let obj = {
name: 'tom',
age: 18,
sayHello: () => {
btn1.onclick = () => console.log(this) //window
}
}
obj.sayHello()
</script>
</body>
</html>
【补充】
箭头函数中没有arguments对象
var getVal = (a,b) => {
console.log(arguments);
return a + b;
}
console.log(getVal(1,2)); //arguments is not defined
箭头函数不能使用new关键字来实例化对象
let Person = ()=>{}
let p1 = new Person();// Person is not a constructor
es6 Symbol
不常用,了解即可
对象的Symbol.iterator属性,指向该对象默认的遍历器方法
【案例】
//1.创建Symbol,symbol表示独一无二的值,不用new关键字
let symbol1 = Symbol()
let symbol2 = Symbol()
console.log(symbol1 == symbol2);//false
//2.symbol作为对象的属性,要用[]存取
var obj = {}
obj[symbol1] = '123'
//console.log(obj.symbol1) //undefined
console.log(obj[symbol1])
//3.传参标识-这种一般用来定义常量
let symbol3 = Symbol('a')
let symbol4 = Symbol('b')
console.log(symbol3)
console.log(symbol4)
const PREFIX = Symbol('PREFIX')
console.log(PREFIX)
es6 iterator 遍历器
和Java的迭代器差不多
【案例】
/*对象的Symbol.iterator属性,指向该对象默认的遍历器方法
数组、字符串、arguments、set/map
已经自带的这个属性所以可以使用 for of方法遍历*/
var arr = ['a','b','c']
for (let s of arr) {
console.log(s);
}
var str = 'efg'
for (let s of str) {
console.log(s);
}
//使用迭代器
var it = arr[Symbol.iterator]()
//每调用一次指针就会往后移动一次,到最后的时候done会变为true
console.log(it.next());//{done: false,value: "a"}
console.log(it.next());//{done: false,value: "b"}
console.log(it.next());//{done: false,value: "c"}
console.log(it.next());//{done: true,value: "undefined"}
es6 generator
了解即可,后面的async/await可以简化这个
【案例】
//可以把异步任务放到yield后面,从而解决回调地狱问题
function* myGenerator(){
console.log('开始执行了')
let a = yield 'hello'
yield console.log(111)
let b = yield 'hi'
console.log('执行结束了')
}
//此处函数体内代码还不会执行,需要使用next方法
let mg = myGenerator()
//使用next方法执行,就是前面迭代器
console.log(mg.next())//{value: 'hello', done: false}
mg.next() //111
console.log(mg.next())//{value: 'hi', done: false}
console.log(mg.next())//{value: undefined, done: true}
【案例2-next方法传参数】
es6 promise
- 理解
1.Promise对象:代表了未来某个将要发生的事件,通常是一个异步操作。有了Promise对象,可以将异步操作以同步的流程表达出来,避免了层层调用的回调函数(俗称”回调地狱“)
ES6的Promise是一个构造函数,用来生成promise实例
promise对象的三个状态
* pending:初始化状态
* fullfilled: 成功状态
* rejected: 失败状态
2.使用promise基本步骤
第一步:创建promise对象
let promise = new Promise((resolve,reject) =>{
//初始化promise状态为pending
//执行异步操作...
if(异步操作成功){
resolve(value) //修改promise的状态为fullfilled
}else{
reject(errMsg) //修改promise的状态为rejected
}
})
第二步: 调用promise的then(成功的回调,失败的回调)
promise.then(function(){
result => console.log(result),
errorMsg => alert(errorMsg)
})
【.then的返回值仍然是promise,意味着可以链式调用then,从而解决回调地狱问题】
3.应用
* 使用promise实现超时处理
* 使用promise封装ajax请求
【promise原理代码】
console.log('111')
var flag = false
let promise = new Promise((resolve,reject) => {
//自动初始化promise状态为pending
console.log('222')
//执行异步任务
setTimeout(()=>{
if(flag){
resolve('哈哈') //成功,修改promise的状态为fullfilled
}else{
reject('哦嘛噶') //失败,修改promise的状态为rejected
}
},2000)
})
console.log('333')
promise.then((res)=>{//成功的回调
console.log(`${res} success`)
},(err)=>{//失败的回调
console.log(`${err} error`)
})
【链式调用例子】~解决回调地狱问题!!!
const p = new Promise(function(resolve,reject){
resolve(1);
}).then(function(value){ // 第一个then // 1
console.log(value);
return value * 2;
}).then(function(value){ // 第二个then // 2
console.log(value);
}).then(function(value){ // 第三个then // undefined
console.log(value);
return Promise.resolve('resolve');
}).then(function(value){ // 第四个then // resolve
console.log(value);
return Promise.reject('reject');
}).then(function(value){ // 第五个then //reject:reject
console.log('resolve:' + value);
}, function(err) {
console.log('reject:' + err);
});
es6 async
【案例】
var flag = true
function f1() {
return new Promise((resolve, reject) => {
//自动初始化promise状态为pending
console.log('222')
//执行异步任务
setTimeout(() => {
if (flag) {
resolve('哈哈') //成功,修改promise的状态为fullfilled
} else {
reject('哦嘛噶') //失败,修改promise的状态为rejected
}
}, 2000)
})
}
//async就不用写.then了
async function test() {
try{
console.log('test start');
let result = await f1() //获取resolve和reject传来的参数
console.log('test end',result);
}catch(e){
/*await可以直接获取到后面Promise成功状态传递的参数
但是却捕捉不到失败状态,所以通过try catch来处理*/
console.log(e)
}
}
test()
es6 三点运算符
【案例】
//扩展运行符-函数参数
function fun(...args){ //三点运算符作为参数,必须放到最后
// arguments.forEach(function(v,i,arr){
// console.log(v)
// })
args.forEach(function(v,i,arr){
console.log(v)
})
}
fun(1,2,3)
//扩展运算符-数组
var arr = [1,2,3]
var arr2 = [4,6,...arr]
arr2[2] = 8
console.log(arr,arr2)
//扩展运算符-对象
var obj = {name:'tom',age: 18}
var obj2 = {...obj,sex: '男'}
obj2.name = 'jerry'
console.log(obj,obj2)
es6 class类使用
【案例】
class Person{//class 定义类
constructor(name,age){ //定义构造方法
this.name = name
this.age = age
}
sayHello(){
console.log(this.name,this.age)
}
}
let person = new Person('tom',16) //创建实例
console.log(Person.prototype.constructor == Person) //true
console.log(person)
class Student extends Person{ //实现继承
constructor(name,age,sex){
super(name,age) //调用父类的构造方法
this.sex = sex
}
//重写父类方法
sayHello(){
console.log(this.name,this.age,this.sex)
}
}
let stu = new Student('jerry',16,'女')
console.log(stu)
stu.sayHello()
es6 字符串和数值的扩展
字符串扩展
【案例】
let str = 'abcdedddd';
console.log(str.includes('a'))//true
console.log(str.startsWith('a'));//true
console.log(str.endsWith('e'));//true
console.log(str.repeat(2));//abcdeddddabcdedddd
数值扩展(了解)
es6 数组方法的扩展
【案例】
//1.Array.from()
function fun(){
let arr = Array.from(arguments) //将伪数组转化成数组
console.log(arr instanceof Array) //true
}
fun(1,2,3)
//2.Array.of(...)
let arr = Array.of('a',1,2,true)
console.log(arr instanceof Array) //true
console.log(arr);//['a', 1, 2, true]
//3.find和findIdex
let nums = [1,2,3,4,5]
let rs = nums.find((v,i,arr) => v>3)//找出第一个大于3的数字
console.log(rs) //4
var rsIndex = nums.findIndex( v => v>3) //找出第一个大于3的数字的下标
console.log(rsIndex);//3
es6 对象方法的扩展
【案例】
console.log(0 == -0) //true
console.log(NaN == NaN) //false
//下面两个正好和上面两个相反
console.log(Object.is(0,-0)) //false
console.log(Object.is(NaN,NaN));//true
//***将对象的属性复制到目标对象上,并把目标对象返回
let obj = {}
let obj1 = {username: 'tom',age: 18}
let rs1 = Object.assign(obj,obj1,{sex:'男'}) //返回目标对象
console.log(rs1 == obj) //true
console.log(obj == obj1) //false
let rs2 = Object.assign({},obj1,{sex:'女'}) //返回目标对象
console.log(rs2)
//直接操作原型
let obj2 = {}
obj2.__proto__ = obj1
console.log(obj2.username)
es6 深拷贝和浅拷贝
【浅拷贝案例】
//===========浅拷贝
var obj = {name: 'tom',age:18,hobby:[1,2,3]}
var obj1 = Object.assign({},obj) //数组对象拷贝的是引用
console.log(obj == obj1)//false
obj1.name = 'jerry'
obj1.hobby[0] = 'a'
console.log(obj,obj1);
var arr = ['a',1,{name: 'tom',age:18}]
var arr1 = arr.concat()
console.log(arr == arr1); //false
arr1[2].name = 'jerry' //对象拷贝的是引用
console.log(arr,arr1);
var arr2 = ['a',1,{name: 'tom',age:18}]
var arr3 = arr2.slice(0)
console.log(arr2 == arr3) //false
arr3[2].name = 'jerry' //对象拷贝的是引用
console.log(arr2,arr3);
【深拷贝案例】
//====深拷贝
//首先知道:利用Object.prototype.toString.call(target) 来判断类型
console.log(Object.prototype.toString.call('a'));
console.log(Object.prototype.toString.call(1));
var arr = [1,2,3]
var obj = {}
console.log(Object.prototype.toString.call(arr))//[object Array]
console.log(Object.prototype.toString.call(obj))//[object Object]
//最终用下面的写法来判断类型
console.log(Object.prototype.toString.call(obj).slice(8,-1))//Object
console.log(Object.prototype.toString.call(arr).slice(8,-1))//Array
//深拷贝代码实现,重点是实现对象和数组的拷贝
//a.判断类型
function checkType(target){
return Object.prototype.toString.call(target).slice(8,-1)
}
//b.for in + 递归实现深拷贝
function clone(target){
let rs;
let type = checkType(target)
if(type === 'Object'){
rs = {}
}else if(type === 'Array'){
rs = []
}else{//如果拷贝其它类型,直接返回
return target
}
//遍历每一项
for (let i in target) {//对象:i为属性名 数组:i为下标
let val = target[i]//取出每一项
if(checkType(val) === 'Object' || checkType(val) === 'Array'){
//如果是则递归拷贝
rs[i] = clone(val)
}else{
rs[i] = val
}
}
return rs
}
var arr = ['a',1,{name: 'tom',age:18}]
var arr1 = clone(arr)
console.log(arr == arr1);
arr1[2].name = 'jerry'
console.log(arr,arr1);
//当然,最简单是JSON.parse(JSON.stringify(json对象)) 来拷贝
// var arrs = ['a',1,{name: 'tom',age:18}]
// var arrs1 = JSON.parse(JSON.stringify(arrs))
// console.log(arrs == arrs1);
// arrs1[2].name = 'jerry'
// console.log(arrs,arrs1);
es6 set和map容器
【案例】
//1.set
//let set = new Set()
let set = new Set(['a','b','c','a']) //实例化
set.add(1) //加数据
set.add(2)
set.add(1)
console.log(set) //{'a', 'b', 'c', 1, 2}
console.log(set.has('a')) //true
console.log(set.size) //5
set.delete(1) //删除数据
console.log(set)
set.clear() //清空set
console.log(set)
//2.map
//let map = new Map()
//注意:如要初始化数据,需要使用二维数组初始化
let map = new Map([['hobby','篮球'],['school','清华大学']])
map.set('username','tom')
map.set('password','123')
map.set('age',10)
console.log(map)
console.log(map.get('hobby'))//篮球
console.log(map.has('age'));//true
console.log(map.size);//5
console.log(map.delete('age'))//true 删除数据
console.log(map)
map.clear() //清空set
console.log(map);
es6 for of
【案例】
//遍历数组
var arr = ['a','b','c']
for (let s of arr) {
console.log(s);
}
//遍历set
let set = new Set(['s1','s2','s3'])
//console.log(set)
for (let s of set) {
console.log(s)
}
//遍历字符串
let str = 'es6'
for(s of str){
console.log(s);
}
//遍历伪数组
function fun(){
for (let s of arguments) {
console.log(s);
}
}
fun('w','s','z')
es7 幂运算和数组包含
指数运算符-幂运算 **
Array.prototype.includes(value) 判断数组中是否包含指定的value
【案例】
//指数运算
let rs = 3 ** 3
console.log(rs) //27
//数组包含指定value
var arr = ['abc','c','d',true]
console.log(arr.includes('a')) //false
console.log(arr.includes('abc')) //true
模块化
历史上,JavaScript 一直没有模块(module)体系,无法将一个大程序拆分成互相依赖的小文件,再用简单的方法拼装起来。其他语言都有这项功能,比如 Ruby 的require
、Python 的import
,甚至就连 CSS 都有@import
,但是 JavaScript 任何这方面的支持都没有,这对开发大型的、复杂的项目形成了巨大障碍。
在 ES6 之前,社区制定了一些模块加载方案,最主要的有 CommonJS 和 AMD 两种。前者用于服务器,后者用于浏览器。ES6 在语言标准的层面上,实现了模块功能,而且实现得相当简单,完全可以取代 CommonJS 和 AMD 规范,成为浏览器和服务器通用的模块解决方案。
ES6 模块的设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonJS 和 AMD 模块,都只能在运行时确定这些东西。比如,CommonJS 模块就是对象,输入时必须查找对象属性。
export和import
模块功能主要由两个命令构成:export
和import
。export
命令用于规定模块的对外接口,import
命令用于输入其他模块提供的功能。
一个模块就是一个独立的文件。该文件内部的所有变量,外部无法获取。如果你希望外部能够读取模块内部的某个变量,就必须使用export
关键字输出该变量
- 新建一个index.js模块,内容如下
//导出属性
// export const name = 'tom'
// //导出方法
// export function sayHello(){
// console.log('hello')
// }
//也可以按下面这样
const name = 'tom'
function sayHello(){
console.log('hello')
}
export {name,sayHello} //导出
- html页面导入模块
<!-- 别忘了加上 type="module" 否则会报错 -->
<script type="module">
import {name,sayHello} from './module/index.js'
console.log(name) //tom
sayHello() //hello
</script>
export default和import
使用export default
命令为模块指定默认输出
- 新建一个export-default.js模块,内容如下
export default function foo(){
console.log('foo...')
}
//或者按下面这样写
// function foo(){
// console.log('foo...')
// }
// export default foo
- html页面导入模块
<script type="module">
import customFoo from './module/export-default.js'
customFoo() //foo...
</script>