前端学习笔记 --ES6新特性

时间:2023-03-09 04:15:52
前端学习笔记 --ES6新特性

前言

这篇博客是我在b站进行学习es6课程时的笔记总结与补充。

此处贴出up主的教程视频地址:深入解读ES6系列(全18讲)

1、ES6学习之路

1.1 ES6新特性

1. 变量
2. 函数
3. 数组
4. 字符串
5. 面向对象
6. Promise
7. generator //类似于Promise
8. 模块化

1.2 变量

1.2.1 let、var、const和作用域

知识点:
1. ES6之前的JS只有函数作用域和全局作用域,ES6引入了let后新增了块级作用域,还引入const。
2. 所有window对象内置属性都拥有全局作用域。 var
1. 可以重复声明
2. 无法限制修改
3. 函数作用域(在函数体内可访问) let
1. 不能重复声明
2. 变量(可以修改)
3. 块级作用域(在大括号{}内可访问) <!--注意:var和let都存在变量提升,但是let在变量声明和初始化前的区间存在暂时性死区,无法调用--> const
1. 不能重复声明
2. 常量(不能修改)
3. 块级作用域 [块级作用域]
说明:因为var是全局变量,js是单线程运行,而for循环是同步执行,onclick()是异步执行,所以for循环执行完成后,全局变量i为3,再执行onclick()就得到了3
var btn=document.getElementsByTagName('input'); //有三个input组件
for(var i=0;i<btn.length;i++){
btn[i].onclick=function(){
alert(i); //一直弹出3,因为var没有块级作用域
};
} 解决方法:
1.用函数封装一层(用立即执行函数),传递i
var btn=document.getElementsByTagName('input'); //有三个input组件
for(var i=0;i<btn.length;i++){
(function (i){
btn[i].onclick=function(){
alert(i); //分别弹出1,2,3
};
})(i);
} 2.把var改成let声明
var btn=document.getElementsByTagName('input'); //有三个input组件
for(let i=0;i<btn.length;i++){
btn[i].onclick=function(){
alert(i); //弹出0,1,2
};h
}

1.2.2 立即执行函数(IIFE / Immediately Invoked Function Expression)

此处引用自stpice的博客

知识点:
function foo(){} //这是定义(Declaration),让解释器知道其存在,并不会立即执行
foo(); //这是语句,(Statement),解释器遇到它会执行 IIFE调用方式:
(function foo(){}());
(function foo(){})();
!function foo() {}();
+function foo() {}();
-function foo() {}();
~function foo() {}(); 在需要表达式的场景下,就不需要用括号了:
void function(){}();
var foo = function(){}();
true && function () {}();
0, function () {}(); $(function(){})的用法:
$(document).ready(function(){
console.log("ready");
});
等同于
$(function(){
console.log("ready");
});

1.3 箭头函数

1.3.1 箭头函数基本用法

知识点:
箭头函数 ()=>{} 等价于 function(){}
当只有一个参数的时候,可以省略(),如 (res)=>{} 等价 res=>{}
当只有一个返回值时,可以省略{},如 ()=>{return 0;} 等价 ()=>return 0;

1.3.2 调用位置和调用栈

此处引用自博主越努力越幸运_952c

知识点:
调用栈:调用栈主要是存放返回地址
调用位置:当前函数在代码中被调用的位置 function baz(){
//当前调用栈是baz
//当前调用位置是全局作用域
bar(); //<--bar的调用位置
} function bar(){
//当前调用栈是baz->bar
//当前调用位置在baz中
foo(); //<--foo的调用位置
} function foo(){
//当前调用栈是baz->bar->foo
//当前调用位置是在bar中
} baz(); //<--baz的调用位置

1.3.3 ES5和ES6的this指向(箭头函数)

此处引用自博主蔡香满屋

ES5:
函数的调用位置决定了this的绑定
全局环境下,this始终指向全局对象(window)
普通函数内部的this分为两种情况:严格模式和非严格模式 非严格模式:this默认指向全局对象window
function f2(){
return this;
}
f2()===window; //true 严格模式:this为undefined
function f2(){
"use strict"; //使用严格模式
return this;
}
f2()===undefined; //true ES6:
箭头函数的定义位置决定了this的绑定
所以call()/apply()/bind()方法对于箭头函数来说只是传入参数,不会影响this值 var Animal=function(){
this.name="Animal"; //下面那一行的this指向的时这外面的this
this.speak=(name,words)=>{
console.log(this.name+'is saying'+words+'.');
}
} var cat=new Animal();
cat.speak("cat","miao~"); //Animal is saying miao~

1.4 函数的参数扩展

知识点:
...可以用于展开/收集参数 [收集参数]
function show(a,b,...args){ //...args必须是最后一个形参
alert(a);
alert(b);
alert(args); //输出3,4,5,6
}
show(1,2,3,4,5,6); [展开参数]
let arr1=[1,2,3];
let arr2=[4,5,6];
let arr=[...arr1,...arr2]; //等价于arr=[1,2,3,4,5,6]

1.5 解构赋值

知识点:
1. 左右两边结构必须一样
2. 右边的格式必须是合法的
3. 声明和赋值不能分开 let [a,b,c]=[1,2,3]; //把1,2,3分别赋值给a,b,c
let [{a,b,c},[d,e,f],g,e]=[{a:1,b:2,c:3},[1,2,3],"string",1];
let [json,arr,s,i]=[{a:1,b:2,c:3},[1,2,3],"string",1]; //注意与上式的区别

1.6 数组

1.6.1 map(映射)

知识点:
map映射可以实现两个数组间的对应映射,比如下面a[],b[]两个数组间的元素一一对应:
a=[10,60,88,50,30];
b=['不及格','及格','及格','不及格','及格'] let score=[19,85,99,25,90];
let result=score.map(item=>item>=60?'及格':'不及格');
alert(score); //分别输出19,85,99,25,90
alert(result); //分别输出'不及格','及格','及格','不及格','及格'

1.6.2 reduce(汇总)

知识点:
reduce将数组[1,2,3]各个元素相加得到总数5 <!--将数组元素相加求和-->
let arr=[12,69,180,8763];
let result=arr.reduce(function(tmp,item,index){ //tmp代表两数相加产生的中间数,item表示当前第二个加数,index为索引(第一次运算得到中间数后为2)
return tmp+item;
}); <!--求数组元素相加后的平均数-->
let arr=[12,69,180,8763];
let result=arr.reduce(function(tmp,item,index){
if(index!=arr.length-1){ //不是最后一次运算
return tmp+item;
}else{
return (tmp+item)/arr.length;
}
});
alert(result);

1.6.3 filter(过滤器)

知识点:
filter将返回true或false来实现过滤 <!--过滤得到能被3整除的数-->
let arr=[12,5,8,99,27,36,75];
let result=arr.filter(item=>item%3==0);
alert(result);

1.6.4 forEach(循环迭代)

知识点:
forEach迭代输出数组的元素 let arr=[12,5,8,9];
arr.forEach((item,index)=>{ //index可加可不加
alert(index,": ",item);
});

1.6.5 map和forEach的异同点

相同点:
都是遍历数组的每一项
执行匿名函数时都支持传入三个参数:item,index,arr //item为当前项,index为索引,arr为原数组 不同点:
map会分配内存空间存储新数组并返回,forEach不会返回
forEach对数据的操作会改变原数组,map不会改变原数组,而是会返回一个新数组

1.7 字符串

1.7.1 startsWith和endsWith

知识点:
startsWith和endsWith通过判断字符串开头或结尾是否含有特定字符串来返回true和false

1.7.2 字符串模板:` 搭配 ${} 连接字符串

知识点:
可以直接把东西塞到字符串里面 $(东西)
可以折行书写代码 let title='标题';
let content='内容'; 旧的写法:
let str='<div>\ //折行需要加'\'
<h1>'+title+'</h1>\
<p>'+content+'</p>\
</div>'; 新的写法:
let str=`<div> //使用反引号包裹
<h1>$(title)</h1> //不需要加'+'连接符
<p>$(content)</p>
</div>`;

1.8 面向对象

1.8.1 老版本JS

缺点:
面向对象概念模糊,类和函数的不分家
方法需要在外面用prototype定义 <!--构造一个类和继承-->
function User(name,pass){
this.name=name;
this.pass=pass;
} User.prototype.showName=function(){
alert(this.name);
} User.prototype.showPass=function(){
alert(this.pass);
} var u1=new User('zhang','123'); u1.showName();
u1.showPass(); //---------------继承User---------------- function VipUser(name,pass,level){
User.call(this,name,pass); //call实现继承
this.level=level;
} VipUser.prototype=new User();
VipUser.prototype.constructor=VipUser; //个人猜测这里是加入User后,重新构造VipUser VipUser.prototype.showLevel=function(){
alert(this.level);
} var v1=new VipUser('zhang','123',3); v1.showName();
v1.showPass();
v1.showLevel();

1.8.2 ES6

优点:
强化面向对象概念,区分函数和类
class关键字、构造器和类分开了
class里面直接加方法 <!--构造一个类和继承-->
class User{
constructor(name,pass){ //构造器
this.name=name;
this.pass=pass;
} showName(){ //方法
alert(this.name);
} showPass(){
alert(this.pass);
}
} var u1=new User('zhang','123'); u1.showName();
u1.showPass(); //---------------继承User---------------- class VipUser extends User{
constructor(name,pass,level){
super(name,pass); //super关键字继承父类
this.level=level;
} showLevel(){ //直接在class里面加方法
alert(this.level);
}
} var v1=new VipUser('zhang','123',3); v1.showName();
v1.showPass();
v1.showLevel();

1.8.3 面向对象应用--React

React特点:
1. 强调组件化,一个组件即一个class
2. 强依赖与JSX(JSX==babel==browser.js) (这里先简单介绍一下react框架,后续将会详细学习)

1.9 JSON

1.9.1 JSON和字符串之间的互换

知识点:
1. JSON转换成字符串:JSON.stringfy()
2. 字符串转换成JSON:JSON.parse()
3. 把字符串作为URI组件进行编码:encodeURIComponent()
4. JSON的标准写法:
· 所有key和字符串类型的value都要用双引号包裹
· 最外层用单引号包裹 eg:let str='{"a":12,"b":"hello"}';

1.9.2 JSON简写

知识点:
1. 名字一样可以简写(key和value一样)
2. 方法可以简写 eg:
<!--名字简写-->
let a=12;
let b=5;
let json={a:a,b:b}; //可简写为:let json={a,b}; <!--方法简写-->
let json={
a:12,
show:function(){ //可简写为:show(){alert(this.a);}
alert(this.a);
}
};

1.10 Promise

1.10.1 Promise简介与基本用法

知识点:
Promise的出现解决了ajax异步加载时代码复杂的问题,在较新版本的ajax中有封装了Promise <!--Promise的基本写法--> let p = new Promise(function(resolve,reject){
$.ajax({
url:'hello.txt',
dataType:'json',
success(arr){
resolve(arr); //执行成功调用resolve()返回arr
},
error(err){
reject(err); //执行失败调用reject()返回err
}
})
}); p.then(function(arr){ //接受resolve()返回的参数
console.log('成功',arr);
},function(err){ //接受reject()返回的参数
console.log('失败',err);
});

1.10.2 Promise.all()

知识点:
当有多个Promise一起异步执行时,使用Promise.all()来返回多个Promise调用结果 let p1 = new Promise(functioin(resolve,reject){...}); let p2 = new Promise(functioin(resolve,reject){...}); Promise.all([p1,p2]).then(arr=>{ //此处也可简写成箭头函数
let [res1,res2]=arr; //解构赋值
console.log(res1,res2);
},err=>{
let [err1,err2]=err;
console.log(err1,err2);
});

1.10.3 Promise.race()

知识点:
与Promise.all()用法相似,原则是谁先来先返回谁,即哪个Promise返回的值快就返回谁,可以用于超时处理 Promise.race([p1,p2]).then(results=>{},err=>{});

1.11 generator

1.11.1 generator简介

知识点:
generator(生成器),执行过程中可以暂停去执行其他函数
generator跟promise相似,用于异步回调处理,异步的请求可以用类似同步的逻辑来写 普通函数异步请求:
function 函数(){
代码... ajax(xxx,function(){
代码...
});
} generator异步请求:
function *函数(){ //*不能省略
代码... yield ajax(xxx); 代码...
}

1.11.2 yield介绍

知识点:
yield把函数分成了几部分,通过调用next()来分别执行
yield可以传参和返回 传参:
function *show(){
alert('a'); let a=yield; //执行第二个next的时候把5赋值给a alert('b');
alert(a); //输出5
} let gen=show();
gen.next(12); //执行yield的上半部分
gen.next(5); //执行yield的下半部分 返回:
function *show(){
alert('a'); yield 12; //返回12给第一个next alert('b'); return 55; //返回55给第二个next
} let gen=show(); let res1=gen.next();
console.log(res1); //输出{value:12,done:false} let res2=gen.next();
console.log(res2); //输出{value:55,done:true}

1.11.3 generator实例

知识点:
在使用前需要安装runner,并引入jquery.js(用于ajax)和runner.js runner(function *(){ //使用同步的写法实现异步的请求 let data1=yield $.ajax({url:'data/1.txt',dataType:'json'});
let data2=yield $.ajax({url:'data/2.txt',dataType:'json'});
let data3=yield $.ajax({url:'data/3.txt',dataType:'json'}); console.log(data1.data2.data3);
});

1.12 回调函数的对比

知识点:
1. 在同时请求多条数据的时候,promise和generator方法效果差不多 <!--传统回调-->
//第一层
$.ajax({
url:xxx,
dataType:'json',
success(data1){
//第二层
$.ajax({
url:xxx,
dataType:'json',
success(data2){
//第三层
$.ajax({
url:xxx,
dataType:'json',
success(data3){
//OK
},error(){
alert('失败');
}
});
},error(){
alert('失败');
}
});
},error(){
alert('失败');
}
}); <!--promise-->
Promise.all([
$.ajax({url:xxx,dataType:'json'}),
$.ajax({url:xxx,dataType:'json'}),
$.ajax({url:xxx,dataType:'json'}),
]).then(res=>{
//OK
},err=>{
alert('失败');
}); <!--generator-->
runner(function *(){
let data1=yield $.ajax({url:xxx,dataType:'json'});
let data2=yield $.ajax({url:xxx,dataType:'json'});
let data3=yield $.ajax({url:xxx,dataType:'json'}); //OK
}); 2. 在请求完数据后,通过数据的值来判断下一条请求时,generator>传统回调>promise <!--传统回调-->
$.ajax({url:'getUserData',dataType:'json',success(userData){
if(userData.type=='vip'){
$.ajax({url:'getVipData',dataType:'json',success(items){
//OK
},error(err){
alert('失败');
}})
}
},error(err){
alert('失败');
}}) <!--promise-->
Promise.all([
$.ajax({url:'getUserData',dataType:'json'})
]).then(res=>{
let userData=res[0]; if(userData.type=='vip'){
Promise.all([
$.ajax({url:'getVipData',dataType:'json'})
]).then(res=>{
//OK
},err=>{
alert('失败');
});
}
},err=>{
alert('失败');
}); <!--generator-->
runner(function *(){
let userData=yield $.ajax({url:'getUserData',dataType:'json'}); if(userData.type=="vip"){
let items=yield $.ajax({url:'getVipData',dataType:'json'});
//OK
}
});