这篇文章我会在持续学习中不断更新,并给出每个具体语法demo的验证结果
一、ES6++
什么是ECMA
中文名称是欧洲计算机制造商协会,这个组织的目的是评估,开发和认可电信和计算机标准。1994年后该组织改名为ECMA国际
什么是ECMAscript
ECMAscript是由ECMA 国际通过ECMA-262标准化的脚本程序设计语言
1、ES6
1.1 let变量声明以及声明特性
//变量不能重复声明
let star = 'zll';
let star = 'wxl';//此时会报错
//块级作用域有效 全局、函数、ecal
{ //if for else while
let str='zzl';
}
console.log(str);//此时会报错,因为拿不到
// 不存在变量提升
console.log(name);//会报错,不能使用name这个变量
let name='zzl';
//不影响作用域链
{
let name='zzl';//虽然是块级作用域,但是不影响作用域链的效果
function fn(){
console.log(name);
}
fn();//可以拿到
}
1.1.1 let const和var的区别?
// var 存在变量提升(在声明语句代码之前可以使用,不会报错) let const 存在暂时性死区(声明之后在声明语句后面代码才可以用)
console.log(a) console..log(a) 报错 console.log(a) 报错
var a = 10 let a = 10 const a = 10
// vat 声明的变量可以重复声明 let const声明的变量不能重复声明
var a = 10 let a = 10 const a = 10
var a = 20 let a = 20 报错 const a = 14 报错
// var 没有块级作用域 let const 有块级作用域(例如:在while for 循环中)
for(var i = 0;i<10;i++){} for(let i = 0;i<10;i++){}
i泄漏为全局变量,存在于全局变量中 i有块级作用域
// var let 是声明变量,变量值可以改变 cosnt 声明常量,值不能改(准确来说,内存地址不能变),而且声明时必须赋值
1.2 const 声明常量以及特点
1,const声明常量
2,一定要赋初始值
3,常量的值不能再修改
4,也是块级作用域,
5,声明常量
const CL='zzl';
// 对于数组和对象的元素修改,不算2中的常量修改,不会报错
const SZ=['zzl','wxl'];
SZ.push('ll');//此时不会报错,因为只是修改数据,并没有修改地址
1.3 变量的解构赋值
//数组解构赋值
const nameS=['zzl','wxl','ll'];
let [name1,name2,name3]=nameS;
console.log(name1);//输出zzl
//对象的结构赋值
const data={
name:'zzl',
age:'22',
active:function(){
console.log('我会飞');
}
}
let {name,age,active}=data;
console.log(name); // 输出zzl
active(); // 调用方法,输出我会飞
let {active} = data;
active();
1.4 模板字符串
let str=`字符串`
// 它可以支持换行
// 变量拼接
let name='zzl';
let data=`${name}是个人` // 'zzl是个人'
1.5 对象简化写法
// es6允许在大括号里面直接写入变量名和函数名,作为对象的属性和方法
let name='zzl';
let fun=function(){
console.log('我是人');
}
const data={
name, //可以直接写变量名,不用再写值了
fun
}
console.log(data); // 结果输出 name:'zzl',fun()
const data={
// 旧的方法定义
// act:function(){
// console.log('我会动');
// }
//新的方法定义
act(){
console.log('我会动');
}
}
1.6 箭头函数以及声明特点
// 声明一个函数
let fn=function(a,b){
}
// 箭头函数声明一个函数
let fn=(a,b)=>{
}
fn(1,2);
1.6.1 特点
// 箭头函数的额this是静态的,this始终指向函数声明时所在作用域下的this的值
function getname(){
//name是zzl,直接调用的话this值指向window
}
console.log(this.name);//zzl
let getname2=()=>{
//而这个箭头函数是在全局作用域下声明的,所以this也是指向window
console.log(this.name);//zzl
}
window.name='zzl';
const data={
name:'wxl'
}
//直接调用
getname();//name是zzl,直接调用的话this值指向window
getname2();//而箭头函数是在全局作用域下声明的,所以this也是指向window
// call方法调用
getname.call(data);//此时普通函数this的值已经变为data了。
getname2.call(data);
//输出wxl,因为它的this依然指向函数getname2声明时所在作用域下的this的值window.name='zzl';
// 不能作为构造实例化对象
let Person=(name,age)=>{
this.name=name;
this.age=age;
}
let me=new Person('zzl','22');//此时会报错,箭头函数不能作为实例化对象。
//不能使用arguments变量
// arguments:用来保存实参的
let fn=()=>{
console.log(arguments);
}
fn(1,2,3);//会报错,不用用来保存实参
1.6.2 简写箭头函数
// 省略小括号
let add=(n)=>{
return n+n;
}
console.log(add(9));
//简写为下面----------
//当形参有且只有一个的时候,可以省略小括号
let add=n=>{
return n+n;
}
console.log(add(9));
//省略花括号
//当代码体只有一条语句的时候
let sum=(n)=>{
return n*n;
};
console.log(sum(9));
//简写为下面----------
//当代码体只有一条语句的时候,此时return必须省略,而且语句的执行结果就是函数的返回值
let sum=(n)=>n*n;
console.log(sum(9));
//小括号和花括号同时省略----------
let sum=n=>n*n;
console.log(sum(9));
1.6.3 箭头函数注意事项
// 箭头函数适合与this无关的回调,比如定时器,数组的方法回调。
// 箭头函数不适合与this有关的回调,比如DOM元素的事件回调、对象的方法。
btn.addEventListener("click",function(){
//此时普通函数的this指向事件缘
//如果使用箭头函数,事件源将变成外部作用域的this值,即这个函数所在的作用域
})
1.6.4 箭头函数中的this
// 箭头函数不会创建自己的this,它只会从自己的作用域链的上一层继承this
function Person() {
this.age = 0;
setInterval(function growUp() {
this.age++; // 定时器中使用普通函数,this永远指向window,因为是window调用定时器 所以this.age 值得是Nan
}, 1000);
}
var p = new Person();
// '------------------------------------'
function Person() {
this.age = 0
setInterval(()=> { // 箭头函数没有自己this,只能往上一层去找
console.log(this) // Person对象
this.age++;
console.log(this.age)
}, 1000);
}
var p = new Person();
1.6.5 笔试题
var myObject = {
foo: "bar",
func: function () {
var self = this; // self = myObject
console.log(this.foo); // 'bar'
console.log(self.foo); // 'bar'
(function () {
console.log(this.foo); // undefined // 立即执行函数里面this永远指向window
console.log(self.foo); // 'bar'
}());
}
};
myObject.func();
// ----------------------------------------------------------------------------
function plugin () {
this.a = 1
this.b = {
x: 1,
confirm: function(title, callback) {
this.title = title
console.log(this, '2')
callback()
}
}
console.log(this, '1')
}
var obj = new plugin()
obj.b.confirm('弹窗', () => {
console.log(this, '3')
})
1.6.6 箭头函数与普通函数的区别?
1,this 的指向问题
// 箭头函数没有自己的作用域,所以没有自己的this,箭头函数中的this指向箭头函数当前函数声明时的作用域链的上一层
// 普通函数有自己的作用域
2,call() 和apply()
// 箭头函数里this的指向不受call,apply的指向,只跟定义位置作用域链的上一层有关,而普通函数可以
3,箭头函数不能当构造函数,而且没有Prototype属性,而普通函数可以
4,箭头函数没有arguments对象,普通函数有arguments对象,箭头函数中的参数可以用...参数名称来获取剩余参数
5,箭头函数不能作为generator函数,普通函数可以
1.6.7 箭头函数的this的指向?
// 箭头函数里的this指向沿着其作用域链往上找
代码的执行结果是什么?
1.7 函数参数初始值
function add(a,b,c=10){
}
//函数参数初始值与结构赋值结合使用
function data({name,age='22'}){
console.log(name);
console.log(age);
}
data({
name='zzl',
// age='23'
})
1.7.1 rest参数
// 用于获取函数的实参,可以代替arguments。
function data(){
console.log(arguments); // 输出的是一个对象
}
data('zzl','wxl');
// rest参数
function data(...args){
console.log(args); //输出的是一个数组,可以使用filter some...
}
data('zzl','wxl');
// --------------------------------------------------------------
// rest参数必须放在参数最后。
function data(a,b,...args){
console.log(a);//1
console.log(b);//2
console.log(args);// [3,4,5]
}
data(1,2,3,4,5)
// --------------------------------------------------------------------------------------
const obj1={
q:'zzl'
}
const obj2={
s:'wxl'
}
const data={...obj1,...obj2}; //相当于合并了两个对象
console.log(data); // {q:'zzl',s:'wxl'}
1.8 扩展运算符
// 它能将数组转换为逗号分隔的参数序列
const names=['zzl','wxl','ll'];
function data(){
console.log(arguments);
}
//不用扩展运算符
data(names); // 只输出一个结果,是一个数组
// 用扩展运算符
data(...names); // 输出3个结果,等价于:data('zzl','wxl','ll'),即参数序列
// --------------------------------------------------------
// 如果数组里面有引用类型的话,扩展呢运算符也只是浅拷贝。
// 可以用来数组的合并
// 数组的克隆
// 伪数组转为真正的数组
// 数组的合并
const a=['zzl'];
const b=['wxl'];
const c=[...a,...b]; // c=['zzl','wxl']
//将伪数组转为真正的数组
const divs=document.querySelectorAll('div'); // 得到伪数组
const divdata=[...divs]; // 转为真正的数组
1.8 symbol
// 它一种新的数据类型,表示独一无二的值,类似于字符串的数据类型。
// 它的值是唯一的,用来解决命名冲突的问题。
// 它的值不能于其他数据进行运算。
// 它定义的对象属性不能使用for…in 循环遍历,但是可以使用Reflect.ownKeys来获取对象的所有键名。
// 创建symbol
let s = Symbol();
// 下面的两个symbol对象时不一样的
let s2 = Symbol('aini')
let s3 = Symbol('sini')
// symbol.for创建
let s4 = Symbol.for('aini')
let s5 = Symbol.for('aini')
let game={
//假如有很多代码很多变量名
}
//声明一个对象
let data={
//Symbol保证了up和down的属性名是独一无二的,
// 所以添加进去也不怕也不怕有属性名冲突
up:Symbol(),
//up属性的数据类型为Symbol
down:Symbol()
};
//第一种添加方式
//把这个Symbol添加到game方法中
game[data.up]=function(){
console.log('我会飞'); //安全的向这个对象中添加了两个方法
}
game[data.down]=function(){
console.log('我会爬');
}
console.log(game);
//
//第二种添加方式
let play={
name='run',
[Symbol('say')]:function(){
console.log('我会说话');
},
[Symbol('sleep')]:function(){
console.log('我会睡觉');
}
}
console.log(paly);
//补充代码
const title1 = Symbol("title")
const title2 = Symbol("title")
const info={
[title1]:'zzl',
[title2]:'wxl'
}
Symbol有很多内置方法
1.8.1 Symbol的数据类型及特性?
// 数据类型的声明
let sym = Symbol()
// 作用:保证每一个对象的名字都是独一无二的
// 可以接受字符串作为参数,作位对Symbol实例的描述
let sym = Symbol('aini')
// symbol 的参数是一个对象,可以调用该对象的toString 方法将其转换为字符串,然后生成一个symbol值
// symbol函数的参数只是对当前Symbol 值的描述,因此相同参数的Symbol函数返回的值是不相等的
let s1 = SYmbol('s1') s2 = Symbol('s1') cosnole.log(s1 === s2) // false
// symbol值不能与其他类型的值进行运算,否则会报错
//symbol 值可以显示转换为字符串和Boolean 但是不能转换为Number
// Object.getOwnPropertySymbol 方法可以获取指定对象的所有Symbol属性名
// symbol声明的属性是没办法通过Object.key() 拿到,所以可以实现私有属性
1.8.2 Symbol.iterator的作用?
// Symbol.iterator 为每一个对象定义了默认的迭代器。该迭代器可以被for ... of 循环使用
1.9 迭代器
任何数据结构只要部署了Iterator接口,就可以使用for…of 来遍历.
1.9.1 具备iterator接口的数据类型
// Array
// Argunments
// Set
// Map
// String
// TypedArray
// NodeList
// 这个接口就是对象里面的一个属性,属性的名字叫Symbol.iterator,也可以自己对结构进行布置iterator接口。
// -------------------------------------------------------------------------------------------
for( let i in data){
i是键名
}
for( let i of data){
i是键值
}
const arr=['zzl','wxl'];
console.log(arr);
//arr里面就有Symbol.iterator这个属性。
1.9.2 工作原理
// 先创建一个指针对象,指向当前数据结构的起始位置
// 第一次调用对象的next方法,指针自动指向数据结构的第一个成员
// 接下来不断调用next方法,指针一直往后移动,直到指向最后一个成员
// 每次调用next方法就会返回一个包含value和done属性(是否完成)的对象
let xiyou = ['唐曾','孙悟空','猪八戒','沙僧']
let iterator = xiyou[Symbol.iterator]();
// 调用对象的next方法
console.log(iterator.next()) // {value:'唐曾',done: false}
console.log(iterator.next()) // {value:'孙悟空',done: false}
console.log(iterator.next()) // {value:'猪八戒',done: false}
console.log(iterator.next()) // {value:'沙僧',done: false}
console.log(iterator.next()) // {value:undefined,done: true}
1.9.3 手写迭代器
//声明一个对象
const data={
name:'zzl',
lis:[
'wxl',
'll',
'hll'
],
//自己给某些结构加上iterator接口
[Symbol.iterator](){
//索引变量
let index=0;
let _this=this;
return {
//返回一个指针对象,即创建一个指针对象
next:function(){
//创建对象的next方法
// 返回一个包含value和done属性(是否完成)的对象
if(index < _this.lis.length){
const result = { value:_this.lis[index], done:false};
index++;
return result;
}else{
return {value: undefined, done:true};
}
}
};
}
}
//自定义遍历这个对象
for(let v of data){
console.log(v);
}
console.log('--------------------')
console.log(data);
1.9.4 iterator接口的作用?
// Iterator 是一个接口,用于处理所有不同的数据结构遍历器。只要数据结构定义了Iterator接口时可以用for of 来循环遍历
// for of 循环的执行过程
var arr = ['blue','green','yellow','balck','orange']
let it = iterator(arr)
console.log(it.next())
console.log(it.next())
console.log(it.next())
console.log(it.next())
function iterator(arr){
let nextIndex = 0
return {
next: function() {
return nextIndex < arr.length ? {
value: arr[nextIndex ++] ,done: true
} : {
value : undefined,done : false
}
}
}
}
// Iterator 接口部署在数据结构的Symbol.iteratro属性
// 一个数据结构只要具有Symbol.Iterator属性,就可以认为是'可遍历的'
// Symbol.Iterator 属性是一个函数,每次运行会返回一个生成器对象包含俩属性value和done
// 原生具备Iterator接口的数据结构
// Array Map Set String 函数的arguments对象
// 伪数组转换为真正数组的方法
1, Array.prototype.slice.call(arguments)
2, Array.from(arguments)
//
1.10 数组的常用方法
1.10.1 forEach()
// 第一个参数:数组元素;
// 第二个参数:数组索引;
// 第三个元素:数组对象本身
var numbers = [1,2,3,4,5,4,3,2,1];
numbers.forEach(function(item,index,array){
console.log(item,index,array[index]);
});
// -----------------------------------------------------------
// 为每个元素加 1
var data = [1,2,3,4];
data.forEach(function(v,i,a){
a[i] = v + 1;
//会改变原数组元素
});
console.log(data);
// 如果只关心数组元素的值,可以只使用一个参数,额外的参数将被忽略
// ------------------------------------------------------------------
// 求和
var data = [1,2,3,4];
var sum = 0;
data.forEach(function(value){ //第一个参数数组元素,相当于遍历多有元素;
sum += value;
//不会改变原数组元素;
});
console.log(sum);
// -------------------------------------------------------------------
// (1)forEach()方法无法在所有元素都被传递给调用的函数之前终止遍历,即没有像 for 循环中使用的 break 语句;
// (2)如果要提前终止,必须把 forEach()方法放在 try 块中,并能抛出一个异常:
function foreach(a,f,t){
try{
a.forEach(f, t);
}
catch(e){
if(e === foreach.break) return;
else throw e;
}
}
foreach.break = new Error("StopIteration");
1.10.2 every() ------------ some()
//(1)最相似的是 every()和 some(),它们都是数组的逻辑判定:用于判定数组元素是否满足某个条件;
//(2)对 every(),传入的函数必须对每一项都返回 true,这个方法才返回 true;
//(3)而 some()是只要传入的函数对数组中的某一项返回 true,就会返回 true,否则返回 false;
var numbers = [1,2,3,4,5,4,3,2,1];
var everyResult = numbers.every(function(item,index,array){
return (item > 2);
});
console.log(everyResult); // false
var someResult = numbers.some(function(item,index,array){
return (item > 2);
});
console.log(someResult); // true
// (4)一旦 every()和 some()确定该返回什么值时,它们就会停止遍历数组元素;
// (5)(some()在判定函数第一次返回 true 后就返回 true,但如果判定函数一直返回 false,它将会遍历整个数组;every()恰好相反,它在判定函数第一次返回 false 后就返回 false,但如果判定函数一直返回 true,它将会遍历整个数组)。
// (6)根据数学上的惯例,在空数组上调用时,every()返回 true,some()返回 false;
1.10.3 filter()
//(1)返回的是数组元素是调用的数组的一个子集;
//(2)回调函数是用来逻辑判定的,该函数返回 true 或 false;如果返回的是 true 或真值,则该函数处理的数组元素就被添加到返回的子集数组中;
//(3)filter()方法会跳过稀疏数组中缺少的元素,它的返回数组总是密集的
var numbers = [1,2,3,4,5,4,3,2,1];
var filterResult = numbers.filter(function(item,index,array){
return item>2;
});
console.log(filterResult);
var evenResult = numbers.filter(function(value,index){
return index % 2 == 0; // [1, 3, 5, 3, 1] index 是索引
});
console.log(evenResult);
// 压缩稀疏数组
var sparse = [1,,,4];
var dense = sparse.filter(function(){
return true; //过滤稀疏元素
});
console.log(dense);
// 压缩并删除 undefined 和 null 元素
var sparse = [1,null,3,undefined,,6];
var dense = sparse.filter(function(v){
return v !== undefined && v != null;
});
console.log(dense);
//(注:该方法返回的是符合条件的数组元素;)
1.10.4 map()
//(1)将调用的数组的每个元素传递给回调函数,并将调用的结果组成一个新数组返回;
//(2)其不会修改原始数组,如果是稀疏数组,返回的也是相同方式的稀疏数组,即具有相同的长度、相同的缺失元素;
var numbers = [1,2,3,4,5,4,3,2,1];
var mapResult = numbers.map(function(item,index,array){
return item*2;
});
console.log(mapResult);
//-------------------------------------------------
var a = [1,null,3,undefined,5];
var b = a.map(function(v){
return v * v;
});
console.log(b);
// --------------------------------------------
//不做任何处理
var spare = [1,,4,null,undefined,NaN,6];
var dense = spare.map(function(v,i,a){
});
console.log(dense);
// ----------------------------------------------------
// 返回所有数组元素
var spare = [1,,4,null,undefined,NaN,6];
var dense = spare.map(function(v,i,a){
return v;
});
console.log(dense);
// -----------------------------------------------------------
//能处理的元素进项处理,空元素返回空元素;
var spare = [1,,4,null,undefined,NaN,6];
var dense = spare.map(function(v,i,a){
return v*v; //进行数据类型转换,由于 null 和 undefined 不能进行数据类型转换});
console.log(dense) //,所以都返回 NaN
1.10.5 reduce() --------------------- reduceRight()
//(1)reduce()和 reduceRight();这两个方法都会迭代数组的所有项,然后构建一个最终返回的值;
//(2)其中,reduce()方法从数组的第一项开始,逐个遍历到最后;而 reduceRight 则从数组的最后一项开始,向前遍历到第一项;
//(3)这两个方法都接收两个参数:调用的函数 callbackfn 和作为归并基础的初始值initialValue(可选的);
//(4)这个函数接收 4 个参数:前一个值、当前值、项的索引和数组对象;其返回的任何值都会作为第一个参数自动传给下一项;第一次迭代发生在数组的第二项上,因此第一个参数是数组的第一项,第二个参数就是数组的第二项
// 求和
var values = [1,2,3,4];
var sum = values.reduce(function(prev,cur,index,array){
return prev + cur;
});
console.log(sum); // 10
// 第一次遍历:prev=1 cur=2;
// 第二次遍历:prev=3 cur=3;
// 第三次遍历:prev=6 cur=4;
// 第四次遍历:prev=10
/*
说明:第一次执行回调函数,prev 与 cur 分别为数组的第 1 和第 2 项,即 prev 是 1,cur 是 2;第二次,
prev 是第一次执行的结果,即 3(1+2 的结果),cur 是数组的第三项,即是 3,以此类推;
reduce()参数函数的参数也可以只使用两个参数,如:
*/
//-------------------------------------------------------------------------------
// 求和
var values = [1,2,3,4];
var sum = values.reduce(function(prev,cur){
return prev + cur;
});
console.log(sum); // 10
// ----------------------------------------------------------------------------------
// 求积
var product = values.reduce(function(prev,cur){
return prev * cur;
});
console.log(product); // 24
//------------------------------------------------------------------------------
// 求最大值
var max = values.reduce(function(prev,cur){
return prev > cur ? prev : cur;
});
console.log(max); // 4
// reduce()方法的第二个参数 initialValue 是回调函数的第一个参数 previousValue 的初始值;如:
// 把以上所有示例添加第二个参数,如:
var values = [1,2,3,4];
var sum = values.reduce(function(prev,cur){
return prev + cur;
},2); // 12
/*
意思就是 pre 不是第一项,而是给 pre 赋一个值,cur 从第一项开始迭代说明,第一次调用时,prev 为 initialValue 值,即为 2,cur 为数组第一项,即为 1;第二次调用,prev 为 3,cur 为 2,以此类推;reduceRight()的作用类似,只不过方向相反,即按照数组索引从高到低(从右到左)处理数组;如:
*/
var values = [1,2,3,4,5];
var sum = values.reduceRight(function(prev,cur,index,array){
return prev + cur; //21
},6);
//pre 的初始值为 6;
console.log(sum); // 15
// ---------------------------------------------------------------------
// 求 2^(3^4),乘方操作的优先顺序是从右到左
var a = [2,3,4];
var big = a.reduceRight(function(accu, value){
return Math.pow(value, accu);
});
console.log(big);
//在空数组上,不带初始值参数的 reduce()将导致类型异常;如果数组只有一个元素并且没有指定初始值,或者一个空数组并且指定了一个初始值,该方法只是简单的返回那个值而不会调用回调函数;
var a = [];
// var b1 = a.reduce(function(x,y){return x + y}); // no initial value 会报错
// var b2 = a.reduce(function(x,y){return x + x}); / /也会报错
var b3 = a.reduce(function(x,y){return x + y},3); //只返回初始值
console.log(b3); // 3
// (1)使用 reduce()还是 reduceRight(),主要取决于要从哪头开始遍历数组;除此之外,它们完全相同。
//(2)reduce()和 reduceRight()是典型的函数式编程;有些地方,会把其回调函数称为化简函数,这个化简函数的作用就是用某种方法把两个值组合或化简为一个值,并返回化简后的值;如以上的示例,化简函数通过各种方法,组合了两个值
1.11 生成器
// 生成器就是一个特殊的函数,是异步编程新的解决方案。分别通过以下代码可知运行逻辑。
function * fun(){
console.log('zzl');
}
let a=fun();
// console.log(a);//输出一个迭代器对象(有next方法)
a.next(); // 输出zzl
// --------------------------------------------
function * fun(){
console.log('zzl');
console.log('wxl');
}
let a=fun();
console.log(a);
//输出一个迭代器对象(有next方法)
a.next();
//输出zzl wxl 即全部输出
// -------------------------------------------------------------
function * fun(){
console.log('zzl');
yield '我要暂停'
console.log('wxl');
}
let a=fun();
console.log(a);
//输出一个迭代器对象(有next方法)
a.next();
//输出zzl 我要暂停
// -----------------------------------------------------------------------------
function * fun(){
console.log('zzl');
yield '我要暂停'
console.log('wxl');
}
let a=fun();
// console.log(a);//输出一个迭代器对象(有next方法)
a.next();//输出zzl
a.next();//输出wxl
function * fun(){
// console.log('zzl');
yield '我要暂停'
// console.log('wxl');
}
let a=fun();
console.log(a);
//输出一个迭代器对象(有next方法)
for(i of fun()){
console.log(i);//我要暂停
}
// -----------------------------------------------------
function * fun(){
// console.log('zzl');
yield '我要暂停'
// console.log('wxl');
}
let a=fun();
console.log(a.next()); //输出 {value: '我要暂停', done: false}
1.11.1 生成器函数的参数传递
function * fun(arg){
console.log(arg); //输出aaa
let one = yield 111;
console.log(one); //输出bbb
let two = yield 222;
console.log(two); //输出ccc
let three = yield 333;
console.log(three); //输出ddd
}
let a=fun('aaa');
console.log(a.next());//第一次调用next
//next方法可以传入实参
//第二次调用next的实参将作为第一个yield的整体返回结果
console.log(a.next('bbb')) // 输出{value: 222, done: false}
console.log(a.next('ccc')) // 输出{value: 333, done: false}
console.log(a.next('ddd')) // 输出{value: undefined, done: true}
1.11.2 生成器函数实例
// 实例1:在控制台每隔一秒分别打印111,222,333
// 回调地狱
setTimeout(() => {
console.log(111);
setTimeout(() => {
console.log(222);
setTimeout(() => {
console.log(333);
}, 3000);
}, 2000);
}, 1000);
// 解决方法
function one(){
setTimeout(()=>{
console.log(111);
a.next(); //定时器运行完调用下一个,实现了异步编程
},1000)
}
function two(){
setTimeout(()=>{
console.log(222);
a.next();
},2000)
}
function three(){
setTimeout(()=>{
console.log(333);
a.next();
},3000)
}
function *fun(){
yield one();
yield two();
yield three();
}
//调用生成器函数
let a=fun();
a.next();
结果演示
// -------------------------------------------------------------------------------------------------
// 实例2 模拟获取用户数据,订单数据,商品数据
function one(){
setTimeout(()=>{
let data='用户数据';
a.next(data);//第二次调用next的实参将作为第一个yield的整体返回结果
},1000)
}
function two(){
setTimeout(()=>{
let data='订单数据';
a.next(data);
},1000)
}
function three(){
setTimeout(()=>{
let data='商品数据';
a.next(data);
},1000)
}
function *fun(){
let users= yield one();
console.log(users); //用户数据 作为第一个yield的整体返回结果
let orders= yield two();
console.log(orders); // 订单数据
let goods= yield three();
console.log(goods); // 商品数据
}
//调用生成器函数
let a=fun();
a.next();
1.12 Promise
// 它是es6中异步编程的新的解决方案。相当于一个构造函数。
function fun(){
return new Promise((resolve,reject)=>{
//如果成功就调用resolve
let data='数据库中的数据';
resolve(data);
//promise状态变为成功
//如果失败就调用reject
let err='数据库读取失败';
reject(err);//promise状态变为失败
})
}
var promise = fun()
promise.then(
//调用then方法
function(success){
// promise状态变为成功后then会调用第一个回调函数
console.log(success);
},function(err){
// promise状态变为失败后then会调用第二个回调函数
console.log(err);
}
)
1.12.1 promise封装读取文件
cosnt fs = require('fs')
fs.readFile('./aini.txt',(err,data) => {
// 如果失败,则抛出异常
if(err) throw err;
// 如果没哟出错,则输出内容
console.log(data.toString())
})
// 使用Promise封装
cosnt p = new Promise(function(resolve,reject){
fs.readFile('./aini.txt',(err,data) => {
// 如果失败,则抛出异常
if(err) reject(err)
// 如果没哟出错,则输出内容
resolve(data)
})
})
p.then(function(value){
console.log(value.toString())
},function(resson){
console.log('读取失败!!!')
})
1.12.2 Promise 封装Ajax请求
const p = new Promise(function(resolve,reject){
cosnt xhr = new XMLHttpRequest()
xhr.open('GET','https://api.apiopen.top/getJoke')
xhr.send()
xhr.onreadystatechange = function(){
if (xhr.randyState == 4){
// 判断状态码
if (xhr.status >= 200 && xhr.status <300){
// 表示成功
resolve(xhr.response)
}else{
reject(xhr.status)
}
}
}
})
p.then(function(value){
console.log(value.toString())
},function(resson){
console.log(reason)
})
1.12.3 Promise.property.then()
const p = new Promise((resolve,reject){
setTimeout(() => {
resolve('用户数据')
},1000)
})
// 调用,then方法的结果也是promise对象,结果由回调函数的结果决定
// 1,如果回调函数返回的结果是非promise类型的数据,状态为成功,返回值为对象的成功值,如果不写return,默认返回undefined,也是成功的promise
const p1 = p.then(value => {
console.log(value)
return '11232' // 成功的Promise
}.err => {
console.warn(reason)
})
// 2,如果返回的是promise对象,则这个promise对象的返回结果决定,then方法返回的结果
const p1 = p.then(value => {
console.log(value)
return new Promise(funtion(resolve,reject){
resolve('ok') // 成功,则then方法返回结果是成功
reject('失败了') // 失败,则then方法返回的结果是失败的
)
}.err => {
console.warn(reason)
})
// 如果抛出错误,then 也是个失败的结果
const p1 = p.then(value => {
console.log(value)
throw new Error('出错啦') // then的状态为rejected
}.err => {
console.warn(reason)
})
// 链式调用
p.then(value=> {
return new Promise(function(resoleve,reject){
resolve()
})
},reason => {
}).then(value=> {
return new Promise(function(resoleve,reject){
resolve()
})
},reason => {
}).then(value=> {
return new Promise(function(resoleve,reject){
resolve()
})
},reason => {
})
1.12.4 Promsie 多个文件内容读取
// 回调地狱
cosnt fs = require('fs')
fs.readFile('./aini.txt',(err,data1) => {
fs.readFile('./norah.txt',(err,data2) => {
fs.readFile('./dilnur.txt',(err,data3) => {
let reault = data1 + data2 + data3
})
})
})
// 使用promise实现
const p = new Promise(function(resolve,reject){
fs.readFile('./aini/txt',(err,data) => {
resolve(data)
})
})
p.then(value => {
return new Promise((resolve,reject) => {
fs.readFile('./norah.txt',(err,data) => {
resolve([value,data])
})
})
}).then(value => {
return new Promise((resolve,reject) => {
fs.readFile('./dilnur.txt',(err,data) => {
value.push(data)
resolve(data)
})
})
}).then(value => {
console.log(value.join('\r\n'))
})
1.12.5 Promise对象的catch()方法
const p = new Promise((reslove,reject)=>{
setTimeout(() => {
reject('出错了')
},1000)
})
// 第一种
p.then(function(value){
// 成功的结果
},function(reason){
// 失败的结果
})
// 第二种
p.then(function(value){
// 成功的结果
}).catch(function(reason){
//失败的结果
})
// 第三种
p.then(function(value){
// 成功的结果
})
p.catch(function(reason){
//失败的结果
})
1.13 Set
Set的数据类型及特性?
// set 是ES6新增的复杂数据类型,类似于数组,但是值没有重复分
// 新建
const s = new Set()
// 可以接受数组或字符串当参数
const s = new Set([1,2,3,4,2,3])
const s1 = new Set('abcdeftg')
// set 可以转化成数组,通过...扩展运算符或者Array.from()方法
let arr = [...s] // 等同于 Array.from(s)
let arr1 = [...s1] // 等同于 Array.from(s1)
// set 用于数组去重
// set 里面的判断机制是 === 运算符 (NaN 例外)
let set = new Set()
set.add(5)
set.add('5') // 5 '5' 是两个元素
// set 实例的属性和方法
1,size() 返回长度
2, has() 判断是否包含某个元素
2,forEach 方法
let set = new Set([1,3,2,3,54,3,2])
console.log(set)
set.forEach((item,index) => console.log(item + '-----------' + index))
// item 和 key 值一样
// 并集
let arr = [1,2,3,4]
let arr1 = [2,3,4,5,6]
let a = new Set(arr)
let b = new Set(arr1)
let set = new Set([...a,...b])
// 交集
let arr = [1,2,3,4]
let arr1 = [2,3,4,5,6]
let a = new Set(arr)
let b = new Set(arr1)
let intersect = new Set([..a].filter(x => b.has(a)))
// 差集
let arr = [1,2,3,4]
let arr1 = [2,3,4,5,6]
let a = new Set(arr)
let b = new Set(arr1)
let chaji = new Set([..a].filter(x => !b.has(a)))
1.14 Map
Map的数据类型及特性?
// Map 是ES6新增的复杂数据类型,类似于对象,也是键值对集合,但是键的范围不限于字符串,各种类型的值都可以当做键
// Map 是值与值的对应,是更完美的键值对结构的提现
// 新建Map数据的方法
1, const m = new Map()
// Map 的方法
1, set() m.set({x: 1}, 22)
2, get m.get({x: 1})
3, has() 判断某个键是否存在 m.has({x:1})
4, delete() 删除某个键值对
// 判断是不是Map类型的数据结构
let map = new Map()
1, map instanceof Map // true 通过原型链查找
2, Object.prototype.toString.call(map) // [object, Map]
// 为什么 Object.prototype.toString.call() 用的是原型链顶端的toString方法呢?
// 因为数组和对象对toString方法都进行了改写
// 可以接受数组作为参数
// 注意的是:数组成员是一个个表示键值对的数组
let map = new Map(
[
['name','aini'],
['age',22],
[{name:'norah'},'这是一个对象']
]
)
// 一个坑
map.get({name:'norah'}) // undefined 是因为 {} 与 {} 不是同一个对象
// 解决办法:
let obj = {name:'norah'}
let map = new Map(
[
['name','aini'],
['age',22],
[obj,'这是一个对象']
]
)
map.get(obj) // 这样才能获取正确的key值
// map参数的扩展
// 任何具有 Iterator 接口,且每个成员都是一个双元素的数组的数据结构,都可以当做map函数的参数
1.15 Class类
// 以手机类为例
function Phone(brand,price){
this.brand = brand
this.price = price
}
// 静态成员
//相当于 python里的staticmethod
Phone.name = '手机'
Phone.change = function(){
console.log('我可以改变世界')
}
// 相当于Python里的classmethod
Phone.ptototype.size = "5.5英寸"
Phone.prototy.call = function() {
console.log('我可以打电话')
}
ler Huawei = new Phone('华为',5999)
// ES6语法
class Phone{
// 构造函数方法
constructor(brand,price){
this.brand = brand
this.call = call
}
// 语法必须使用这个方法,不能用ES5老语法
call() {
console.log('我可以打电话')
}
}
ler Huawei = new Phone('华为',5999)
1.15.1 构造函数继承
function Phone(brand,price){
this.brand = brand
this.price = price
}
Phone.prototype.call = function(){
console.log('我可以打电话')
}
//智能手机
function SmartPhone(brand,price,color,size){
Phone.call(this,brand,price){
this.color = color
this.size = size
}
}
// 设置子级构造函数的原型
SmartPhone.prototype = new Phone
SmartPhone.prototype.constructor = SmartPhone
// 声明子类的方法
SmartPhone.prototype.photo = function(){
console.log('我可以拍照')
}
SmartPhone.prototype.playGame = function() {
console.log('我可以打游戏')
}
const chuzi = new SmartPhone("锤子",2499,'黑色','505')
1.15.2 ES6里类的继承
class Phone{
constructor(brand,price){
this.brand = brand
this.price = price
}
call(){
console.log('我可以打电话')
}
}
class SmartPhone extends Phone{
constructor(brand,price,color,size){
super(brand,price)
this.color = color
this.size = size
}
photo() {
console.log('我可以拍照')
}
playGame() {
console.log('我可以打游戏')
}
}
const xioami = new SmartPhone('xiaomi',2499,'黑色','505')
1.15.3 子类对父类方法的重写
class Phone{
constructor(brand,price){
this.brand = brand
this.price = price
}
call(){
console.log('我可以打电话')
}
}
class SmartPhone extends Phone{
constructor(brand,price,color,size){
super(brand,price)
this.color = color
this.size = size
}
photo() {
console.log('我可以拍照')
}
playGame() {
console.log('我可以打游戏')
}
// 可以对父类方法重写
call(){
console.log('我可以进行视频通话')
}
}
const xioami = new SmartPhone('xiaomi',2499,'黑色','505')
1.15.4 class中的getter和setter
class Phone{
get price() {
console.log('我被读取了')
return 'i love you '
}
set price(val){
console.log('价格修改了')
this.val = val
}
}
let s = new Phone()
console.log(s.price)
// '我被读取了'
// 'i love you ' -------------------> 这才是s.price的返回值
s.price = 'free' // '价格修改了'
1.16 数值扩展
// Number.EPSILON -------------------------- 是Javascript表示的最下精度
// Number.isFinite -------------------------- 检测一个数是否为有限数
// Number.isNaN -----------------------------检测一个数值是否为 NaN
// Number.parseInt Number.parseFloat-------- 字符串转整数
// Number.isInteger ---------------------------判断一个数是否为整数
// Math.trunc ----------------------------------将数字的小数部分抹掉
// Math.sign -------------------------------------判断一个数到底为正数,负数还是0 -----------> 1 -1 0
// 进制数的表示方法
let b = 0b1010
let o = 0o777
let d = 100
let x = 0xfff
1.17 对象方法扩展
// Object.is --------------------------------> 判断两个值是否完全相等
console.log(Object.is(NaN,NaN)) // true
console.log(NaN === NaN) // false
// Object.assign ----------------------------> 对象的合并
// Object.setPropertyOf ----------------------> 设置Object原型对象
// Object.getPropertyOf-----------------------> 获取对象的属性
1.18 模块化
// 模块化是指将一个大的文件,拆分成许多小的文件,然后将小文件组合起来,一个个小文件就是一个模块。
// 防止命名冲突
// 代码复用
// 高维护性
1.18.1 ES Module
//第一种暴露方法:分别暴露
export let a='zzl'; //向外暴露
export function fun(){
console.log('我是谁');//向外暴露
}
//第二种暴露方法:统一暴露[ 注意{}导出的并不是对象 ]
let a='zzl';
export{a,fun};
//第三种:默认暴露
export default{
a:'zzl',
fun:function(){
console.log('我是谁');
}
}
1.18.2 html代码引入js模块
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script type="module">
//第一种引入方式:引入mi.js暴露的模块
import * as m from './模块化/m1.js';
console.log(m.a); //zzl
//第二种引入方式解构赋值形式:引入mi.js暴露的模块
import {a as a1,fun} from './模块化/m1.js';
console.log(a1);//可以给a一个别名a1 输出zzl
//默认暴露的引入比较特殊,引入语法是
import * as m from './模块化/m1.js';
m.default.fun();
//默认暴露的解构赋值
import{default as mm} from './模块化/m1.js';
console.log(mm);
//简便形式 针对默认暴露
import m from './模块化/m1.js';
//动态调用模块,即使用时再导入
import('./模块化/m1.js').then(module =>{
module.fun();
})
</script>
</body>
</html>
1.18.3 module.exports 与 exports的使用与区别
// 二者都是专门负责导出的东西,都属于commonJS规范,是同步加载的.
// exports
a.js
const a=10
//导出 (实际上exports就是一个对象)
exports._a = a
b.js
// 引入a.js导出的对象,实际就是exports对象
const ba =require('./a.js') // === exports对象
console.log('输出',ba._a) //输出 10
// exports
a.js
const a=10
//导出 (实际上exports就是一个对象)
// exports._a=a
/**
* 实际上module也是一个对象
* 下边三行代码具体逻辑如下:
* 1、module.exports = exports 即4行的exports,
* 此时是引用赋值,并且可以继续使用原来b.js的导入方式
* 2、重新给module.exports一个新对象地址{_a:a}
* 3、b.js导入方式不变,因为只是对象地址,该对象module.exports依然是导出对象
*/
module.exports = {
_a:a
}
b.js
// 引入a.js导出的对象,实际就是module.exports对象
const ba=require('./a.js') // === module.exports对象
console.log('输出',ba._a) //输出 10
1.18.4 require()与import()
if (true){
import * from './a.js'
}
/*
以上代码是错误的,因为imprt关键字不支持在这类代码里,但是require支持,因为require本质是函数,但是,require不支持在浏览器里运行,并且它是同步执行的,所以ESModule里可以用import函数来引入外部模块,并且它是异步执行的.
*/
if(true){
import('./a.js').then(res=>{
console.log(res._a)
}).catch(err=>{
console.log(err)
})
}
// <script src type=”module”> 也是异步加载执行的,相当于加了async属性
1.19 Babel
// 它能将js的新语法转化为旧语法,以便更好的兼容。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
// 安装工具 babel-cli babel-preset-env browserify(打包工具)
// 初始化 npm init -yes
// 安装 npm i babel-cli babel-preset-env browserify -D
// 因为局部安装,用 npx 执行命令
// npx babel aini.js(源文件目录) -d dist/js(打包的目标目录) --presers=babel-preset-env
// 打包 npx browserify dist/js/app.js -o dist/bundle.js
</body>
</html>
2、ES7
2.1 新特性
Array.prototype.includes // 检测数组中是否包含某个元素
const name = ['aini','norah','dilnur']
console.log(name.includes('aini')) // true
** // 幂次方
console.log(2 ** 10) // 1024 Math.pow(2,10) 一样的效果
3、ES8
3.1 async函数
// async 函数的返回值为promise对象
// promise对象的结果由 async函数执行的返回值决定
async function fn() {
return "aini"
// 如果跑错误,则返回的是失败的promise对象
// 如果返回的是一个promise对象,结果取决于返回的promise对象的状态
}
const result = fn();
console.log(result) // promise对象
3.2 await 表达式
// await 必须写在async函数中
// await 右侧的表达式一般为promise对象
// await 返回的是promise成功的值
// await 的promise失败了,就会抛出异常,需要通过try...catch捕获处理
const p = new Promise((resolve,reject) => {
resolve('用户数据')
})
async function fn() {
const result = await p
console.log(result) // '用户数据'
}
fn()
// --------------------------------------------------------------------------------------
async function fn() {
try{
const result = await p
console.log(result) // '用户数据'
}catch(e){
console.log(e)
}
}
fn()
3-3 async和await结合
const fs = require("fs");
function readWeiXue() {
return new Promise((resolve,reject) => {
fs.readFile('/为学.md',(err,data)=>{
if(err){
reject(err)
}else{
resolve(data)
}
})
})
}
function readChaYangShi() {
return new Promise((resolve,reject) => {
fs.readFile('/插秧诗.md',(err,data)=>{
if(err){
reject(err)
}else{
resolve(data)
}
})
})
}
function readGuanShuYouGan() {
return new Promise((resolve,reject) => {
fs.readFile('/观书有感.md',(err,data)=>{
if(err){
reject(err)
}else{
resolve(data)
}
})
})
}
async function main() {
let weixue = await readWeiXue()
let chayang = await readChaYangShi()
let guanshu = await readGuanShuYouGan()
console.log(weixue.toString())
console.log(chayang.toString())
console.log(guanxue.toString())
}
3-4 对象方法的扩展
Object.values() // 返回一个给定对象所有可枚举属性值的数组
Object.entries() // 返回一个给定对象自身可遍历属性的数组
Object.getOwnPropertyDescriptors() // 返回指定对象自身属性的描述对象
4、ES9
4.1 扩展运算符和rest参数
function connect({host, port, ...user}){
console.log(host) // 127.0.0.1
console.log(port) // 3306
console.log(user) // {username: 'aini', password: 'aini'}
}
connect({
host:'127.0.0.1',
port:3306,
username:'aini',
password:'aini'
})
const a = {
q: 'aini'
}
const b = {
w: 'norah'
}
const c = {
e: 'dilnur'
}
const d = {
r: 'zhuozi'
}
// 对象的合并
const name = {...a, ...b, ...c, ...d}
console.log(name) // {q: 'aini', w: 'norah', e: 'dilnur', r: 'zhuozi'}