★★代码都是从上到下依次执行,写在前面的先执行,别跳看!!一步一步的来!!
一、promise对象的使用
Promise处理的是一个异步问题
通常情况:回调函数是异步编程解决方案的一种
只要是回调函数,都可以改写成promise写法!
10年积累的成都网站设计、成都网站建设、外贸网站建设经验,可以快速应对客户对网站的新想法和需求。提供各种问题对应的解决方案。让选择我们的客户得到更好、更有力的网络服务。我虽然不认识你,你也不认识我。但先做网站后付款的网站建设流程,更有莲花免费网站建设让你可以放心的选择与我们合作。
function send( url,callback ){
//var xhr =...
//...
callback(); //回调函数这么使用
}
send( 'url1',()=>{ //参数是一个回调函数
//do something
} )
Promise是一个构造函数,使用的时候需要new一下,Promise需要接收一个参数,这个参数是一个函数,并且该函数需要传入2个参数resolve、reject,分别表示异步操作执行成功后的回调函数和异步操作执行失败后的回调函数。
1.1、我们用Promise的时候一般是包在一个函数中,在需要的时候去运行这个函数,因为new promise的时候,传入其中的函数会自动执行,所以我们需要控制其中的函数在需要的时候才去执行
1.2、对于Promise的then()方法,then总是会返回一个Promise实例,因此你可以一直调用then,then接收一个函数,函数接收2个参数resolve、reject
var p = new Promise( ()=>{
console.log('执行了'); //定义Promise对象的时候,一定要传入一个函数,否则报错,而且这个函数在创建Promise对象的时候聚立即执行了。 此时Promise对象的状态是pending
} )
//传入参数resolve,reject,执行 resolve();就把Promise变成成功的状态了;执行 reject();就把Promise变成失败的状态了
var p = new Promise( (resolve,reject)=>{
//这里面可以执行别的自定义方法,不一定只能是resolve或者reject,在自定义方法里面,设置什么时候执行resolve,什么时候执行reject,下面使用catch捕捉reject
console.log(1); //执行到这里,下面马上把promise变为成功状态
resolve(); //把Promise变成成功的状态
} )
var p = new Promise( (resolve,reject)=>{ //内部有三种状态,pending、resolve、reject
return resolve("成功"); //进入成功执行的代码 一般会加个return,阻止后续代码的执行
reject(); //进入执行失败的代码 resolve与reject二者只会执行前一个,后者不会执行,但是后续的代码还会继续执行;resolve(data)有参数,then用data接收,reject(err)也有参数,catch用err接收
console.log(); //会继续执行到这里
} )
p.then( (data)=>{ //这里的data接收上面resolve传入的"成功"字符串
console.log("then:"+data);
} )
1.3、then方法是一个实例方法,建立在返回p实例的基础上
then( ()=>{ //then接收一个函数,函数接收2个参数,前者是上面进入成功状态执行,后者是上面进入失败状态执行
console.log('成功') //上面的resolve()时进入这里
},()=>{ //上面的reject()时进入这里
console.log('失败')
} )
tips、但是一般不使用上面的写法
p.then( (data)=>{ //then默认返回成功状态
console.log(data); //成功
} ) //成功状态时进入
p.catch( ()=>{
console.log("失败");
} ) //失败状态时进入
var p = new Promise( (resolve,reject)=>{
resolve("成功");
} )
var p2 = p.then( ()=>{} ); //执行完返回的promise是成功的状态,p2.then走成功resolve()方法
//这里没有返回值,就相当于return undefined,返回undefined
var p2 = p.then( ()=>{
return "非promise对象" //执行完返回的promise也是成功的状态
} )
后续链式调用then方法,返回的是一个Promise对象,这个返回的Promise对象的状态是成功还是失败,取决于then方法内部的代码,如果内部代码返回的是非Promise对象,无论是有值还是undefined,都是resolve(),否则,如果内部返回的是Promise对象,那么如果该返回的Promise对象是reject,那么then返回的是reject,否则就是resolve
1.4、catch方法
catch相当于then的第二个参数
var p = new Promise( (res,rej)=>{
res('成功');
} )
p.then( ()=>{
console.log( 1 )
} ).then( ()=>{
console.log( 2 )
} ).catch( (err)=>{ //捕捉失败
console.log(err)
} ).finally( ()=>{ //匿名函数里面没有参数了,不管执行了成功还是失败,这里都会执行到
console.log('执行完毕');
} )
console.log('这儿的代码仍然能执行');
//执行了catch方法,捕捉错误,不会影响后续代码的执行
=====等价于======
var p = new Promise( (res,rej)=>{
res('成功');
} )
let res = p.then( ()=>{ //执行完then,永远都返回promise对象,成功还是失败取决于返回的类型,成功继续走then,失败走catch
console.log( 1 )
} ).then( ()=>{
console.log( 2 )
} )
res.catch( (err)=>{ //捕捉失败
console.log(err)
} ).finally( ()=>{ //匿名函数里面没有参数了,不管执行了成功还是失败,这里都会执行到
console.log('执行完毕');
} )
console.log('这儿的代码仍然能执行');
//执行了catch方法,捕捉错误,不会影响后续代码的执行
p.then(data)里面的data接收的是上面resolve()里面传入的值
1.5、Promise静态方法
一个成功
p.then( ()=>{ console.log(1)} )
.then( ()=>{ return new Promise( (res,rej)=>{return res(400)} ) } )
.then( ()=>{ return Promise.resolve(400) } ) //两种写法一样
.then( (data)=>{console.log(data)} ) //400
一个失败
p.then( ()=>{ console.log(1)} )
.then( ()=>{ return new Promise( (res,rej)=>{return rej(400)} ) } )
.then( ()=>{ return Promise.reject('失败!') } ) //两种写法一样
.catch( (err)=>{console.log(err)} ) //失败!
//all方法:合并所有的promise对象,返回所有promise全部执行完之后的结果
let p1 = new Promise(()=>{ setTimeout(()=>{ res('a') },500) })
let p2 = new Promise(()=>{ setTimeout(()=>{ res('b') },500) })
let p3 = new Promise(()=>{ setTimeout(()=>{ res('c') },500) })
var p = Promise.all( [p1,p2,p3] );
p.then( (data)=>{ console.log(data) } ) //获取所有的promise完成之后的数据,保存为一个数组 ['a','b','c']
//race方法:返回所有promise中最先执行完的那个的结果
var p = Promise.race( [p1,p2,p3] );
p.then( (data)=>{ console.log(data) } ) //获取所有的promise中最先完成的那个数据
案例:
let p = new Promise((resolve,reject)=>{
resolve('成功');
})
p.then((data)=>{
console.log(data); //成功
})
console.log(p); //Promise {: "成功"}
new Promise((resolve,reject)=>{
resolve('成功啊啊啊');
})
.then((data)=>{
console.log(data); //成功啊啊啊
})
let box = document.getElementById("box");
function movePromise(obj,attr,target,duration){
return new Promise((res,rej)=>{
let b = parseInt(getComputedStyle(obj)[attr]); //obj没有引号,attr是字符串类型
let c = target - b ;
let d = duration;
let temp = new Date().getTime();
let timer = setInterval(function(){
let t = new Date().getTime() - temp;
if(t >= d){
clearInterval(timer);
t = d;
}
let v = c/d*t + b;
obj.style[attr] = v + 'px';
if(t === d){
res();
}
},20)
})
}
movePromise(box,"width",200,500)
.then( (res,rej)=>{
movePromise(box,"height",300,500)
} )
//这里面不是直接promise后接着执行then,而是promise外面的函数来执行then方法,所以里面的peomise需要加上return了,让外面的函数执行完毕之后返回一个promise对象
虽然promise会默认返回一个promise,但是需要return出来,才能让外面的函数接着执行then方法
二、定义变量:var、let声明变量,const声明常量
1、var声明的变量支持预解析(变量提升),let不支持
var a = 1;
console.log(a); //1
var c;
console.log(c); //undefined,先声明未赋值,值为undefined,没有保错
console.log(d); //undefined,预解析(变量提升),值为undefined,没有保错
var d;(var d = 2;)都是输出undefined
let e;
console.log(e); //undefined,先声明未赋值,值为undefined,没有保错
console.log(f); //Uncaught ReferenceError: f is not defined,报错
let f;
console.log(g); //Uncaught ReferenceError: g is not defined,报错
let g = 1;
let b = 2;
console.log(b); //2,正确输出
2、var可以重复声明,后面的值会覆盖前面的,let不能重复声明,会报错
var x = 1;
var x = 2; //2
let m = 1;
let m = 100; //Uncaught SyntaxError: Identifier 'm' has already been declared,报错
3、let支持块级作用域,块级作用域内的变量外部访问不到,但是var可以,(内部二者都可以访问到)
{
var a = 1;
console.log(a); //1
}
console.log(a); //1
{
let a = 1;
console.log(a); //1
}
console.log(a); //Uncaught ReferenceError: a is not defined
4、const声明常量
const声明一个常量之后,不可修改,
和let一样,不可重复声明,不支持预解析,支持块级作用域
let和var可以只声明,不赋值;const不可以,声明的同时必须进行赋值
三、函数声明
1、JS中常见的两种函数声明(statement)方式有这两种:
// 函数表达式(function expression)
h(); //报错
var h = function() {
// h
}
h(); //正确
m(); //正确
// 函数声明(function declaration)
function m() {
//m
}
m(); //正确
2、总结
四、解构赋值
1、数组的解构赋值
和对象差不多,严格按照数组下标来取值
let arr = [1,2,3];
let [a,b,c] = arr
console.log(a,b,c); // 1 2 3
let [a,,c] = arr;
console.log(a,c); // 1 3
let [a,,c,d=4] = arr; //支持赋默认值
console.log(a,c,d); //1 3 4
let [a,...rest] = arr;
console.log(a,rest); //1 (2) [2, 3]
2、字符串的解构赋值
3、对象的解构赋值
let obj = {
name:'leo',
age:20,
b:1,
move:function(){
console.log(1)
}
}
let {name,age} = obj;//声明的同时解构赋值,{}里的变量名必须和obj里面的变量名一致
console.log(name,age); //leo 20
★对象的结构赋值,可以只取对象中的某一个属性,不是必须一次性全部取出来
let {name} = obj;
console.log(name); //20
//可以解构赋值取别名
let { name : str } = obj;
console.log(str) //leo
console.log(name) //无值
//可以赋默认值,obj里面没有该属性则取赋的默认值,有属性则取obj里面的值
let { name = 'xx' } = obj;
console.log(name) //leo
let { c = 'xx' } = obj;
console.log(c) //xx
//...取剩余的元素,归到一个数组里
let {b,...r} = obj;
console.log(b,r); //1 {name: "leo", age: 20, move: ƒ}
五、扩展运算符...与rest运算符...
1、扩展运算符
1.1、用三个点号表示,功能是把数组或类数组对象展开成一系列用逗号隔开的值
var foo = function(a, b, c) {
console.log(a);
console.log(b);
console.log(c);
}
var arr = [1, 2, 3];
//传统写法
foo(arr[0], arr[1], arr[2]);
//使用扩展运算符
foo(...arr); //这里面的arr就可以看作,获取到了arr里面所有的值了
//1
//2
//3
1.2、特殊应用场景:
//数组深拷贝
var arr2 = arr;
var arr3 = [...arr];
console.log(arr===arr2); //true, 说明arr和arr2指向同一个数组
console.log(arr===arr3); //false, 说明arr3和arr指向不同数组
//把一个数组插入另一个数组字面量
var arr4 = [...arr, 4, 5, 6];
console.log(arr4);//[1, 2, 3, 4, 5, 6]
//字符串转数组
var str = 'love';
var arr5 = [...str];
console.log(arr5);//[ 'l', 'o', 'v', 'e' ]
2、rest运算符
2.1、rest运算符也是三个点号,不过其功能与扩展运算符恰好相反,把逗号隔开的值序列组合成一个数组
//主要用于不定参数,所以ES6开始可以不再使用arguments对象
var bar = function(...args) {
for (let el of args) {
console.log(el);
}
}
bar(1, 2, 3, 4);
//1
//2
//3
//4
bar = function(a, ...args) {
console.log(a);
console.log(args);
}
bar(1, 2, 3, 4);
//1
//[ 2, 3, 4 ]
2.2、rest运算符配合解构使用:
var [a, ...rest] = [1, 2, 3, 4];
console.log(a);//1
console.log(rest);//[2, 3, 4]
小结:
等号表达式是典型的赋值形式,函数传参和for循环的变量都是特殊形式的赋值。解构的原理是赋值的两边具有相同的结构,就可以正确取出数组或对象里面的元素或属性值,省略了使用下标逐个赋值的麻烦。
一点经验:
let arr1 = [1,2,3];
let arr2 = arr1.map((value,key,arr) => {
console.log(value) // 1,2,3
console.log(key) // 0,1,2
console.log(arr) //[1,2,3] [1,2,3] [1,2,3]
return value * value;
})
console.log(arr1); // [ 1, 2, 3 ]
console.log(arr2); // [ 1, 4, 9 ] //返回一个新的数组,如果在forEach里面使用return,则没有这个效果
filter函数可以看成是一个过滤函数,返回符合条件的元素的数组
filter需要在循环的时候判断一下是true还是false,是true才会返回这个元素;
let arr1 = [1,2,3];
let arr2 = arr1.filter((value,key,arr) => {
console.log(value) // 1,2,3
console.log(key) // 0,1,2
console.log(arr) // [1,2,3]
return value >= 3 ? false : true;
})
console.log(arr1); // [ 1, 2, 3 ]
console.log(arr2); // [ 1, 2 ]
some()方法
Array.some(function( item ){})
七、Object.assign
Object.assign是ES6新添加的接口,主要的用途是用来合并多个JavaScript的对象。
Object.assign()接口可以接收多个参数,第一个参数是目标对象,后面的都是源对象,assign方法将多个原对象的属性和方法都合并到了目标对象上面,如果在这个过程中出现同名的属性(方法),后合并的属性(方法)会覆盖之前的同名属性(方法)。
assign的基本用法如下:
var target = {a : 1}; //目标对象
var source1 = {b : 2}; //源对象1
var source2 = {c : 3}; //源对象2
var source3 = {c : 4}; //源对象3,和source2中的对象有同名属性c
var obj = Object.assign(target,source1,source2,source3);
//结果如下:
//{a:1,b:2,c:4}
1、Object.assign进行的拷贝是浅拷贝。也就是说,如果拷贝过来的属性的值是对象等复合属性,那么只能拷贝过来一个引用。
const obj1 = {a: {b: 1}};
const obj2 = Object.assign({}, obj1);
obj1.a.b = 2;
obj2.a.b // 2
由于是浅拷贝,拷贝的是引用,obj1的值改变,obj2的属性值也会随之改变
2、Object.assign进行合并的时候,一旦碰到同名属性,就会出现覆盖现象。所以使用时务必小心。
3、Object.assign是针对Object开发的API,一旦在源对象的参数未知接收到了其他类型的参数,会尝试类型转换。如果是数组类型的话,类型转换的结果是将每个数组成员的值作为属性键值,将数组成员在数组中的位置作为属性键名。多个数组组成参数一同传入的话还会造成覆盖。具体例子如下:
Object.assign([1, 2, 3], [4, 5])
// [4, 5, 3]
上面代码中,assign把数组视为属性名为 0、1、2 的对象,因此源数组的 0 号属性4覆盖了目标数组的 0 号属性1。
4、Object.assign只能将属性值进行复制,如果属性值是一个get(取值函数)的话,那么会先求值,然后再复制。
// 源对象
const source = {
//属性是取值函数
get foo(){return 1}
};
//目标对象
const target = {};
Object.assign(target,source);
//{foo ; 1} 此时foo的值是get函数的求值结果