一、let :
- 用来声明变量类似于var
- let变量必须先声明后使用的·(没有变量提升,没有预解析)
- let不允许重复声明变量
- let声明的变量拥有块级作用域·—var是全局和函数作用域
- “暂时性死区”:在代码块内,使用let声明变量前,该变量都是不可用的,声明变量之前可理解暂时性死区。
二、Const:
- const :声明常量的特点和语法与let相同的。能改变的是变量,常量不能够改变
- 常量不允许修改值。
- 常量必须赋初始值。
- const与let相同同样有块作用域(没有预解析,变量提升)
- const定义的引用类型的值内容可以修改
三、解构赋值:
数组解构:
- 解构:左边一种结构,右边一种结构,左右一一对应
- 完全解构
{
//1.完全解构
let a,b;
[a,b]=[1,2];
console.log(a,b);
}
不完全解构(部分解构)
{
//2.不完全解构(部分解构)
1et[a]=[1,2,3]
console.log(a);
1et[b,c]=[1];
console.log(b,c);//如果解构不成功,变量值就默认等于undefined
}
忽略方式解构
{
//3.忽略方式解构
let[,,a]=[1,2,3];
console.log(a);
}
嵌套解构
{
//4.嵌套解构
let[a,b,[c]=["a","b",["c"]];
console.log(a,b,c);
}
剩余运算符方式解构
{
//5.剩余运算符方式解构
let [a,...b]=[1,2,3,4,5,6,7];
console.log(a,b);
//a只取第一个,剩下的都赋值给b
}
解构默认值
//6.解构默认值
let [a,b=100]=[96];
console.log(a,b);
//如果设置了默认值,当没有第二个参数值时就取默认值100
使用场景
场景1:使用解构方式实现两数交换?
{
//场景1:使用解构方式实现两数交换?
//方式一传统
1et a=10;
1et b=20;
let temp;
temp a;
a =b;
b temp;
console.log(a,b);
}
//方式二:算法
let a=10;
let b=20;
a=a+b;//a:30
b=a-b;//b:10(30-20)
a=a-b;//a:20(30-10)
console.log(a,b);
//方式三:
let a=10;
let b=20;
[a,b]=[b,a];
console.log(a,b);
场景2:使用解构方式实现两数交换?
function fn(){
return [10,12,13];
}
//传统
let a1 = fn();
console.log(a1[0],a1[1],a1[2]);
//数组解构
[a,b,c] = fn();
console.log(a,b,c);
对象解构:
☐ 数组解构和对象解构的不同:
☐ 数组解构是按照顺序一一解构,
☐ 对象解构与顺序无关,根据属性名和对象名进行比较。
完全解构
//1.完全解构
let obj = {name:zhangsan',age:20};
let {age,name}=obj;
console.log(name,age);
部分解构
//12.部分解构
let obj {name:zhangsan',age:20};
let {age} = obj;
console.log(age);
let an = {name:'晓得',age:45};
let {zhu,name} = an;//zhu无定义则会为undefined
console.log(name,zhu);//晓得 undefined
解构之后重命名
let an = {name:'晓得',age:45};
let {age:zhuzhu,name:names} = an;//和顺序无关,
//且重命名规则为:匹配名称:变量名
console.log(zhuzhu,names);//45 '晓得' :结果
剩余运算符对象解构 -浅拷贝 …
let an = {name:'晓得',age:45};
let {...ans} = an;
console.log(ans);//{name: '晓得', age: 45}:结果
let anzz = {name:'晓是得',age:415,img:"/sdsjd/ik"};
let {name,...ff} = anzz;
console.log(name,ff);//晓是得 {age: 415, img: '/sdsjd/ik'}:结果
对象默认值
//如果默认xiaozi是没有定义的:
let an = {name:'晓12得',age:425};
let {age:zhuzhu,name:names,xiaozi =4} = an;//和顺序无关,且重命名规则为:匹配名称:变量名
console.log(zhuzhu,names,xiaozi);//425 '晓12得' 4 :结果
//如果默认xiaozi是定义了的:
let an12 = {name:'晓12得',age:425,xiaozi1:'猪'};
let {age:zhuzhu1,name:names1,xiaozi1 =4} = an12;//和顺序无关,且重命名规则为:匹配名称:变量名
console.log(zhuzhu1,names1,xiaozi1);//425 '晓12得' '猪' :结果
使用场景
函数返回值
function aa(){
return {
name:'小德',
age:12,
fan:'你嘛'
}
}
//传统:
let aas = aa();
console.log(aas.age,aas.name,aas.fan); //结果:12 '小德' '你嘛'
//对象解构
let {name:a,fan:b,age:c} = aa();
// console.log(name,fan,age);//结果: 小德 你嘛 12
console.log(a,b,c,1212);
//结果: 小德 你嘛 12 1212
字符串解构:
let str ="qwert";
//数组用法
let [a,b,c,d] = str;
console.log(a,b,c,d); // 结果: q w e r
//对象用法
let {length}
= str;//String字符串里面的属性;
console.log(length);//结果 5
四、ES6模板字符串:
语法:使用反括号``
- 可以当成普通字符串使用
- 可以定义多行字符串
- 可以直接嵌套变量——语法:${变量}
let ac = {name:'猪猪猪',id:1,age:45};
console.log(`我叫${ac.name},
今年${ac.age}岁,
编号为:${ac.id}`)
//自定义换行,自由插入变量,下方是结果:
// 我叫猪猪猪,
// 今年45岁,
// 编号为:1
五、ES6字符串新增的方法:
includes()
判断是否包含返回true/false
let str =`hello world`;
console.log(str.includes('h'));//true
console.log(str.includes('yy')); //false
startswith()
判断该字符串开头是否在某字符串头部出现 true/false
console.log(str.startsWith('he'));//true
endswith()
判断该字符串开头是否在某字符串尾部部出现 true/false
console.log(str.endsWith('rld'));//true
repeat()
将字符串复制N次 返回重复后的新字符串
let str =`大傻 H大大 `;
console.log(str.repeat(3));//大傻 H大大 大傻 H大大 大傻 H大大
六、对象新增的方法:
Object.assign():
- //Object.assign() 用于对象合并
- //注意点:如果不希望更改源对象,第一个参数可以给一个空{}
- //合并时如果存在相同属性,后面对象的会覆盖前面的对象属性值;
- 但是前面不加{}()Object.assign(a,b); 会把B合并到A里;
let a = {id:1,name:"小德",address:"云南昆明",phone:"13008672507"};
let b = {age:35,phone:"13008672016"};
let ad = Object.assign({},a,b);//如果有相同属性,会以后面的为主
console.log(ad);//{id: 1, name: '小德', address: '云南昆明', phone: '13008672016', age: 35}
console.log(a,b);
//此时,a和b对象都还是没变,因为加了{},不加就是a:{id: 1, name: '小德', address: '云南昆明', phone: '13008672016', age: 35},b为: {age:35,phone:"13008672016"};了
Object.is():
Object.is() 比较两个值是否严格相等 值以及类型 类似===
console.log(1==1);//true
console.log("1"==1);//true
console.log("1"===1);//false
console.log(Object.is(1,1));//true
console.log(Object.is("1",1));//false
//注意
console.log(Object.is({},{}));//false 因为比较的是内存地址
七、ES6对象扩展:
属性和方法的简洁表达方式
属性
- 如果对象属性名和接收的变量名相同可以直接使用变量名作为属性名
// 假设我们有一个变量 router
let router = "myRouterValue";
// 我们想要创建一个对象,这个对象有一个属性叫做 router
// 在 ES6 中,如果属性名(router)和变量名(router)相同,
// 我们可以简写成 { router } 而不是 { router: router }
let vue = {
router // 这里是简写,等同于 router: router
};
// 打印 vue 对象看看
console.log(vue); // 输出将是 { router: "myRouterValue" }
方法方法名()
data(){
console.log("我是data-fn");
},
//等同于下方
data:function(){
console.log("我是data-fn");
}
console.log(vue);//打印结构
vue.data();//调用对象属性的方法
表达式方式的属性名和方法名
//[表达式] 用于动态生成属性名称,或者遍历
let aa = {
['aas'+'猪']:12,
age:4,
['funnc'+'ss'](){
console.log(666)
}
}
console.log(aa);//{aas猪: 12, age: 4}
aa. funncss()//{aas猪: 12, age: 4, funncss: ƒ}
八、数组的扩展
扩展运算符 … 浅拷贝
let [a,...b]=[1,2,3,4,5,6,7];
console.log(a,b);
let a1 = [1,3,5];
let a2 = [...a1];
console.log(a2);//(3) 结果:[1, 3, 5];
let a3 = [4,9];
let a4 = [...a1,...a3];
console.log(a4);//(3) [1, 3, 5];//结果:(5) [1, 3, 5, 4, 9]
用于数组解构和合并
find(callbak):查找,找出第一个复合条件的数组成员
let a1 = [1,3,5];
let q1 = a1.find(item=>{
return item>2;
});
console.log(q1);
结果:3
findIndex(callbak):查找,查找出第一个复合条件的数组成员的索引
let a1 = [1,3,5];
let q1 = a1.find(item=>{
return item>2;
});
console.log(q1);
结果:1
Array.of():将一组值转换为数组;
console.log(Array.of(1,2,2,2));
//结果(4) [1, 2, 2, 2]
Array.from(obj[,fn]):将对象转换成真正的数组;
let zhuzhu = {
0:1,
1:"小德",
length:2
}
let z = Array.from(zhuzhu);
console.log(z);
//结果:
//转换成数组了:(2) [1, '小德']
重点:这里转换有条件
转换需要是可遍历的对象
需要有索引以及length属性,如上
九、函数扩展:
this:
- 基础this
构造函数是默认指向new的对象
call和aoopply和bind区别:
- 他们三个都用于修改 this的指向
- 用法
-
区别:(会立马执行的意思是’zhuzhu.fn.call({name:‘1111’})‘代码写了以后就直接会执行一次这个方法)
- obj.call(要指向的对象,参数1,参数2…)——会立马执行
2. **obj.apply**(要指向的对象,[参数1,参数2...])——**会立马执行**
3. **obj.bind**(要指向的对象)——**返回对应的函数,不会立马执行**,想要的时候当作对象调用;
apply和call的区别在于传参不一样:
apply是一个数组里面多个对象;
call是直接逗号分隔的多个对象;
函数默认值:
//传统默认值设置
function aa(time){
time = time || 1000;//设置默认值
console.log(time)
}
//ES6中:
function zz(time = 1200){
console.log(time)
}
//调用
aa();
zz();
重点:设置了ES6默认值的,就不要在参数里面写多个参数了,建议只允许一个参数,不然就得搭配undefined来用;建议放在最后一个用
箭头函数:
初体验 ()=>{}:当方法体中只有一行代码,可以省略()
//传统的函数写法
function aa(time){
console.log(time)
}
//ES6中: ()=>{}
let zz = (time = 12) =>{
console.log(time)
}
//调用
aa(80);
zz();
适用场景-回调函数
setTimeout(),
//计时器: 会立即执行 其他函数需要调用
let jish = setTimeout(()=>{
console.log('立即执行');
alert(111);
},2000);
forEach():
//遍历数组
let ac = ['nihao',1,"小德"];
ac.forEach((item)=>{
console.log(item);
});
有参函数和无参函数定义
有参数:
let fn2 = (a,b)=> a+b;
fn2(10,10);//20
无参:
如果函数体只有一行 =>后可以省略{},多行则不可以省略;
如果函数体只有一行, return可以省略,多行则return不可以省略;
let fn = () => 99;
如果返回值是一个对象,则必须使用()
代码块多行代码
多行加{}
如果遇到返回一个对象则=>要加一个();
let fn = ()=>{name:"小德",sj:'1'}; //想要返回对象,这种会报错
console.log(fn());
let fn1 = ()=>({name:"小德",id:1}); //想要返回对象,需要加一个括号
console.log(fn1());//{name: '小德', id: 1}
箭头函数注意点:
函数体的this是指的定义时的对象;
箭头函数不可以用作构造函数;
箭头函数不可以使用arguments对象;
箭头函数不可以使用yield命令,因此箭头函数不能用作Generator函数;
十、数值扩展:
'use strict' //严格模式下不允许使用八进制文字(0开头的就是8进制或者二进制)
let aa = 15
+197
+1;
console.log(aa);
let a1 = 100;//十进制
let a2 = '0x100';//十六进制
let a3 = 0b100;//二进制
let a4 = 0o100;//八进制
console.log(a1,a2,a3,a4);
console.log(Number(a2));
console.log(Number(0b100));//4
Number(0b100);是数值转换为十进制;
ES6中的数据类型:
- Undefined
- Null
- Boolean
- Number
- String
- Symbol(新加)
- Object
其中,Symbol 是 ES6 新加入的数据类型。下面是对新加的 Symbol 以及它的用途的说明:
Symbol
Symbol 是一个特殊的、不可变的数据类型,可以用作对象的属性名。这意味着每个 Symbol 都是唯一的,不会与其他任何值冲突。
举个简单的例子:
想象你正在为一个小游戏写代码,其中有很多玩家(player)对象,每个玩家对象都有一个名字(name)和分数(score)。如果你不小心用了一个已经用过的属性名,可能就会出问题。这时 Symbol 就能派上用场。
用代码举例子:
// 普通的属性名
const player1 = { name: 'Alex', score: 100 };
// 假设后来你想添加一个“特殊的名字”,但已经用 'name' 了
// 使用 Symbol,就能避免属性名冲突
const specialName = Symbol('name');
player1[specialName] = 'Super Alex';
在这个例子中,specialName 是一个 Symbol,它保证了即使我们已经用 ‘name’ 作为一个普通的属性名,我们依然可以用 specialName 作为一个完全独立、不会和 ‘name’ 冲突的属性名。
Symbol的常见用途:
- 创建唯一的属性名,避免冲突:
for循环默认取不了Symbol的值需要:Object.
当你的代码变得很大,或者你引入了别人的代码(比如一个库或框架),可能会不小心用了相同的属性名。使用
Symbol 作为属性名可以完全避免这种冲突。
2. “隐藏”对象的属性:
当你使用 Symbol 作为属性名时,这个属性不会出现在常规的JavaScript操作中,比如使用
for…in 循环或
Object.keys() 查看对象的属性。这样,
Symbol 创建的属性就像是“隐藏”的,不容易被意外地改动或查看。
Object.getOwnPropertySymbols(zhuzhu);
3. 使用系统定义的特殊 Symbol:
JavaScript 本身定义了一些特殊的 Symbol,它们用来修改某些默认的行为。比如,Symbol.iterator 定义了一个对象如何成为一个可迭代的对象(可以用 for…of 循环)。
- Symbol 的引入主要是为了解决在复杂应用中可能出现的属性名冲突问题,以及为了定义一些与语言本身行为相关联的特性,这些特性通过预定义的 Symbol 值来实现。
- 其他的数据类型(Undefined, Null, Boolean, Number, String, Object)都是从之前的 ECMAScript 标准版本中继承过来的,并没有新的改动。
实际操作:
新数据类型Symbol 一般用于对象中,表示不知道对象中的任何属性时,用symbol可以创建一个独一无二的属性,即使属性名相同;
//语法:
let aa = Symbol('这里是属性的描述');
console.log(aa);
//语法:
let aa = Symbol('这里是属性的描述');
console.log(aa);
let bb = Symbol('这里是属性的描述');
console.log(aa==bb);//即使这里描述一样,但是因为本身代表这个属性独一无二,所以:false
实际在对象里面的操作
//1.已经有一个完整的对象,但是内容不知道,避免先后设置属性相同;
let zhuzhu ={
id:1,
name:"小德",
age:23
}
console.log(zhuzhu);//{id: 1, name: '小德', age: 23}
//在不知道内部结构的时候,我要加一个我要的name属性,但是又不能改掉他的
zhuzhu.name = "晓得的";
console.log(zhuzhu);//改变了{id: 1, name: '晓得的', age: 23}
let name = Symbol("name");
let id = Symbol("id");
zhuzhu[name] = "你好";//Symbol 的用法相当于数组,用【】
zhuzhu[id] = "0101";
console.log("结果:",zhuzhu);//结果: {id: 1, name: '晓得的', age: 23, Symbol(name): '你好'},
console.log("Symbol中的name是:",zhuzhu[name],zhuzhu.name);//Symbol中的name是: 你好 晓得的
//当对象有多个Symbol时,遍历出来Symbol类型的属性 getOwnPropertySymbols
let ac = Object.getOwnPropertySymbols(zhuzhu);
for(let i = 0;i<ac.length;i++){
console.log(ac[i]);
console.log(zhuzhu[ac[i]]);//访问对象里面的Symbol的话,还是需要源对象来接受
}
// Symbol(name)
// 你好
// Symbol(id)
// 0101
十一、Arguments
在 ES6(ECMAScript 2015)中,
Arguments 是函数内部可以使用的特殊对象,它包含了函数被调用时传递的所有参数。
虽然 Arguments 在函数中是可用的,但它不是一个真正的数组,而是一个类似数组的对象。这意味着它具有类似数组的性质,例如可以通过索引访问参数,但它没有数组的所有方法。
以下是 Arguments 对象的一些重要特性:
包含所有参数
无论您在函数定义中是否声明了参数,Arguments 对象都会包含函数被调用时传递的所有参数,按照它们的顺序排列。
类数组对象
Arguments 对象可以通过索引访问参数,就像数组一样。例如,arguments[0] 表示第一个传入的参数,arguments[1] 表示第二个传入的参数,依此类推。
没有数组方法
尽管 Arguments 对象类似于数组,但它没有数组的方法,如 forEach、map、filter 等。如果想在 Arguments 对象上使用这些方法,需要将其转换为真正的数组。
不受参数命名影响:
Arguments 对象的索引是从 0 开始的,不受参数在函数定义中的命名影响。即使在函数定义中没有指定参数名,也可以通过索引访问传入的参数。
function sum() {
let total = 0;
for (let i = 0; i < arguments.length; i++) {
total += arguments[i];
}
return total;
}
console.log(sum(2, 3, 5)); // 输出: 10
在这个示例中,
sum 函数没有显式定义参数,但它可以通过
arguments 对象获取传入的所有参数,并将它们相加返回总和。请注意,尽管在现代 JavaScript 中,更常见的做法是使用剩余参数(rest parameters)或展开操作符(spread operator)来处理可变数量的参数。
十二、Class类:
回顾:
ES5的语法:** **
// ES5 定义对象
// 构造函数
function Person(id, name) {
this.id = id;
this.name = name;
}
// 在 Person 的原型上定义 CEshi 方法
Person.prototype.CEshi = function() {
console.log(`我的编号是:${this.id},我的名字叫:${this.name}`);
};
// 创建一个 Person 实例
let screen = new Person(1, "小德");
// 打印实例
console.log(screen);
// 调用 CEshi 方法
screen.CEshi();
//ES6 语法 : class、constructor
//ES6 语法
class User{
constructor(age,address){
this.age = age;
this.address = address;
}
showInfo(){
console.log(`我家住在:${this.address},今年${this.age}岁了`);
}
}
let user = new User(18,"北京朝阳区");
console.log(user);//User {age: 18, address: '北京朝阳区'}
继承:super();必须放第一行
//继承
class User{
constructor(age,address){
this.age = age;
this.address = address;
}
showInfo(){
console.log(`我家住在:${this.address},今年${this.age}岁了`);
}
}
class son extends User{
constructor(id,name,sonage,age,address){
super(age,address);//必须放第一行
this.id = id;
this.name = name;
this.sonage = sonage;
}
}
let ii = new son(1,"嘻嘻",23,4,"北京");
console.log(ii.showInfo());//我家住在:北京,今年4岁了
get和set属性:
静态属性和静态方法:通过类名直接.的(Static),不需要new对象
十三、Set:是ES6中新增的数据类型,键值都相同;
Set是一种无序的、不重复的集合,它不保留元素的插入顺序,并且每个元素只能在Set中出现一次。
使用 For…of是具体值,使用for…in是索引下标
添加数据 add()
//Set:新的数据结构,存储的成员都是唯一的,类似键值对,但是Set的键值对键值都是一样的
// 类似数组
// 作用:存储多个数据的容器
let aa = new Set();
//添加数据 add
aa.add(1);
aa.add({name:'小德'});
aa.add([1,2,3,4]);
aa.add(1);//这里是不报错的,但是无效,因为值是唯一的
console.log(aa);
//[[Entries]]
// 0: 1
// 1: value: {name: '小德'}
// 2: Array(4) value: (4) [1, 2, 3, 4]
// size: 3
数组去重(Set)去重方式之一
//初始数组
let aa = [1,2,"写的","zhu","猪","写的",2];
console.log(aa,"个数:",aa.length);//(7) [1, 2, '写的', 'zhu', '猪', '写的', 2]'个数:' 7
//Set去重
let ze = new Set(aa);
console.log(ze,"个数:",ze.size);//Set(5) {1, 2, '写的', 'zhu', '猪'}'个数:' 5
注意:Set是Arrays类型但是Set是以size属性来计数的,数组是length
初始化:new Set() /new Set(array);
Set大小:Size属性;
判断是否存在 has() :ture / false
let aa = [1,2,"写的","zhu","猪","写的",2];
let ze = new Set(aa);
console.log(ze,"个数:",ze.size);
console.log("是否有猪或者1",ze.has("猪"),ze.has(1));
//结果:
// Set(5) {1, 2, '写的', 'zhu', '猪'} '个数:' 5
// 是否有猪或者1 true true
删除 delete
//删除 delete
// Set(5) {1, 2, '写的', 'zhu', '猪'} '个数:' 5
if(ze.has(1)){
ze.delete(1);
}
console.log("删除结果为:",ze);
//删除结果为: Set(4) {2, '写的', 'zhu', '猪'}
清空Set集合 clear()
//清空Set集合 clear()
// Set(4) {2, '写的', 'zhu', '猪'}
ze.clear();
console.log("清空结果为:",ze);
//清空结果为: Set(0) {size: 0}
遍历 for(let asc of a.values()){}
for(let [key,value] of a.entries()){}
//遍历
let aa = [1,2,"写的","zhu","猪","写的",2];
let a = new Set(aa);
//传统的遍历方法(键值对)
a.forEach(((keys,values,itemlist) =>{
console.log(keys,values,itemlist);
}))
//结果:
// 1 1
// 1 1 Set(5) {1, 2, '写的', 'zhu', '猪'}
// 2 2 Set(5) {1, 2, '写的', 'zhu', '猪'}
// 写的 写的 Set(5) {1, 2, '写的', 'zhu', '猪'}
// zhu zhu Set(5) {1, 2, '写的', 'zhu', '猪'}
// 猪 猪 Set(5) {1, 2, '写的', 'zhu', '猪'}
//ES6中遍历 因为Set键值都相等
console.log(a.keys());//键
//遍历
for(let ac of a.keys()){
console.log(ac);
}
console.log(a.values());//值
for(let asc of a.values()){
console.log(asc);
}
console.log(a.entries())//得到所有键值对
// 结果:
// SetIterator {1 => 1, 2 => 2, '写的' => '写的', 'zhu' => 'zhu', '猪' => '猪'}
for(let [key,value] of a.entries()){
console.log("entries:",key,value);
}
十四、Map:数据类型:键和值可以不一样;
Map 数据结构 键值对结构
初始化
let a = new Map();//Map(0) //{size: 0}
获取容器大小 size属性
console.log(a,a.size);//{size: 0} 0
添加成员 set(key,value)
a.set("name","小德");
a.set("age",23);
a.set("list",{
id:"1",
name:"小德而"
})
console.log(a,a.size);
//结果:
// Map(3) {'name' => '小德', 'age' => 23, 'list' => {…}} 3
获取成员通过 get(key) 返回value:
let list = a.get("list");
console.log(list);
// 结果:
// {id: '1', name: '小德而'},
console.log(a.get("name"));//小德
删除成员 delete(key): 根据key值来删除k,v
//Map(3) {'name' => '小德', 'age' => 23, 'list' => {…}} 3
a.delete("name");
console.log(a);//Map(2) {'age' => 23, 'list' => {…}}
清除Map clear()
a.clear();
console.log(a);//Map(0) {size: 0}
遍历Map元素
for(let [keys,values] of a.entries()){
console.log("key:",keys,"value:",values)
}
// 结果:
// key: age value: 23
// index.html:46 key: list value: {id: '1', name: '小德而'}
for(let keys of a.keys()){
console.log("key:",keys) //key: age
// key: list
}
for(let values of a.values()){
console.log("value:",values)// value: 23
// value: {id: '1', name: '小德而'}
}
//传统的遍历:
a.forEach((value,key)=>{
console.log(key,value)
});
//结果:
// age 23
// list {id: '1', name: '小德而'}
十五、Generator
- Generator函数是ES6提供的一种异步编程解决方案;
- Generator函数是一个状态机,封装了多个内部状态。执行Generator函数会返回一个遍历器对象;
- Generator也是一个遍历器对象生成函数;
- 返回的遍历器对象,可以依次遍历Generator函数内部的每一个状态;
基本语法
Generator 本身是一个函数,返回一个iterator迭代器
*基础语法 * —— function (){}
yield 暂停标志
let generator = function*() {
console.log("A请求");
yield; // 暂停/产出
console.log("B请求");
yield;
console.log("C请求");
// 这里因为函数体是一个整体,不加yield的话会被当成整体顺序执行
// 假如yield后可实现函数体的暂停
}
let aa = generator(); // 创建一个迭代器iterator
console.log(aa.next()); // { value: undefined, done: false },并打印"A请求"
console.log(aa.next()); // { value: undefined, done: false },并打印"B请求"
console.log(aa.next()); // { value: undefined, done: true },并打印"C请求"
yield的语法 返回值 跟在yield后面的可以是要传递出去的参数;
let zhuzhu = {
id:1,
name:"小夏"
}
//定义
let generator =function *(){
console.log(1);
yield zhuzhu;
console.log(2);
yield "小德";
console.log(3);
yield 1;
}
//声明
let aa = generator(); //iterator迭代器
//开始迭代
console.log(aa.next());//{value: {…}, done: false}
console.log(aa.next());//{value: '小德', done: false}
console.log(aa.next());//{value: 1, done: false}
console.log(aa.next());//{value: undefined, done: true}
// for(let a of generator()){
// console.log(a);
// }
yield 传参
第一个Yield传参是无用的,要从第二个开始
yield 直行到yield的地方会自动停止,需要下一次的迭代xx.next(参数);
let zhuzhu = {
id:1,
name:"小夏"
}
let generator = function*() {
let ac = yield; // 这里接收的是第二次调用next时传入的参数
console.log(1, ac);
let a = yield; // 这里接收的是第三次调用next时传入的参数
console.log(2, a);
let b = yield; // 这里接收的是第四次调用next时传入的参数
console.log(3, b);
}
let aa = generator(); // 创建一个迭代器iterator
console.log(aa.next("zhuzhu1")); // {value: undefined, done: false},"zhuzhu1"被忽略
console.log(aa.next("zhuzhu2")); // {value: undefined, done: false},并打印 "1 'zhuzhu2'"
console.log(aa.next("zhuzhu3")); // {value: undefined, done: false},并打印 "2 'zhuzhu3'"
console.log(aa.next("zhuzhu4")); // {value: undefined, done: true},并打印 "3 'zhuzhu4'"
实际操作
因对象默认没有实现Symbol.Iterator函数,不可以使用for…of,而Generator正好是返回Iterator对象。所以用Generator来实现Symbol.Iterator;
如果你想遍历对象的属性,并且在迭代过程中输出属性名和属性值,你可以在
for…in 循环中使用类似的方式:
for (let key in objs) {
if (objs.hasOwnProperty(key)) {
console.log(`${key}: ${objs[key]}`);
}
}
let objs = {
name: "小德",
id: 1,
age: 23,
address: "云南昆明" // 注意我这里修正了一个小的拼写错误:将 "addres" 改为 "address"
};
// 实现Symbol.iterator方法
objs[Symbol.iterator] = function*() {
for (let key in objs) {
if (objs.hasOwnProperty(key)) {
// 使用反引号和正确的模板字符串语法
yield `${key}: ${objs[key]}`;
}
}
};
console.log(objs);
for (let prop of objs) {
console.log(prop);
}
为了解决回调地狱,实现先A在B在C的异步请求;用Generator
//封装请求
let ag =function (url,data){
return new Promise((resolve,reject)=>{
//假设请求成功
setTimeout(resolve,2000,{
url:url,data:data
})
})
};
//使用Generator函数来实现异步请求 实现业务逻辑请求顺序
let generator = function *(){
let res1 = yield ag("请求A",1012);
console.log(777,res1);
//这里可以把A请求拿到的A请求的结果,用于B请求,来请求列表数据
let res2 = yield ag("请求B",res1.data);
yield ag("请求C",res2.data);
}
let it = generator();//返回Iteratir迭代器
//递归执行迭代器
function digui(it,result=null){
let ittor = it.next(result);//这里是把result接受到上一个请求的结果传递给当前yield并赋值,
//为下一个请求的参数做准备
if(!ittor.done){
ittor.value.then(res=>{
console.log(res);
digui(it,res);//传递当前请求的结果给下一个请求当做参数
},err=>{
console.log(err);
})
}
}
digui(it);//启动递归,执行业务逻辑请求
十六、Iterator: 1ter rui ter(读) 迭代器
对象默认没有Iterrator机制,只要有Symbol.iterator接口都有一个next()指针对象
都可以使用For … of 来遍历
next()指针默认指向第一个元素,每调一次指针会往后移一个
是一种接口机制,为了访问不通的数据结构而提供的一种统一的访问机制。(Array、Set、Map)
iterator底层原理
数组默认实现了iterator接口 next()指针每调用一次就会往后移动一位
// 数组迭代
let aa = ["你", "我", "它"];
let ceshi = aa[Symbol.iterator]();
console.log(ceshi.next()); // {value: '你', done: false}
console.log(ceshi.next()); // {value: '我', done: false}
console.log(ceshi.next()); // {value: '它', done: false}
console.log(ceshi.next()); // {value: undefined, done: true}
for (let item of aa) {
console.log(item);
// 你
// 我
// 它
}
// 自定义对象迭代
let obj = {
name: "小德",
type: [2, 4, 6, 8]
};
// 实现 obj 的 Symbol.iterator 方法
obj[Symbol.iterator] = function() {
let index = 0;
return {
next: () => {
return number>=obj.type.length?{value: undefined, done: true}:{value: obj.type[number++], done: false}
}
};
};
// 使用自定义迭代器遍历 obj.type
let ii = obj[Symbol.iterator]();
console.log("结果:", ii.next()); // 结果: {value: 2, done: false}
console.log("结果:", ii.next()); // 结果: {value: 4, done: false}
console.log("结果:", ii.next()); // 结果: {value: 6, done: false}
console.log("结果:", ii.next()); // 结果: {value: 8, done: false}
console.log("结果:", ii.next()); // 结果: {value: undefined, done: true}
// 使用 for...of 循环直接遍历 obj
for (let items of obj) {
console.log("结果:", items);
// 结果: 2
// 结果: 4
// 结果: 6
// 结果: 8
}
注意:对象里面写方法function不能少
十七、Promise异步处理
原始的回调地狱
使用Promise就是为了解决这种回调地狱:
Promise:承诺。未来某个时间节点反馈数据给你。
Promise是Es6异步编程的一种解决方案(目前最先进的解决方案是async和acait的搭配(ES8),但是它们是基于promise的),从语法上讲,Promise是一个对象或者说是构造函数,用来封装异步操作并可以获取其成功或失败的结果。
从语法上来说:Promise是一个构造函数
从功能上来说: promise对象用来封装一个异步操作)并可以获取其成功/失败的结果值
Promise对象代表一个异步操作,
有三种状态: pending(进行中)、fulfilled(已成功)和rejected 己失败)
Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。一旦状态改变,就「凝固」了,会一直保持这个状态,不会再发生变化。
并且一旦状态改变,promise.then()绑定的函数就会调用。
基本语法
//Promise 基本语法
let promise = new Promise((resolve,reject)=>{
//resolve 成功要执行的函数
//reject 失败要执行的函数
setTimeout(()=>{
resolve({
name:"小德",
id:1
})
},2000)
})
//回调函数
promise.then(res=>{
//成功
console.log(res);
},err=>{
//失败
console.log("执行失败");
})
then 链式接收,解决回调地狱
多个请求异步执行 链式的 .then()
想保证多个异步操作按照一定的顺序执行,就需要在每个 .then() 内部返回 Promise 对象,以便控制异步操作的流程
//封装请求
let fu = (url,data)=>{
return new Promise((resolve,reject)=>{
//假设都执行成功
setTimeout(resolve,2000,请求成功——请求${url},参数是:${data})
})
}
//调用then链式
fu("请求1",{id:1}).then(res=>{
console.log(res);
return fu("请求2",{id:2});
},err=>{
console.log(err);
}).then(res2=>{
console.log(res2);
return fu("请求3",{id:3});
},err2=>{
console.log(err2);
}).then(res3=>{
console.log(res3);
},err3=>{
console.log(err3);
});
Promise.all([p1,p2,p3]) 将多个Promise包装成一个Promise
场景:请求多张图片信息,希望图片是都加载好,一次性显示出来
- Promise.all([p1,p2,p3]) 基本语法
- 相当于all的话,不管你这个接口执行多久,都会等多个Promise都执行完毕返回结果了,才会按顺序一次返回结果
- 多个Promise每个Promise只要实例化了都会执行,只要其中一个不成功,那么他就直接停止了,会返回某个对应请求不成功的错误信息;
let promise1 = new Promise((resolve,reject)=>{
resolve("第一次请求结果");
});
let promise2 = new Promise((resolve,reject)=>{
setTimeout(reject,2000,"第二次请求结果");
});
let promise3 = new Promise((resolve,reject)=>{
setTimeout(reject,5000,"第三次请求结果");
});
let aa = Promise.all([promise1,promise2,promise3]);
console.log(aa);
aa.then(res=>{
console.log(res);
},err=>{
console.log(err);
})
//结果 按顺序返回
//['第一次请求结果', '第二次请求结果', '第三次请求结果']
Promise.race([p1,p2,p3]) 将多个Promise包装成一个Promise
- 谁最先响应得到谁的响应结果
- 相当于race的话,不管你这个接口执行多久,谁最先反应就返回那个Promise的结果
- 多个Promise每个Promise只要实例化了都会执行,其中某个执行最快,那么他就直接停止了,会返回该最快对应请求的信息
//Promise.race([p1,p2,p3]) 基本语法
let promise1 = new Promise((resolve,reject)=>{
// reject("第一次请求结果");
setTimeout(resolve,6000,"第二次请求结果");
});
let promise2 = new Promise((resolve,reject)=>{
setTimeout(resolve,2000,"第二次请求结果");
});
let promise3 = new Promise((resolve,reject)=>{
setTimeout(resolve,5000,"第三次请求结果");
});
let aa = Promise.race([promise1,promise2,promise3]);
console.log(aa);
aa.then(res=>{
console.log(res);
},err=>{
console.log(err);
})
十八、Module
有js文件都在一个html中引入,造成以下不良影响:
- 请求过多:首先我们要依赖多个模块,那样就会发送多个请求,导致请求过多;
- 依赖模糊:我们不知道他们的具体依赖关系是什么,也就是说很容易因为不了解他们之间的依赖关系导致加载先后顺序出错。
- 难以维护:以上两种原因就导致了很难维护,很可能出现牵一发而动全身的情况导致项目出现严重的问题。
ES6 Module模块带来的好处?
- 避免变量污染,命名冲突;
- 提供代码的复用率、维护性;
- 提供有效依赖处理关系;
- Module:提供可维护性、独立性,保护变量,避免重名
语法:
使用Module后,要用只能先导出在引入(export | import)
声明了是Module以后,关键字:export 导出 | import 引入
语法:
**export { a,b,c} **
import {a,b} from ‘./plugin.js’
基本使用
导入的变量是不可以修改的,只能拿来用;可以用别名as
//export导出变量
//导出变量
//方式一:
let name = "小德";
let age 20;
let address="天河区";
export {name,age,address}
//方式二:
export let name="张三";
export let age 25;
export let address="花都区";
export let obj ={
name:zhaoliu',
age:20
}
//导出函数
export function fn(){
console.log("我是fn函数...");
}
--------------------------------------------------------------
//import导入模块
//1.导入语法
import {name,age,address,obj,fn}from './export.js'
console.log("import:"name,age,address,obj);
2.导入的变量不可以修改的
age=60;//报错!!!
//3.取别名as
//当导入的变量名与自身定义的名称冲突时,可以取别名
import {fn as fn1} from './export.js'
function fn(){
console.log("我是import里面的fn函数");
fn();
fn1();
模块的整体加载
//之前的写法
import {name,age,address,obj,fn}from './export.js'
console.log("import:"name,age,address,obj);
//通配符 *
import * from './export.js'
console.log("import:"name,age,address,obj);
//或者
import * as zhu from './export.js'
console.log("import:"zhu.name,zhu.age,zhu.address,zhu.obj);
默认导出
一个模块内只能有一个默认导出,default
export defalut function(){
}
不需要{},相当于导出什么,接受的引入的就是什么,因此导入也不需要{};
它本身直接取个名字就是导出的东西;