3、ES8
1. async函数和await表达式
acync:
- async函数的返回值为promise对象
- promise对象的结果由async函数执行的返回值决定
async function fn() {
// 1.return的不是promise对象,则返回的结果就是成功的promise对象
return '123'; // [[PromiseState]]: "fulfilled" [[PromiseResult]]: "123"
return // [[PromiseState]]: "fulfilled" [[PromiseResult]]: undefined
// 2.抛出错误,返回的结果是一个失败的promise对象
throw '出错'; // [[PromiseState]]: "rejected" [[PromiseResult]]: "出错"
// 3.return的是一个promise对象,则返回的结果由return的那个pomise决定
return new Promise((resolve, reject) => {
resolve('成功'); // [[PromiseState]]: "fulfilled" [[PromiseResult]]: "成功"
reject('失败'); // [[PromiseState]]: "rejected" [[PromiseResult]]: "失败"
});
}
const result = fn();
console.log(result);
await表达式:
- await必须写在async函数中
- await右侧的表达式一般为promise对象
- await返回的是promise成功的值
- await的promise失败了就会抛出异常,需要通过try…catch捕获处理
const p = new Promise((resolve, reject) => {
resolve('成功');
reject('失败');
})
async function main() {
try {
// p成功的情况下执行
let result = await p;
console.log(result); // 成功
} catch(e) {
// p失败的情况下执行
console.log(e); // 失败
}
}
main();
async和await结合读取文件
const fs = require('fs');
function getOne() {
return new Promise((resolve, reject) => {
fs.readFile('文件地址1', (err, data) => {
if(err) reject(err);
resolve(data);
});
})
}
function getTwo() {
return new Promise((resolve, reject) => {
fs.readFile('文件地址2', (err, data) => {
if(err) reject(err);
resolve(data);
});
})
}
function getThree() {
return new Promise((resolve, reject) => {
fs.readFile('文件地址3', (err, data) => {
if(err) reject(err);
resolve(data);
});
})
}
async function main() {
let one = await getOne();
let two = await getTwo();
let three = await getThree();
console.log(one.toString());
console.log(two.toString());
console.log(three.toString());
}
main();
async和await结合封装AJAX请求
// 发送AJAX请求,返回的结果是promise对象
function sendAJAX(url) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open("GET", url);
xhr.send();
xhr.onreadystatechange = function() {
if(xhr.readyState === 4) {
if(xhr.status >= 200 && xhr.status < 300) {
resolve(xhr.response);
} else {
reject(xhr.status);
}
}
}
})
}
// promise.then 方法测试
sendAJAX("https://....").then(value => {
console.log(value);
}, reason => {
console.log(reason);
})
// async await测试
async function main() {
try {
let result = await sendAJAX("https://....");
console.log(result);
} catch(e) {
console.log(e);
}
}
2. Object.values 和 Object.entries
- Object.values() 方法返回一个给定对象的所有可枚举属性值的数组
- Object.entries() 方法返回一个给定对象自身可遍历属性(key, value)的数组
const person = {
name: 'zhangsan',
age: [11,16],
like: ['play', 'eat', 'speak']
}
Object.keys(person); // ["name", "age", "like"]
Object.values(person); // ["zhangsan", Array(2), Array(3)]
// 0: "zhangsan"
// 1: (2) [11, 16]
// 2: (3) ["play", "eat", "speak"]
Object.entries(person); // [Array(2), Array(2), Array(2)]
// 0: (2) ["name", "zhangsan"]
// 1: (2) ["age", Array(2)]
// 2: (2) ["like", Array(3)]
// 创建Map
console.log(new Map(Object.entries(person)));
// Map(3) {"name" => "zhangsan", "age" => Array(2), "like" => Array(3)}
// 0: {"name" => "zhangsan"}
// 1: {"age" => Array(2)}
// 2: {"like" => Array(3)}
Object.keys(obj)
参数:要返回其枚举自身属性的对象
返回值:一个表示给定对象的所有可枚举
属性的字符串数组,不包括属性名为Symbol值的属性 enumerable: false不可枚举
- 如果属性名的类型是
Number
,那么Object.keys
返回值是按照key
从小到大排序 - 如果属性名的类型是
String
,那么Object.keys
返回值是按照属性被创建的时间升序排序。 - 如果属性名的类型是
Symbol
,那么逻辑同String
相同 - 负数是作为字符串
String
处理的,也是按照定义时候的顺序
// 处理对象,返回可枚举的属性数组
let person = {name:"张三",age:25,address:"深圳",getName:function(){}}
Object.keys(person); // ["name", "age", "address","getName"]
// 处理数组,返回索引值数组
let arr = [1,2,3,4,5,6];
Object.keys(arr); // ["0", "1", "2", "3", "4", "5"]
// 处理字符串,返回索引值数组,因为String对象有可提取的属性
let str = "saasd字符串";
Object.keys(str); // ["0", "1", "2", "3", "4", "5", "6", "7"]
// 常用技巧
let person = {name:"张三",age:25,address:"深圳",getName:function(){}}
Object.keys(person).map((key)=>{
return person[key] // 获取到属性对应的值,做一些处理 ["张三", 25, "深圳", ƒ]
})
// 返回的对象没有任何可提取的属性,所以返回空数组
Object.keys(123); // []
var obj = {name: 'zyp', age: 18};
Object.defineProperty(obj, 'like', {
enumerable: false, // 不可枚举
value: 'reading'
})
Object.defineProperty(obj, Symbol(), {
value: 'symbol'
})
var properNameArr = Object.keys(obj); // properNameArr结果为["name", "age"]
当Object.keys
被调用时背后发生了什么
-
调用
ToObject(O)
将结果赋值给变量obj
-
调用
EnumerableOwnPropertyNames(obj, "key")
将结果赋值给变量nameList
-
调用
CreateArrayFromList(nameList)
得到最终的结果
1、将参数转换成Object(ToObject(O)
)
- Undefined | 抛出TypeError
- Null | 抛出TypeError
- Boolean | 返回一个新的 Boolean 对象
- Number | 返回一个新的 Number 对象
- String | 返回一个新的 String 对象
- Symbol | 返回一个新的 Symbol 对象
- Object | 直接将Object返回
2、获得属性列表(EnumerableOwnPropertyNames(obj, "key")
)
针对Object.keys
这个API来说,获取属性列表中最重要的是调用了内部方法OwnPropertyKeys
得到ownKeys
。其实也正是内部方法OwnPropertyKeys
决定了属性的顺序。
关于OwnPropertyKeys
方法ECMA-262中是这样描述的:
当O
的内部方法OwnPropertyKeys
被调用时,执行以下步骤(其实就一步):
Return ! OrdinaryOwnPropertyKeys(O).
而OrdinaryOwnPropertyKeys
是这样规定的:
- 声明变量
keys
值为一个空列表(List类型) - 把每个Number类型的属性,按数值大小升序排序,并依次添加到
keys
中 - 把每个String类型的属性,按创建时间升序排序,并依次添加到
keys
中 - 把每个Symbol类型的属性,按创建时间升序排序,并依次添加到
keys
中 - 将
keys
返回(return keys
)
上面这个规则不光规定了不同类型的返回顺序,还规定了如果对象的属性类型是数字,字符与Symbol混合的,那么返回顺序永远是数字在前,然后是字符串,最后是Symbol。
Object.keys({
5: '5',
a: 'a',
1: '1',
c: 'c',
3: '3',
b: 'b'
}) // ["1", "3", "5", "a", "c", "b"]
// 属性的顺序规则中虽然规定了Symbol的顺序,但其实Object.keys最终会将Symbol类型的属性过滤出去。(原因是顺序规则不只是给Object.keys一个API使用,它是一个通用的规则)
3、将List类型转换为Array得到最终结果(CreateArrayFromList( elements )
)
将List类型的属性列表转换成Array类型非常简单:
- 先声明一个变量
array
,值是一个空数组 - 循环属性列表,将每个元素添加到
array
中 - 将
array
返回
上面介绍的排序规则同样适用于下列API:
Object.entries
Object.values
for...in
循环Object.getOwnPropertyNames
Reflect.ownKeys
Object.values()
Object.values方法返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历( enumerable )属性的键值。
var obj = { 100: 'a', 2: 'b', 7: 'c' };
Object.values(obj); // ["b", "c", "a"]
// 上面代码中,属性名为数值的属性,是按照数值大小,从小到大遍历的,因此返回的顺序是b、c、a。
// Object.values只返回对象自身的可遍历属性。
var obj = Object.create({}, {p: {value: 42}});
Object.values(obj) // []
//上面代码中,Object.create方法的第二个参数添加的对象属性(属性p),如果不显式声明,默认是不可遍历的。Object.values不会返回这个属性。
// Object.values会过滤属性名为 Symbol 值的属性。
Object.values({ [Symbol()]: 123, foo: 'abc' }); // ['abc']
// 如果Object.values方法的参数是一个字符串,会返回各个字符组成的一个数组。
Object.values('foo'); // ['f', 'o', 'o']
// 上面代码中,字符串会先转成一个类似数组的对象。字符串的每个字符,就是该对象的一个属性。因此,Object.values返回每个属性的键值,就是各个字符组成的一个数组
// 如果参数不是对象,Object.values 会先将其转为对象。由于数值和布尔值的包装对象,都不会为实例添加非继承的属性。所以,Object.values 会返回空数组。
Object.values(42); // []
Object.values(true); // []
Object.entries()
Object.entries方法返回一个数组,成员是参数对象自身的(不含继承的)所有可遍历( enumerable )属性的键值对数组。
var obj = { foo: 'bar', baz: 42 };
Object.entries(obj); // [ ["foo", "bar"], ["baz", 42] ]
// 除了返回值不一样,该方法的行为与Object.values基本一致。
// 如果原对象的属性名是一个 Symbol 值,该属性会被省略。将来可能会有Reflect.ownEntries()方法,返回对象自身的所有属性。
Object.entries({ [Symbol()]: 123, foo: 'abc' }); // [ [ 'foo', 'abc' ] ]
Object.entries的基本用途是遍历对象的属性。
var obj = { foo: 'bar', baz: 42 };
for (let [k, v] of Object.entries(obj)) {
console.log(`${JSON.stringify(k)}: ${JSON.stringify(v)}`);
} // "foo": "bar" // "baz": 42
Object.entries方法的一个用处是,将对象转为真正的Map结构。
var obj = { foo: 'bar', baz: 42 };
var map = new Map(Object.entries(obj)); // Map(2) {"foo" => "bar", "baz" => 42}
map // Map { foo: "bar", baz: 42 }
自己实现Object.entries方法,非常简单。
function* entries(obj) {
for (let key of Object.keys(obj)) {
yield [key, obj[key]];
}
}
// 非 Generator 函数的版本
function entries(obj) {
let arr = [];
for (let key of Object.keys(obj)) {
arr.push([key, obj[key]]);
}
return arr;
}
3. Object.getOwnPropertyDescriptors
返回指定对象所有自身属性的描述对象
const person = {
name: 'zhangsan',
age: [11,16],
like: ['play', 'eat', 'speak']
}
Object.getOwnPropertyDescriptors(person);
// {name: {…}, age: {…}, like: {…}}
// age: {value: Array(2), writable: true, enumerable: true, configurable: true}
// like: {value: Array(3), writable: true, enumerable: true, configurable: true}
// name: {value: "zhangsan", writable: true, enumerable: true, configurable: true}
// __proto__: Object