ES6 笔记
ECMAScript 6
一、块级作用域: let,const
1.1 基本用法
const: 声明常量,在声明时必须被赋值,一般大写命名
let : 声明变量,所声明的变量,只在let命令所在的代码块内有效
const的应用场景
- 函数中不变的系数,例如 y=kx+b中的k和b
- 系统中固定都参数,比如数据库的用户,密码,库名,表名,URL地址等
- 定义一个函数
- 定义一个js对象
- 声明对象:对象不能再换,但值可以修改
当需要修改时,使用let
let声明的变量只在它所在的代码块有效
例:
{
var a = 1; //使用var,将变量提升至最高0hhrg
let b = 2; //只在代码块中有效
}
输出:
a // ReferenceError: a is not defined
b // 2
var命令会发生'变量提升'现象
let和coust则不会发生
1.2 不存在变量提升
var命令会发生"变量提升"现象,值为undefined
,
为纠正这种现象,let改变了语法行为,它所声明的变量一定要在声明后使用,否则报错。
1.3 暂时性死区(temporal dead zone,简称 TDZ)
JavaScript引擎在扫描代码时发现变量声明时
- 如果是var, 则会提升至html
- 如果是let&&coust,则会将提至暂时性死区中
只有执行过变量声明语句后,变量才会从TDZ中移出后才能正常访问;
例:
var tmp = 123;
if(true){
//未声明前就被使用
tmp = 66; // 报错:ReferenceError
let tmp;
}
1.4 不允许重复声明
不允许在相同作用域中重复声明同一个变量
面试题:let和coust是什么
- 块级作用域
- 不会发生变量提升现象
- 有暂时性死区
解构赋值
1.1 数组的解构赋值
数组本身就是个对象
以前写法:
let a = 1;
let b = 2;
let c = 3;
在ES6中允许写成:
let [a,b,c] = [1,2,3];
let [a,,c] = [1,2,3]
a//1
c//3
//更为简便,方便
对于Set结构,也可以使用数组的解构赋值
let [x,y,z] = new Set(['a','b','c']);
//字符串
let [a,b,c,d,e] = 'hello';
a//h,
b//e
c//l
d//l
e//o
当解构不成功的时候,变量值变为undefined
如果等号的右边不为数组,将报错
例:
let [foo] = null;
let [foo] = 1;
let [foo] = {};
let [foo] = undefined;
1.2 对象的解构赋值
let {foo,bar} = {foo:'aa',bar:'xx'};
//如果没有对应的属性名吗,则结构失败
//变为undefined
//对象的解构赋值,可以将现存在对象的方法,赋值到变量
let {log} = console;
log('hello');
let {x=3} = {}; //ES6思维
-------------------
var {x:x} = {}; //对象结构
if(x == undefined){ //条件判断给默认值 可以改为if(!x)代替
x = 3;
}
-------------------
//传统思维
var myObj = {}; //声明一个目标对象
var x = myObj.x; //没有对象解构,传统通过访问属性赋值给一个变量
if(x == undefined){
x = 3;
}
1.3函数参数的解构赋值
const myArray = [1,2,3];
const myFun = function (item){
console.log('hi:'+item);
}
myArray.map(myFun); //myFun执行几次取决于myArray中的位数
//这个for循坏等效于上面这串代码
for(let i=0 ;i<myArray.length ;i++){
myFun();//运行几次取决于myArray的长度
myFun(myArray[i]);//遍历出数组myArray的内容
}
例:
const myArray = [{age:17},{age:18},{age:19},{age:20}]
let newArray = myArray.map( function({age})){
return age * 2 ;
})
//map() 作用:1. 2.创建一个新数组
console.log( newArray );
- 是一个数组,而且是数组中有数组(数组的嵌套). [[1,2],[3,4]]
- map等于对代码段function进行了for遍历,数组中有2个元素,代码段就会执行2次
- 代码段中的function参数,传值的内容,上述数组中的元素
- 如果传值的变量本身就是一个数组或对象,可以进行进一步的解构
- 这个代码段对象中return用来干嘛,用于构造出一个全选的数组
1.4 箭头函数
1.5 this
1.1 this的引用
根层调用this,指向最根层的window对象
const student = {
name:'十五',
sex:'很好',
play(){
console.log(this);
}
}
student.play(); //函数中this指向跟调用者一样 ,student,this不固定
let {play} = student;
play(); //根层调用它,window
实例:
const myClass = {
no:'S4155',
stuList:[
{name:'十五','age':18,play(){console.log(this)}},
{name:'十六','age':17,play() => {console.log(this)}}
//箭头函数的this,指向与它的上一级同等,window
]
}
myClass.stuList[0].play(); //调用者是谁,this就指向谁
//调用者为myClass.stuList[0],就是'十五'这条数据
let {stuList:[{play}]} = myClass ; //解构赋值
play(); //this指向window
原始数据类型 Symbol
-第七种数据类型
-表示独一无二的值
其他数据类型:
- nudefined、null
- Boolean、String
- Number、Object
- BigInt
Set与Map数据结构
Set()是一个构造函数,内部封装好的
用来生成Set()数据结构,类似数组,但其中的成员是唯一的,没有重复的值.
let a = new Set();
//通过forEach遍历
[1,2,3,4].forEach(x => a.add(x));
console.log(a);
Set函数可以接收一个数组作为参数,用来初始化
let a = new Set([1,2,3,4,4]);
[...a] //[1,2,3,4]
//通过...来展开
Set()实例操作方法:
Set.prototype.add(value);//添加某个值,返回Set结构本身
Set.prototype.delete(value);//删除某个值,返回一个布尔值
Set.prototype.has(value);//返回一个布尔值,表明该值是否为Set的成员
Set.prototype.clear();//清除全部成员,无返回值
a.add(x);
a.delete(x);
使用Array.from()方法可以将Set结构转化为数组结构
for...of循环
语法:
for(item of arr){
console.log(item);
}
例子:
const arr2 = [{name:'十五'},{name:'十六'},{name:'十七'}]
//使用for...fo循环
for( let {name} of arr2){
console.log(name); //十五,十六,十七
}
//forEach
arr2.forEach(el => console.log(el.name));//十五,十六,十七
//不使用箭头函数,本质
arr2.forEach(
//el -> 代表遍历后储存的数据
function (el){
console.log(el.name)
//十五,十六,十七
}
)
Class 的语法与继承
ES5 模仿类的写法
// 不要看成是函数
//声明一个类
function name(x,y){
this.x = x;
this.y = y;
}
//把方法写在外层
name.prototype.run = function (){
console.log(this.x + this.y);
}
let name2 = new name(15,15); //创建一个实例
console.log(name2);
name2.run(); //30
ES6的Class语法糖:Class写法
class name{
constructor(x,y){
this.x = x;
this.y = y;
}
//将方法卸载
run(){
return this.x + this.y ;
}
}
ES6的Class不需要箭头函数,也不存在
Module 模块
ES5 :
//CommonJs模块
var { stat, exists, readfile } = require('fs');
//两者等效,只是时代更迭,所以使用ES6模块写法
//es6模块
import { stat,exists, readfile } from 'fs';
import fs form 'fs';
node.js
//页面大
var e = require('element-ui');
//页面还是很大
var {Button} = require('element-ui');
node.js --> 拿代码,以及导入的代码,进行分析,重新打包,给回一个按需的,解构的
--> 压缩的全选的代码返回(页面就变小,加载速度变快)
//ES6之前模块化处理 -->
commonJS.js与AMD
//CommonJS
var math = require('math');
math.add(2,3);// 需要等math.js加载完毕
-----
//AMD
//当加载完毕后,执行一段代码
require('math',function(){
math.add(2,3);
});
//需要下载require.js和curl.js
require.js使用
创建一个html文件
将下载的require.js文件加入
再创建一个自己的js文件
//在html中引入require.js和自己的js文件
` <script src="require.js" data-main="main"></script>`
JavaScript modules 模块
最新的浏览器开始原生支持模块功能了
使用模块依赖于import和export
当创建一个模块文件时,我们可以将后缀由.js
改为.mjs
,来分辨到底是模块文件还是常规文件
//用来指示引入的模块
<script type = "module">
</script>
-----------------------------------------------
例:
//main.js文件
<script>
const a = 1;
const b = 2;
export default {a as a1}
//当导出多个时,将不再使用default
//使用 as 来进行别名
export {a as a1,b as b1}
</script>
-------------
//main.html文件
<script type = "module">
import a1 from 'main.js的路径'
console.log(a1);
//将import输入的变量是可读的,不可改变
//对其重新赋值将会报错
//但如果是一个对象,改写属性是允许的
//import会提升,首先执行,并且是静态执行,不能在里面进行运算等一系列凑在哦
</script>
ES6模块化测试必须在服务器上(tomcat)测试,如果本地浏览器会报错.(或者使用编辑器)
具体的内容可访问
JavaScript modules 模块
Promise
Promise是一个异步编程的解决方案,有三种状态:
pending(进行中),fulfilled(已成功),rejected(已失败),状态一旦发生改变就不能再次改变
Promise的不足:1.Promise的链式调用可以让代码更直观,但还是存在冗余
2.无法取消,一旦新建就会执行
3.不设置回调函数,Promise内部抛出的错误,不会反应到外部
4.当处于pending状态时,无法得知进展阶段
- 主线程
- 微任务
- 宏任务
当主线程执行完后,下发给微任务,最后加载宏任务。
一个异步加载图片的例子
<div id='aa'></div>
<script>
function loadImg( imgSrc,pass,error ){
console.log('打开洗衣机');
//创建一个图像实例
let image = new Image();
//图像的地址等于传参的imgSrc
image.src = imgSrc;
//加载事件
image.onload = () => {
pass(image);
};
//当失败时,将失败输出给参数error来完成
image.onerror = error;
console.log('按开关');
}
//调用方法 ,参数为imgSrc、pass、error
loadImg('../../img/baidu.jpg',(image) => {
//image 输出的是一个带有路径的img标签
//将img节点添加到div父节点的最后一个节点上
let img = document.getElementById('aa').appendChild(image);
console.log('洗成功了')
},() => {
console.log('失败');
});
</script>
关于执行
setTimeout( ()=>{
console.log('最后的宏任务'); //宏任务,第三步
},3000) //定时器,在微任务结束3秒后开始执行
let a = new Promise( (resolve,reject) =>{
resolve('成功了'); //主线程,第一步
//当参数不同,那么触发的内容也不同
}).then(
(resolve)=>{ console.log(resolve+'可以吃饭')}, //微任务,第二步
(reject) =>{ console.log(reject+'不可以吃饭')}//微任务,第二步
)
console.log('在吃饭吗'); //主线程,第一步
状态中转
var Prom = new Promise((resolve,reject)=>{
resolve('主线程1,任务没人接收');
})
var Prom2 = new Promise((resolve,reject)=>{
console.log('主线程2,让微任务去做主线程1的事');//1.
resolve(Prom); //委托主线程2的微任务帮忙
}).then(
resolve=>{console.log(resolve+':我来接收了')},//2
reject=>{console.log('失败了')}
)
输出://1.主线程2,让微任务去做主线程1的事
//2.主线程1,任务没人接收:我来接收了
套娃操作
new Promise((resolve,reject)=>{
resolve('成功了');//主线程1
}).then(
resolve=>{return new Promise(
(resolve2,reject2)=>{
resolve2('成功了2') //主线程2
}
)},
reject=>{}
).then(
resolve=>{console.log(resolve)},
//输出:'成功了2',执行第二个Promise
reject=>{console.log(reject)}
)
//请注意: 微任务是通过return来传值的
carth():捕获错误
Promise的语法糖:async...await
例:
async function k1(){
let text = await '123' //await 相当于 then
console.log(text); //text
}
//语法糖的根本只是为了简洁方便,最底层还是Promise
//将上述转译为下
∧
∨
//原生写法
new Promise((resolve,reject)=>{
resolve('123');
}).then(
resolve =>console.log(resolve),
reject => console.log(reject)
)
利用async...await做的延时显示
//声明一个数组
let arr = [1,2,3]
function k1(){
return new Promise((resolve)=>{
setTimeout(()=>{resolve()},1500)
})
}
//遍历数组
//使用async..await
async function k2(){
for(let arr2 of arr){
console.log(arr2);
await k1()
}
}
k2();
//实现了一个延时逐个显示的延时器
原理解析
式子1. let a = 999;
式子2. let a2 = await 999;
第一个: 返回一个Promise
原理Promise写法:
new Promise(resolve=>{
resolve(999)
})
.then(a=>{console.log(a)})
第二个:
写法:
async function k(){
let a2 = await 999; //局长与处长
//返回的是局长发出的通知,不是局长的肉体
//等待:等局长吃饱,局长发通知
//等号左边存储局长通知的内容:999
console.log(a2);
}
k()
杂类
ES6多行字符串与模板字符串
console.log(`多行
字符
串`
);
//符号: ` `
------------------------
与+号连接同理
var text1 = '小明';
var text2 = 20;
console.log(`你好,${text1},你今年${text2}岁了`);
//输出: 你好,小明,你今年20岁了