出格网站建设公司,南沙商城网站建设,深圳制作网站的公司哪家好,windows建立网站let和const命名
let基本用法-块级作用域
在es6中可以使用let声明变量#xff0c;用法类似于var
⚠️ let声明的变量#xff0c;只在let命令所在的代码块内有效
{let a 10;var b 20;
}
console.log(a); //a is not defined
console.log(b); //20不存在变量提升
var命令…let和const命名
let基本用法-块级作用域
在es6中可以使用let声明变量用法类似于var
⚠️ let声明的变量只在let命令所在的代码块内有效
{let a 10;var b 20;
}
console.log(a); //a is not defined
console.log(b); //20不存在变量提升
var命令会发生变量提升现象即变量可以在声明之前使用值为undefined。这种现象多多少少是有些奇怪的按照一般的逻辑变量应该在声明语句之后才可以使用。
为了纠正这种现象let命令改变了语法行为它所声明的变量一定在声明后使用否则报错
//var的情况
console.log(c);//输出undefined
var c 30;//let的情况
console.log(c);// 报错ReferenceError
let c 30;不允许重复声明
let不允许在相同作用域内重复声明同一个变量var允许
let c 10;
let c 30;
console.log(c); //报错function func(arg) {let arg; // 报错
}暂时性死区
暂时性死区说的就是let和const命令声明变量的特征。
在代码块内使用let命令声明变量之前该变量都是不可用的。这在语法上称为暂时性死区(temporal dead zone简称 TDZ)
ES6明确规定如果区块中存在let和const命令这个区块对这些命令声明的变量从一开始就形成了封闭作用域凡是在声明之前就是使用这些变量就会报错
//暂时性死区
var tmp 20;
if(true){// tmpabc;//ReferenceError: Cannot access tmp before initializationlet tmp;console.log(tmp);//undefinedtmp123;console.log(tmp);//123
}// typeof x;//ReferenceError: Cannot access x before initialization
let x;
//暂时性死区也意味着typeof不再是一个百分之百的安全操作
//如果一个变量根本没有被声明使用typeof反而不会报错
console.log(typeof c);//undefined为什么需要块级作用域
原因一内层变量可能会覆盖外层变量
function foo(a){console.log(a);if(12){var a hello 小马哥;}
}
var a 10;
foo(a);原因二用来计数的循环遍历泄露为全局变量
var arr []
for(var i 0; i 10; i){arr[i] function(){return i;}
}
console.log(arr[5]());变量i只用来控制循环但是循环结束后它并没有消失用于变量提升泄露成了全局变量。
解决循环计数问题
//解决方式一使用闭包
var arr []
for(var i 0; i 10; i){arr[i] (function(n){return function(){return n;}})(i)
}
//解决方式二使用let声明ivar arr []
for(let i 0; i 10; i){arr[i] function () {return i;}
}const基本用法-声明只读的常量
这意味着const一旦声明变量就必须立即初始化不能留到以后赋值。对于const来说只声明不赋值就会报错。
const a 10;
a 20;//报错const b; //报错与let命令相同点
块级作用域暂时性死区不可重复声明
let和const使用建议
在默认情况下用const,而只有你在知道变量值需要被修改的情况下使用let
模板字符串
传统的 JavaScript 语言输出模板通常是这样写的
const oBox document.querySelector(.box);
// 模板字符串
let id 1,name 小马哥;
let htmlTel ullipid: id /ppname: name /p/li/ul;
oBox.innerHTML htmlTel;上面的这种写法相当繁琐不方便,ES6引入了模板字符串解决这个问题
let htmlTel ullipid:${id}/ppname:${name}/p/li
/ul;函数
function pick(obj, ...keys){// ...keys解决了arguments的问题let result Object.create(null);for(let i0; ikeys.length; i){result[keys[i]] obj[keys[i]];}return result;
}let book {title: es6教程,author: 小马哥,year: 2019
}
let bookData pick(book, year, author);
console.log(bookData);function pick(obj){let result Object.create(null);for(let ii; iarguments.length; i){result[arguments[i]] obj[arguments[i]];}return result;
}
let book {title: es6教程,author: 小马哥,year: 2019
}
let bookData pick(book, author, year);
console.log(bookData);扩展运算符
//剩余运算符把多个独立的合并到一个数组中
//扩展运算法将一个数组分割并将各个项作为分离的参数传给函数
const maxNum Math.max(20, 30);
console.loh(maxNum);//处理数组中的最大值使用apply
const arr [10, 20, 50, 30, 90, 100, 40];
console.log(Math.max.apply(null, arr));//es6 扩展运算符
console.log(Math.max(...arr));箭头函数
//使用来定义 function(){}等于与(){}//let add function(a,b){return ab};
//let add (a, b) {return a b};
let add (a, b) a b;
console.log(add(10, 20));let fn () hello world 123;
console.log(fn());
let fn (function(){return function(){console.log(hello es6);}
})();let fn ((){return (){console.log(hello ea62);}
})();
fn();let getObj id {return {id: id,name: 小马}
}
let getObj id ({id: id,name: 小马
})
let obj getObj(1);
console.log(obj);箭头函数this指向和注意事项
//没有this指向
//es5中this指向取决于调用该函数的上下文对象
let PageHandle {id: 123,//init函数不要使用箭头函数因为会指向windowinit: function(){//箭头函数没有this指向 箭头函数内部this值只能通过查找作用域链来确定一旦使用箭头函数当前就不存在作用域document.addEventListener(click, (event) {this.doSomeThings is not a function//console.log(this);//documentthis.documentThings(event.type);}, false)//false不冒泡//this.documentThings(event.type);//}.bind(this), false)//false不冒泡//document.addEventListener(click, function(event){//this.doSomeThings is not a function//console.log(this);//document//this.documentThings(event.type);//})},doSomeThings: function(type){console.log(事件类型${type}当前id:${this.id})}
}
PageHandle.init();//使用箭头函数的注意事项
//1.使用箭头函数 函数内部没有arguments
let getVal (a, b) {console.log(argments);//is not definedreturn a b;
}
console.log(getVal(1, 3));
//2.箭头函数不能使用new关键字来实例化对象
let Person (){};
let p new Person();//Person is not a constructor解构赋值
解构赋值是对赋值运算符的一种扩展。它通常针对数组和对象进行操作。 优点代码书写简洁且易读性高 数组解构
在以前为变量赋值只能直接指定值
let a 1;
let b 2;
let c 3;ES6允许我们这样写:
let [a,b,c] [1,2,3];如果解构不成功变量的值就等于undefined
let [foo] [];
let [bar, foo] [1];foo的值都会等于undefined
对象解构
解构可以用于对象
let node {type:identifier,name:foo
}let {type,name} node;
console.log(type,name)//identifier foo对象的解构赋值时可以对属性忽略和使用剩余运算符
let obj {a:{name:张三},b:[],c:hello world
}
//可忽略 忽略b,c属性
let {a} obj;
//剩余运算符 使用此法将其它属性展开到一个对象中存储
let {a,...res} obj;
console.log(a,res);
//res{b:[], c:hello world}默认值
let {a,b 10} {a:20};函数参数解构赋值
function add([x, y]){return x y;
}add([1, 2]); // 3使用默认值
function addCart(n,num0){return nnum;
}
addCart(10);//10
addCart(10,20); //30用途 交换变量的值 let x 1;
let y 2;
let [x,y] [y,x];上面代码交换变量x和y的值这样的写法不仅简洁而且易读语义非常清晰。 从函数返回多个值 函数只能返回一个值如果要返回多个值只能将它们放在数组或对象里返回。有了解构赋值取出这些值就非常方便。
// 返回一个数组function example() {return [1, 2, 3];
}
let [a, b, c] example();// 返回一个对象function example() {return {foo: 1,bar: 2};
}
let { foo, bar } example();函数参数的定义
解构赋值可以方便地将一组参数与变量名对应起来。
// 参数是一组有次序的值
function f([x, y, z]) { ... }
f([1, 2, 3]);// 参数是一组无次序的值
function f({x, y, z}) { ... }
f({z: 3, y: 2, x: 1});提取JSON数据
解构赋值对提取 JSON 对象中的数据尤其有用
let jsonData {id: 42,status: OK,data: [867, 5309]
};//data: number把data重命名为number
let { id, status, data: number } jsonData;
//对象的解构赋值的内部机制是先找到同名属性然后再赋给对应的变量。真正被赋值的是后者而不是前者
console.log(id, status, number);
// 42, OK, [867, 5309]函数参数的默认值
输入模块的指定方法
加载模块时往往需要指定输入哪些方法。解构赋值使得输入语句非常清晰。
const {ajax} require(xxx)ajax()函数的扩展
带参数默认值的函数
ES6之前不能直接为函数的参数指定默认值只能采用变通的方法
function log(x,y){y y || world;console.log(x,y);
}
log(hello);//hello world
log(hello,china) //hello china
log(hello,)//hello worldES6 允许为函数的参数设置默认值即直接写在参数定义的后面。
function log(x, y World) {console.log(x, y);
}log(Hello) // Hello World
log(Hello, China) // Hello China
log(Hello, ) // HelloES6 的写法还有两个好处首先阅读代码的人可以立刻意识到哪些参数是可以省略的不用查看函数体或文档其次有利于将来的代码优化即使未来的版本在对外接口中彻底拿掉这个参数也不会导致以前的代码无法运行。 默认的表达式可以是一个函数
function getVal(val) {return val 5;
}
function add2(a, b getVal(5)) {return a b;
}
console.log(add2(10));//20小练习
请问下面两种写法有什么区别
// 写法一
function m1({x 0, y 0} {}) {return [x, y];
}// 写法二
function m2({x, y} { x: 0, y: 0 }) {return [x, y];
}上面两种写法都对函数的参数设定了默认值区别是写法一函数参数的默认值是空对象但是设置了对象解构赋值的默认值写法二函数参数的默认值是一个有具体属性的对象但是没有设置对象解构赋值的默认值。
// 函数没有参数的情况
m1() // [0, 0]
m2() // [0, 0]// x 和 y 都有值的情况
m1({x: 3, y: 8}) // [3, 8]
m2({x: 3, y: 8}) // [3, 8]// x 有值y 无值的情况
m1({x: 3}) // [3, 0]
m2({x: 3}) // [3, undefined]// x 和 y 都无值的情况
m1({}) // [0, 0];
m2({}) // [undefined, undefined]m1({z: 3}) // [0, 0]
m2({z: 3}) // [undefined, undefined]rest参数
ES6 引入 rest 参数形式为...变量名用于获取函数的多余参数这样就不需要使用arguments对象了。rest 参数搭配的变量是一个数组该变量将多余的参数放入数组中。
function add(...values) {let sum 0;for (var val of values) {sum val;}return sum;
}add(2, 5, 3) // 10上面代码的add函数是一个求和函数利用 rest 参数可以向该函数传入任意数目的参数。
箭头函数 ***
ES6允许使用箭头定义函数
let f vv;
//等同于
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 () hello world;let doThing () {}
//如果箭头函数直接返回一个对象必须在对象外面加上括号否则会报错。
let getId id ({id: id,name: mjj}) //注意
let obj getId(1);箭头函数的作用 使表达更加简洁 const isEven n n % 2 0;
const square n n * n;简化回调函数 // 正常函数写法
[1,2,3].map(function (x) {return x * x;//[1,4,6]
});// 箭头函数写法
[1,2,3].map(x x * x);使用注意点 没有this绑定 let PageHandler {id:123,init:function(){document.addEventListener(click,function(event) {this.doSomeThings(event.type);},false);},doSomeThings:function(type){console.log(事件类型:${type},当前id:${this.id});}
}
PageHandler.init();//解决this指向问题
let PageHandler {id: 123,init: function () {// 使用bind来改变内部函数this的指向document.addEventListener(click, function (event) {this.doSomeThings(event.type);}.bind(this), false);},doSomeThings: function (type) {console.log(事件类型:${type},当前id:${this.id});}
}
PageHandler.init();let PageHandler {id: 123,init: function () {// 箭头函数没有this的指向箭头函数内部的this值只能通过查找作用域链来确定// 如果箭头函数被一个非箭头函数所包括那么this的值与该函数的所属对象相等否则 则是全局的window对象document.addEventListener(click, (event) {console.log(this);this.doSomeThings(event.type);}, false);},doSomeThings: function (type) {console.log(事件类型:${type},当前id:${this.id});}
}
PageHandler.init();箭头函数中没有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对象的扩展
属性的简洁表示法
const name 张三;
const age 19;
const person {name, //等同于name:nameage,// 方法也可以简写sayName() {console.log(this.name);}
}
person.sayName();这种写法用于函数的返回值将会非常方便。
function getPoint() {const x 1;const y 10;return {x, y};
}getPoint()
// {x:1, y:10}对象扩展运算符
const [a, ...b] [1, 2, 3];
a // 1
b // [2, 3]解构赋值
对象的解构赋值用于从一个对象取值相当于将目标对象自身的所有可遍历的enumerable、但尚未被读取的属性分配到指定的对象上面。所有的键和它们的值都会拷贝到新对象上面。
let { x, y, ...z } { x: 1, y: 2, a: 3, b: 4 };
x // 1
y // 2
z // { a: 3, b: 4 }解构赋值必须是最后一个参数否则会报错 let { ...x, y, z } obj; // 句法错误
let { x, ...y, ...z } obj; // 句法错误扩展运算符
对象的扩展运算符...用于取出参数对象的所有可遍历属性拷贝到当前对象之中。
let z { a: 3, b: 4 };
let n { ...z };
n // { a: 3, b: 4 }扩展运算符可以用于合并两个对象。
let ab { ...a, ...b };
// 等同于
let ab Object.assign({}, a, b);对象的方法
//is()
// 比较两个值是否严格相等
console.log(NaN NaN);//false
console.log(Object.is(NaN, NaN));//true//assign()
//对象的合并
//Obejct.assign(target, obj1, obj2);//返回合并之后的新对象
let newObj Object.assign({}, {a: 1}, {b: 2});
console.log(newObj);//{a:1,b:2}Symbol类型
原始数据类型Symbol它表示是独一无二的值
最大的用途用来定义对象的私有变量
const name1 Symbol(name);
const name2 Symbol(name);
console.log(name name);//falselet s1 Symbol(s1);
console.log(s1);//Symbol(s1)
let obj {};
obj[s1] 张三;
// 如果用Symbol定义的对象中的变量取值时一定要用[变量名]
console.log(obj[s1]);//张三
console.log(obj.s1);//undefindedlet obj2 {[s1]: 张三;
};
console.log(obj2[s1]);//张三//用.声明时obj输出的是{sa: 张三}
//用[]声明时输出obj是{Symbol(name): 张三}//获取Symbol声明的属性
let s Object.getOwnPropertySymbols(obj2);
console.log(s[0]);//张三
let m Reflect.ownKeys(obj2);
console.log(m[0]);//张三
Set集合数据类型
Set集合表示无重复值的有序列表
let set new Set();
console.log(set);//Set(0) {}//添加元素 add()
set.add(2);
set.add(4);
set.add(4);
set.add([1,2,3]);
console.log(set);//set(3) {2, 4, Array(3)}
//删除元素 delete()
set.delete(2);
//判断元素是否存在集合中 has()
console.log(set.has(4));//true
//获取集合长度
console.log(set.size);//2
//遍历集合
set.forEach((val, key){//键值一样console.log(val);console.log(key);
});//将set转换成数组
let set2 new Set([1,2,3,3,3,4]);
console.log(set2);//Set(4) {1,2,3,4}
let arr [...set2];
console.log(arr);//(4) [1,2,3,4]//1.set中对象的引用无法被释放
let set3 new Set();
let obj1 {};
set3.add(obj1);
//释放当前的资源
obj1 null;
console.log(set3);//set3还存在objlet set4 new WeakSet();
let obj2 {};
//释放当前的资源
obj2 null;
console.log(set4);//set4没有obj//WeakSet
//1.不能传入非对象类型的参数
//2.不可迭代
//3.没有forEach()
//4.没有size属性Map数据类型
let map new Map();
console.log(map);//Map(0) {}
map.set(name, 张三);
map.set(age, 20);
console.log(map);// Map(2) {name 张三, age 20}
console.log(map.has(name));//true
map.delete(name);
console.log(map);//Map(1) {age 20}
map.clear();
console.log(map);//Map(0) {}map.set([a,[1,2,3]], hello);
console.log(map);//Map(1) {Array(2) hello}let m new Map([[a, 1],[c, 2]
]);
console.log(m);//Map(2) {a 1, c 2}//WeakMap
//1.不能传入非对象类型的参数
//2.不可迭代
//3.没有forEach()
//4.没有size属性数组的扩展方法
//1.from() 将伪数组转换成真正的数组
function add(){console.log(arguments);//Arguments(3) [1, 2, 3, callee: ƒ, Symbol(Symbol.iterator): ƒ]//es5转换let arr [].slice.call(arguments);console.log(arr);//(3) [1, 2, 3]//es6写法let arr1 aArray.from(arguments);console.log(arr);//(3) [1, 2, 3]
}
add(1,2,3);let lis document.querySelectorAll(li);
console.log(lis);//NodeList(4) [li,li,li,li]
console.log(Array.from(lis));//(4) [li,li,li,li]
// 扩展运算符 将伪数组转换成真正的数组
console.log([...lis]);//(4) [li,li,li,li]//from() 还可以接受第二个参数用来对每个元素进行处理
let liContents Array.from(lis, ele ele.textContent);
console.log(liContents);//(4) [1, 2, 3, 4]//2.of() 将一组任意数据类型的值转换成数组
console.log(Array.of(3,11,20,30));//(4) [3,11,20,30]//3.copyWithin() 数组内部将制订位置的元素复制到其他位置返回当前数组
// 从3位置往后的所有数值替换从0位置往后的3个数值
[1,2,3,8,9,10].copyWithin(0, 3);//[8,9,10,8,9,10]//4.find() findIndex()
//find()找出第一个符合条件的数值成员
let num [1,2,-10,-20,9,2].find(n n0);
console.log(num);// -10//findIndex()找出第一个符合条件的数据成员的索引
let index [1,2,-10,-20,9,2].findIndex(n n0);
console.log(index);// 2// 5.entries() keys() values() 返回一个遍历器 可以使用for...of循环进行遍历
console.log([a, b].keys());//Array Iterator {}
//取键名
for(let index of [a, b].keys()){console.log(index);// 0 1
}
//取值
for(let value of [a, b].values()){console.log(value);// a b
}
//取键值对
for(let [index,value] of [a,b].entries()){console.log(index,value);// 0 a 1 b
}
let letter [a, b, c];
let it letter.entries();
console.log(it.next().value);//(2) [0, a]
console.log(it.next().value);//(2) [1, b]
console.log(it.next().value);//(2) [2, c]
console.log(it.next().value);//undefined// 6.includes() 返回一个布尔值表示某个数组是否包含给定的值
console.log([1,2,3].includes(2));//true
console.log([1,2,3].includes(4));//false
// 之前 indexOf() 返回值存在是1 不存在是-1迭代器Interator
Interator是一种新的遍历机制
//1.迭代器是一个接口能快捷的访问数据通过Symbol.iterator来创建迭代器通过迭代器的next()方法获取迭代之后的结果
//2.迭代器是用于遍历数据结构的指针数据库的游标//使用迭代
const items [one, two, three];
// 1.创建新的迭代器
const ite items[Symbol.iterator]();
console.log(ite.next());//{value: one, done: false} done为false表示遍历没有完成为true表示遍历完成了
console.log(ite.next());//{value: two, done: false}
console.log(ite.next());//{value: three, done: false}
console.log(ite.next());//{value: undefined, done: true} 生成器Generator
generator函数可以通过yield关键字将函数挂起为了改变执行流提供了可能同时为了做异步编程提供了方案
//generator和普通函数的区别
//1.function后面 函数名之前有个*
//2.只能在函数内部使用yield表达式让函数挂起function* func(){console.log(one);yield 2;console.log(two);yield 3;console.log(end);
}
let fn func();
console.log(fn.next());//one {value: 2, done: false}
console.log(fn.next());//two {value: 3, done: false}
console.log(fn.next());//end {value: undefined, done: true}//总结generator函数是分段执行的yield语句是暂停执行而next()是恢复执行的 function* add(){console.log(start);// x不是yield 2的返回值它是next()调用时恢复yield()执行时传入的实参let x yield 2;console.log(one: x);let y yield 3;console.log(two: y);return x y;
}
const fn add();
console.log(fn.next());//start {value:2,done:false}
console.log(fn.next(20));//one: 20 {value:2,done:false}
console.log(fn.next(30));//two: 30 {value: 50 ,done:false}//利用生成器给没有迭代器接口的对象创建一个迭代器接口
//使用场景为不具备Interator接口的对象提供了遍历操作
function* objectEntries(obj){//获取对象的所有key保存到数组中 [name,age]const propKeys Object.keys(obj);for(const propkey of propKeys){yield [propkey, obj[propkey]]}
}
const obj {name: 张三,age: 20
}
obj[Symbol.iterator] objectEntries;
console.log(obj);//obj对象现在具备了Symbol.iteratorfor(let [key,value] of objectEntries(obj)){console.log(${key}:${value});
}//另外一种方式
Object.prototype【Symbol.iterator】 function* gerne() {let keys Object.keys(this); //this指向传入实例化的对象本身for (let key of keys) {yield obj【key】;}}
let obj new Object({
name:wkf,
age:23
})
for(let 【key,val】 of obj【symbol.iterator】(){
}Generator的应用
Generator部署ajax操作让异步代码同步化
script srchttps://cdn.staticfile.org/jquery/1.10.2/jquery.min.js/script
script// $.ajax({// url: http://127.0.0.1/users,// method: get,// success(res) {// console.log(res);// $.ajax({// url: http://127.0.0.1/users,// method: get,// success(res) {// console.log(res);// }// });// }// });//解决回调地狱的问题function* main(){let res yield request(http://127.0.0.1/users);console.log(res);//执行后面的操作console.log(数据请求完成可以继续操作)}const ite main();ite.next();function request(url){$.ajax({url:http://127.0.0.1/users,method: get,success(res){ite.next(res);}});}function* load(){loadUI();yield showData();hideUI();}let itLoad load();itLoad.next();//加载loading...页面//数据加载完成...异步操作//loading关闭掉function loadUI(){console.log(加载loading...页面)}function showData(){//模拟异步操作setTimeout(() {console.log(数据加载完成...);itLoad.next();}, 1000)}function hideUI(){console.log(隐藏loading...页面)}//loadUI();//showData();//hideUI();//加载loading...页面//隐藏loading...页面//数据加载完成...
/scriptPromise 对象
异步编程模块在前端开发中显得越来越重要。从最开始的XHR到封装后的Ajax都在试图解决异步编程过程中的问题。随着ES6新标准的到来处理异步数据流又有了新的解决方案。在传统的ajax请求中当异步请求之间的数据存在依赖关系的时候就可能产生不优雅的多层回调俗称”回调地域“(callback hell)这却让人望而生畏Promise的出现让我们告别回调地域写出更优雅的异步代码。
回调地狱带来的负面作用有以下几点
代码臃肿。可读性差。耦合度过高可维护性差。代码复用性差。容易滋生 bug。只能在回调里处理异常。 在实践过程中却发现Promise并不完美Async/Await是近年来JavaScript添加的最革命性的的特性之一Async/Await提供了一种使得异步代码看起来像同步代码的替代方法。接下来我们介绍这两种处理异步编程的方案。 什么是Promise Promise 是异步编程的一种解决方案 从语法上讲Promise是一个对象通过它可以获取异步操作的消息 从本意上讲它是承诺承诺它过一段时间会给你一个结果。 promise有三种状态pending(等待态)fulfilled(成功态)rejected(失败态) 状态一旦改变就不会再变。 创造promise实例后它会立即执行。 看段习以为常的代码
// Promise是一个构造函数自己身上有all,reject,resolve,race方法原型上有then、catch等方法
let p new Promise((resolve,reject){// 做一些异步操作setTimeout((){/* let res {ok:1,data:{name:张三}} */let res {ok:0,error:new Error(有错)}if(res.ok 1){resolve(res.data);}else{reject(res.error.message)}}, 1000)
})
p.then((val){console.log(val);//res.data的值
},(err){console.log(err);//res.error.message的值
})Promise的状态和值
Promise对象存在以下三种状态
Pending(进行中)Fulfilled(已成功)Rejected(已失败) 状态只能由 Pending 变为 Fulfilled 或由 Pending 变为 Rejected 且状态改变之后不会在发生变化会一直保持这个状态。 Promise的值是指状态改变时传递给回调函数的值
上面例子中的参数为resolve和reject他们都是函数用他们可以改变Promise的状态和传入的Promise的值
resolve 和 rejectresolve : 将Promise对象的状态从 Pending(进行中) 变为 Fulfilled(已成功)reject : 将Promise对象的状态从 Pending(进行中) 变为 Rejected(已失败)resolve 和 reject 都可以传入任意类型的值作为实参表示 Promise 对象成功Fulfilled和失败Rejected的值
then方法
p.then((data){console.log(data);return data;
},(error){console.log(error)
}).then(data{console.log(data);
})promise的then方法返回一个promise对象所以可以继续链式调用
上述代码我们可以继续改造因为上述代码不能传参
function timeout(ms) {return new Promise((resolve, reject) {setTimeout(() {resolve(hello world)}, ms);})
}
timeout(1000).then((value) {console.log(value);//hello world
})then方法的规则
then方法下一次的输入需要上一次的输出如果一个promise执行完后 返回的还是一个promise会把这个promise 的执行结果传递给下一次then中如果then中返回的不是Promise对象而是一个普通值则会将这个结果作为下次then的成功的结果如果当前then中失败了 会走下一个then的失败如果返回的是undefined 不管当前是成功还是失败 都会走下一次的成功catch是错误没有处理的情况下才会走then中不写方法则值会穿透传入下一个then中
Promise封装XHR对象ajax
const getJSON function (url) {return new Promise((resolve, reject) {const xhr new XMLHttpRequest();xhr.open(GET, url);xhr.onreadystatechange handler;xhr.responseType json;xhr.setRequestHeader(Accept, application/json);xhr.send();function handler() {console.log(this.readyState);//2 3 4if (this.readyState ! 4) {//4表示数据接收完成return;}if (this.status 200) {//成功resolve(this.response);} else {reject(new Error(this.statusText));}}})
}
getJSON(https://free-api.heweather.net/s6/weather/now?locationbeijingkey4693ff5ea653469f8bb0c29638035976).then((res) {console.log(res);}, function (error) {console.error(error);})//then方法的链式调用
getJSON(https://free-api.heweather.net/s6/weather/now?locationbeijingkey4693ff5ea653469f8bb0c29638035976).then((res){return res.HeWeather6;
}).then((HeWeather6){console.log(HeWeather6);
})catch方法
//catch(err{})方法等价于then(null,err{})
getJSON(https://free-api.heweather.net/s6/weather/now?locationbeijingkey4693ff5ea653469f8bb0c29638035976).then((json) {console.log(json);
}).then(null,err{console.log(err);
})
//等价于
getJSON(https://free-api.heweather.net/s6/weather/now?locationbeijingkey4693ff5ea653469f8bb0c29638035976).then((json) {console.log(json);
}).catch(err{console.log(err);
})resove()
resolve()方法将现有对象转换成Promise对象该实例的状态为fulfilled
let p Promise.resolve(foo);
//等价于 new Promise(resolveresolve(foo));
p.then((val){console.log(val);//foo
})reject()
reject()方法返回一个新的Promise实例该实例的状态为rejected
let p2 Promise.reject(new Error(出错了));
//等价于 let p2 new Promise((resolve,reject)reject(new Error(出错了)));
p2.catch(err {console.log(err);
})all()方法
all()方法提供了并行执行异步操作的能力并且再所有异步操作执行完后才执行回调
试想一个页面聊天系统我们需要从两个不同的URL分别获得用户的的个人信息和好友列表这两个任务是可以并行执行的用Promise.all实现如下
let meInfoPro new Promise( (resolve, reject) {setTimeout(resolve, 500, P1);
});
let youInfoPro new Promise( (resolve, reject) {setTimeout(resolve, 600, P2);
});
// 同时执行p1和p2并在它们都完成后执行then:
Promise.all([meInfoPro, youInfoPro]).then( (results) {console.log(results); // 获得一个Array: [P1, P2]
});race()方法
有些时候多个异步任务是为了容错。比如同时向两个URL读取用户的个人信息只需要获得先返回的结果即可。这种情况下用Promise.race()实现
let meInfoPro1 new Promise( (resolve, reject) {setTimeout(resolve, 500, P1);
});
let meInfoPro2 new Promise( (resolve, reject) {setTimeout(resolve, 600, P2);
});
Promise.race([meInfoPro1, meInfoPro2]).then((result) {console.log(result); // P1
});Promise.all接受一个promise对象的数组待全部完成之后统一执行success; Promise.race接受一个包含多个promise对象的数组只要有一个完成就执行success 举个更具体的例子加深对race()方法的理解
当我们请求某个图片资源会导致时间过长给用户反馈
用race给某个异步请求设置超时时间并且在超时后执行相应的操作
function requestImg(imgSrc) {return new Promise((resolve, reject) {var img new Image();img.onload function () {resolve(img);}img.src imgSrc;});
}
//延时函数用于给请求计时
function timeout() {return new Promise((resolve, reject) {setTimeout(() {reject(图片请求超时);}, 3000);});
}
Promise.race([requestImg(images/2.png), timeout()]).then((data) {console.log(data);document.body.appendChild(data);
}).catch((err) {console.log(err);
}); async 函数
异步操作是JavaScript编程的麻烦事很多人认为async函数是异步编程的解决方案
Async/await介绍
async/await是写异步代码的新方式优于回调函数和Promise。async/await是基于Promise实现的它不能用于普通的回调函数。async/await与Promise一样是非阻塞的。async/await使得异步代码看起来像同步代码再也没有回调函数。但是改变不了JS单线程、异步的本质。(异步代码同步化)
Async/await的使用规则 凡是在前面添加了async的函数在执行后都会自动返回一个Promise对象 async function test() {}let result test()
console.log(result) //即便代码里test函数什么都没返回我们依然打出了Promise对象await必须在async函数里使用不能单独使用 async test() {let result await Promise.resolve(success)console.log(result)
}
test();await后面需要跟Promise对象不然就没有意义而且await后面的Promise对象不必写then因为await的作用之一就是获取后面Promise对象成功状态传递出来的参数。 function fn() {return new Promise((resolve, reject) {setTimeout(() {resolve(success)})})
}async test() {let result await fn() //因为fn会返回一个Promise对象console.log(result) //这里会打出Promise成功后传递过来的success
}test();Async/Await的用法
使用await函数必须用async标识await后面跟的是一个Promise实例
function loadImg(src) {const promise new Promise(function (resolve, reject) {const img document.createElement(img)img.onload function () {resolve(img)}img.onerror function () {reject(图片加载失败)}img.src src})return promise
}
const src1 https://hcdn1.luffycity.com/static/frontend/index/banner2x_1574647618.8112254.png
const src2 https://hcdn2.luffycity.com/media/frontend/index/%E7%94%BB%E6%9D%BF.png
const load async function () {const result1 await loadImg(src1)console.log(result1)const result2 await loadImg(src2)console.log(result2)
}
load()
复制代码当函数执行的时候一旦遇到 await 就会先返回等到触发的异步操作完成再接着执行函数体内后面的语句。
async/await的错误处理
关于错误处理如规则三所说await可以直接获取到后面Promise成功状态传递的参数但是却捕捉不到失败状态。在这里我们通过给包裹await的async函数添加then/catch方法来解决因为根据规则一async函数本身就会返回一个Promise对象。
const load async function () {try{const result1 await loadImg(src1)console.log(result1)const result2 await loadImg(src2)console.log(result2)}catch(err){console.log(err);}
}
load();//需求获取和风天气 现在now的数据
const getJSON function (url) {return new Promise((resolve, reject) {const xhr new XMLHttpRequest();xhr.open(GET, url);xhr.onreadystatechange handler;xhr.responseType json;xhr.setRequestHeader(Accept, application/json);xhr.send();function handler() {console.log(this.readyState);//2 3 4if (this.readyState ! 4) {//4表示数据接收完成return;}if (this.status 200) {//成功resolve(this.response);} else {reject(new Error(this.statusText));}}})
}async function getNowWeather(url){//发送ajax 获取实况天气let res await getJSON(url);console.log(res);//获取HeWeather6的数据 获取未来3~7天的天气状况let arr await res.HeWeather6;return arr[0].now;
}
getNowWeather(https://free-api.heweather.net/s6/weather/now?locationbeijingkey4693ff5ea653469f8bb0c29638035976).then(now{console.log(now);
})为什么Async/Await更好
Async/Await较Promise有诸多好处以下介绍其中三种优势 简洁 使用Async/Await明显节约了不少代码。我们不需要写.then不需要写匿名函数处理Promise的resolve值也不需要定义多余的data变量还避免了嵌套代码。 中间值
在前端编程中我们偶尔会遇到这样一个场景我们需要发送多个请求而后面请求的发送总是需要依赖上一个请求返回的数据。对于这个问题我们既可以用的Promise的链式调用来解决也可以用async/await来解决然而后者会更简洁些
const makeRequest () {return promise1().then(value1 {return promise2(value1).then(value2 { return promise3(value1, value2)})})
}使用async/await的话代码会变得异常简单和直观
const makeRequest async () {const value1 await promise1()const value2 await promise2(value1)return promise3(value1, value2)
}提高可读性
下面示例中需要获取数据然后根据返回数据决定是直接返回还是继续获取更多的数据。
const makeRequest () {return getJSON().then(data {if (data.needsAnotherRequest) {return makeAnotherRequest(data).then(moreData {console.log(moreData)return moreData})} else {console.log(data)return data}})
}代码嵌套6层可读性较差它们传达的意思只是需要将最终结果传递到最外层的Promise。使用async/await编写可以大大地提高可读性:
const makeRequest async () {const data await getJSON()if (data.needsAnotherRequest) {const moreData await makeAnotherRequest(data);console.log(moreData)return moreData} else {console.log(data)return data }
}Class的基本用法
简介
JavaScript语言中生成实例对象的传统方法是通过构造函数
function Person(name,age) {this.name name;this.age age;
}
//原型
Person.prototype.sayName function() {return this.name;
}
let p new Person(小马哥,18);
console.log(p);上面这种写法跟传统的面向对象语言比如 C 和 Java差异很大很容易让新学习这门语言的程序员感到困惑
ES6 提供了更接近传统语言的写法引入了 Class类这个概念作为对象的模板。通过class关键字可以定义类。
基本上ES6 的class可以看作只是一个语法糖它的绝大部分功能ES5 都可以做到新的class写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。上面的代码用 ES6 的class改写就是下面这样
class Person {// constructor方法 是类的默认方法,通过new命令生成对象实例时,自动调用该方法,一个类必须有constructor方法,如果没有定义,会被默认添加constructor(name, age) {this.name name;this.age age;}//等同于Person.prototype function sayName(){}sayName(){return this.name;}
}
console.log(PersonPerson.prototype.constructor);类的方法内部如果含有this它默认指向类的实例 类的继承
//使用关键字extends
class Animal{constructor(name, age){this.name name;this.age age;}sayName(){return this.name;}sayAge(){return this.age;}
}class Dog extends Animal{constructor(name, age, color){super(name, age);//相当于 Animal.call(this,name,age);this.color color;}sayColor(){return ${this.name}是${this.age}岁了它的颜色是${this.color};}sayName(){return this.name name super.sayAge() this.color;}
}
let d1 new Dog(小黄, 28, red);
console.log(d1);
console.log(d1.sayName);Module 模块化
概述
历史上JavaScript 一直没有模块module体系无法将一个大程序拆分成互相依赖的小文件再用简单的方法拼装起来。其他语言都有这项功能比如 Ruby 的require、Python 的import甚至就连 CSS 都有import但是 JavaScript 任何这方面的支持都没有这对开发大型的、复杂的项目形成了巨大障碍。
在 ES6 之前社区制定了一些模块加载方案最主要的有 CommonJS 和 AMD 两种。前者用于服务器后者用于浏览器。ES6 在语言标准的层面上实现了模块功能而且实现得相当简单完全可以取代 CommonJS 和 AMD 规范成为浏览器和服务器通用的模块解决方案。
ES6 模块的设计思想是尽量的静态化使得编译时就能确定模块的依赖关系以及输入和输出的变量。CommonJS 和 AMD 模块都只能在运行时确定这些东西。比如CommonJS 模块就是对象输入时必须查找对象属性。
export命令
模块功能主要由两个命令构成export和import。export命令用于规定模块的对外接口import命令用于输入其他模块提供的功能。
一个模块就是一个独立的文件。该文件内部的所有变量外部无法获取。如果你希望外部能够读取模块内部的某个变量就必须使用export关键字输出该变量
//module/index.js
export const name zhangsan ;
export const age 18;
export const color red ;
export const sayName function() {console.log(fristName);
}//也可以这样
const name zhangsan ;
const age 18;
const color red ;
const sayName function() {console.log(fristName);
}
export {name,age,color,sayName}import命令
使用export命令定义了模块的对外接口以后其他 JS 文件就可以通过import命令加载这个模块。
//main.js
import {name,age,color,sayName,fn} from ./modules/index.js;如果想为输入的变量重新取一个名字import命令要使用as关键字将输入的变量重命名
import * as obj from ./modules/index.js;
console.log(obj);export default 命令
使用export default命令为模块指定默认输出
//export-default.js
export default function(){console.log(foo);
}//或者写成
function foo() {console.log(foo);
}export default foo;在其它模块加载该模块时import命令可以为该匿名函数指定任意名字
//import-default.js
import customName from ./export-default.js
customNmae();//foo如果想在一条import语句中同事输入默认方法和其他接口可以写成下面这样
import customName,{add} from export-default.js对应上面export语句如下
//export-default.js
export default function(){console.log(foo);
}export function add(){console.log(add)
}export default也可以用来输出类。
// MyClass.js
export default class Person{ ... }// main.js
import Person from MyClass;
let o new Person();
nJS 和 AMD 规范成为浏览器和服务器通用的模块解决方案。ES6 模块的设计思想是尽量的静态化使得编译时就能确定模块的依赖关系以及输入和输出的变量。CommonJS 和 AMD 模块都只能在运行时确定这些东西。比如CommonJS 模块就是对象输入时必须查找对象属性。#### export命令模块功能主要由两个命令构成export和import。export命令用于规定模块的对外接口import命令用于输入其他模块提供的功能。一个模块就是一个独立的文件。该文件内部的所有变量外部无法获取。如果你希望外部能够读取模块内部的某个变量就必须使用export关键字输出该变量javascript
//module/index.js
export const name zhangsan ;
export const age 18;
export const color red ;
export const sayName function() {console.log(fristName);
}//也可以这样
const name zhangsan ;
const age 18;
const color red ;
const sayName function() {console.log(fristName);
}
export {name,age,color,sayName}import命令
使用export命令定义了模块的对外接口以后其他 JS 文件就可以通过import命令加载这个模块。
//main.js
import {name,age,color,sayName,fn} from ./modules/index.js;如果想为输入的变量重新取一个名字import命令要使用as关键字将输入的变量重命名
import * as obj from ./modules/index.js;
console.log(obj);export default 命令
使用export default命令为模块指定默认输出
//export-default.js
export default function(){console.log(foo);
}//或者写成
function foo() {console.log(foo);
}export default foo;在其它模块加载该模块时import命令可以为该匿名函数指定任意名字
//import-default.js
import customName from ./export-default.js
customNmae();//foo如果想在一条import语句中同事输入默认方法和其他接口可以写成下面这样
import customName,{add} from export-default.js对应上面export语句如下
//export-default.js
export default function(){console.log(foo);
}export function add(){console.log(add)
}export default也可以用来输出类。
// MyClass.js
export default class Person{ ... }// main.js
import Person from MyClass;
let o new Person();