es6学习笔记
★★代码都是从上到下依次执行,写在前面的先执行,别跳看!!一步一步的来!!
一、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(); //正确
- 先说两者的显著区别:
- 第一种声明方式也就是var声明方式, 函数只有在var语句声明之后才能被调用
- 第二种生命方式也就是function声明方式, 函数可以在function声明之前被调用
- 这是因为,
- 对第一种情况,函数表达式是在函数运行阶段才赋值给变量h
- 对第二种情况,函数表达式是在代码运行阶段之前,也就是代码预解析阶段赋值给标识符m
2、总结
- 函数表达式: 即上面第一种方式, 这种方法使用function操作符创建函数, 表达式可以存储在变量或者对象属性里. 函数表达式往往被称为匿名函数, 因为它没有名字. 证明这一点你可以 console.log(h.name); 可以看到打印为空 ""
- 函数声明: 即上面第二种方式, 会声明一个具名函数, 且函数能在其所在作用域的任意位置被调用, 其创建的函数为具名函数, 证明这一点你可以 console.log(m.name); 可以看到打印为 "m". 可在后面的代码中将此函数通过函数名赋值给变量或者对象属性
四、解构赋值
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循环的变量都是特殊形式的赋值。解构的原理是赋值的两边具有相同的结构,就可以正确取出数组或对象里面的元素或属性值,省略了使用下标逐个赋值的麻烦。
- 对于三个点号,三点放在形参或者等号左边为rest运算符; 放在实参或者等号右边为扩展运算符,或者说,放在被赋值一方为rest运算符,放在赋值一方为扩展运算符。
一点经验:
- 在等号赋值或for循环中,如果需要从数组或对象中取值,尽量使用解构。
- 在自己定义函数的时候,如果调用者传来的是数组或对象,形参尽量使用解构方式,优先使用对象解构,其次是数组解构。代码可读性会很好。
- 在调用第三方函数的时候,如果该函数接受多个参数,并且你要传入的实参为数组,则使用扩展运算符。可以避免使用下标形式传入参数。也可以避免很多人习惯的使用apply方法传入数组。
- rest运算符使用场景应该稍少一些,主要是处理不定数量参数,可以避免arguments对象的使用。
六、map、forEach、filter、some的区别
map和forEach的遍历方法不同,在forEach中return语句是没有任何效果的,而map则可以改变当前循环的值,返回一个新的被改变过值之后的数组(map需return),一般用来处理需要修改某一个数组的值。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 ){}) - 用于检测数组中的元素是否满足指定条件(函数提供),会依次执行数组的每个元素
- 如果有一个元素满足条件,则表达式返回true , 剩余的元素不会再执行检测
- 如果没有满足条件的元素,则返回false
- 如果return返回值有一项为true,那么结果就是true
七、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}
- Object.assign拷贝的属性是有限制的,只会拷贝对象本身的属性(不会拷贝继承属性),也不会拷贝不可枚举的属性。但是属性名为Symbol值的属性,是可以被Object.assign拷贝的。
- 如果assign只接收到了一个对象作为参数的话,就是说没有源对象要合并到目标对象上,那会原样把目标对象返回。
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函数的求值结果
分享题目:es6学习笔记
网页路径:http://myzitong.com/article/poegic.html