1、for in 、 for of区别
1.1 for in
遍历数组时,key为数组下标字符串;遍历对象,key为对象字段名。
缺点:
- for in 不仅会遍历当前对象,还包括原型链上的可枚举属性(一般使用hasOwnProperty来判断)
- for in 不适合遍历数组,主要应用为对象
1.2 for of
可迭代对象(包括 Array,Map,Set,String,TypedArray,arguments对象,NodeList对象)上创建一个迭代循环,调用自定义迭代钩子,并为每个不同属性的值执行语句。
let arr = [{age: 1}, {age: 5}, {age: 100}, {age: 34}]
for(let { age } of arr) {
if (age > 10) {
break // for of 允许中断
}
console.log(age)
}
优点:
- for of 仅遍历当前对象
- 可以被中断
2、object
2.1 Object.keys
该方法返回一个给定对象的自身可枚举属性组成的数组。
const obj = { a: 1, b: 2 };
const keys = Object.keys(obj); // [a, b]
手写实现一个函数模拟Object.keys
function getObjectKeys(obj) {
const result = [];
for (const prop in obj) {
if (obj.hasOwnProperty(prop)) {
result.push(prop);
}
}
return result;
}
console.log(getObjectKeys({
a: 1,
b: 2
}))
2.2 Object.values
该方法返回一个给定对象自身的所有可枚举属性值的数组。
const obj = { a: 1, b: 2 };
const keys = Object.keys(obj); // [1, 2]
手写实现一个函数模拟Object.values
function getObjectValues(obj) {
const result = [];
for (const prop in obj) {
if (obj.hasOwnProperty(prop)) {
result.push(obj[prop]);
}
}
return result;
}
console.log(getObjectValues({
a: 1,
b: 2
}))
2.3 Object.entries
该方法返回一个给定对象自身可枚举属性的键值对数组。
const obj = { a: 1, b: 2 };
const keys = Object.entries(obj); // [ [ 'a', 1 ], [ 'b', 2 ] ]
手写实现
function getObjectEntries(obj) {
const result = [];
for (const prop in obj) {
if (obj.hasOwnProperty(prop)) {
result.push([prop, obj[prop]]);
}
}
return result;
}
console.log(getObjectEntries({
a: 1,
b: 2
}))
2.4 Object.getOwnPropertyNames
该方法返回一个数组,该数组对元素是 obj自身拥有的枚举或不可枚举属性名称字符串。
Object.prototype.aa = '1111';
const testData = {
a: 1,
b: 2
}
for (const key in testData) {
console.log(key);
}
// 获取自身,不获取原型链
console.log(Object.getOwnPropertyNames(testData)) // ['a','b']
2.5 Object.getOwnPropertyDescriptor
什么是descriptor? 对象对应的属性描述符, 是一个对象. 包含以下属性:
- configurable。 如果为false,则任何尝试删除目标属性或修改属性特性(writable, configurable, enumerable)的行为将被无效化。所以通常属性都有特性时,可以把configurable设置为true即可。
- writable 是否可写。设置成 false,则任何对该属性改写的操作都无效(但不会报错,严格模式下会报错),默认false。
- enumerable。是否能在for-in循环中遍历出来或在Object.keys中列举出来。 ```javascript const object1 = {}; Object.defineProperty(object1, ‘p1’, { value: ‘111’, writable: false });
object1.p1 = ‘not’;
console.log(object1.p1); // 严格模式报错,非严格模式111
<br />proxy 数据劫持:
```javascript
const obj = new Proxy({}, {
get: function (target, propKey, receiver) {
console.log(`getting ${propKey}`); // getting something
return target[propKey];
},
set: function (target, propKey, value, receiver) {
console.log(`setting ${propKey}`); // setting something
return Reflect.set(target, propKey, value, receiver);
}
});
obj.something = 1;
console.log(obj.something); // 1
2.6 Reflect(就是对js不规范语言优化)
- 将Object对象的一些明显属于语言内部的方法(比如Object.defineProperty),放到Reflect对象上。现阶段,某些方法同时在Object和Reflect对象上部署,未来的新方法将只部署在Reflect对象上。也就是说,从Reflect对象上可以拿到语言内部的方法
- 让Object操作都变成函数行为。某些Object操作是命令式,比如name in obj和delete obj[name],而Reflect.has(obj, name)和Reflect.deleteProperty(obj, name)让它们变成了函数行为。
- Reflect对象的方法与Proxy对象的方法一一对应,只要是Proxy对象的方法,就能在Reflect对象上找到对应的方法。这就让Proxy对象可以方便地调用对应的Reflect方法,完成默认行为,作为修改行为的基础。也就是说,不管Proxy怎么修改默认行为,你总可以在Reflect上获取默认行为。
但是要注意, 通过defineProperty设置writable为false的对象, 就不能用Proxy了
const target = Object.defineProperties({}, {
foo: {
value: 123,
writable: false,
configurable: false
},
});
const proxy = new Proxy(target, {
get(target, propKey) {
return 'abc';
}
});
proxy.foo
2.7 Object.assign
浅拷贝, 类似于 { …a, …b };
function shallowClone(source) {
const target = {};
for (const i in source) {
if (source.hasOwnProperty(i)) {
target[i] = source[i];
}
}
return target;
}
2.8 Object.is
Object.is() 方法判断两个值是否为同一个值,同一个引用。
const a = {
name: 1
};
const b = a;
console.log(Object.is(a, b)) // true
console.log(Object.is({}, {})) // false
2.9 Object.create
- Object.create()方法创建一个新的对象,并以方法的第一个参数作为新对象的proto属性的值(根据已有的对象作为原型,创建新的对象。)
- Object.create()方法还有第二个可选参数,是一个对象,对象的每个属性都会作为新对象的自身属性,对象的属性值以descriptor(Object.getOwnPropertyDescriptor(obj, ‘key’))的形式出现,且enumerable默认为false ```javascript function Person(name, sex) { this.name = name; this.sex = sex; }
const b = Object.create(Person.prototype, { name: { value: ‘coco’, writable: true, configurable: true, enumerable: true, }, sex: { enumerable: true, get: function () { return ‘hello sex’ }, set: function (val) { console.log(‘set value:’ + val) } } })
console.log(b.name) // coco console.log(b.sex) // hello sex
Object.create(null)创建一个对象,但这个对象的原型链为null,即Fn.prototype = null;
```javascript
const b = Object.create(null) // 返回纯{}对象,无prototype
b // {}
b.__proto__ // undefined
b.toString() // throw error
所以当你要创建一个非常干净的对象, 没有任何原型链上的属性, 那么就使用Object.create(null). for in 遍历的时候也不需要考虑原型链属性了。
3、Promise
3.1 promise.all
function PromiseAll(promiseArray) {
return new Promise(function (resolve, reject) {
//判断参数类型
if (!Array.isArray(promiseArray)) {
return reject(new TypeError('arguments muse be an array'))
}
let counter = 0;
let promiseNum = promiseArray.length;
let resolvedArray = [];
for (let i = 0; i < promiseNum; i++) {
// 3. 这里为什么要用Promise.resolve?
Promise.resolve(promiseArray[i]).then((value) => {
counter++;
resolvedArray[i] = value; // 2. 这里直接Push, 而不是用索引赋值, 有问题吗
if (counter == promiseNum) { // 1. 这里如果不计算counter++, 直接判断resolvedArr.length === promiseNum, 会有问题吗?
// 4. 如果不在.then里面, 而在外层判断, 可以吗?
resolve(resolvedArray)
}
}).catch(e => reject(e));
}
})
}
// 测试
const pro1 = new Promise((res, rej) => {
setTimeout(() => {
res('1')
}, 1000)
})
const pro2 = new Promise((res, rej) => {
setTimeout(() => {
res('2')
}, 2000)
})
const pro3 = new Promise((res, rej) => {
setTimeout(() => {
res('3')
}, 3000)
})
const proAll = PromiseAll([pro1, pro2, pro3])
.then(res =>
console.log(res) // 3秒之后打印 ["1", "2", "3"]
)
.catch((e) => {
console.log(e)
})
3.2 Promise.allSeettled
需要返回所有promise的状态和结果;
function PromiseAllSettled(promiseArray) {
return new Promise(function (resolve, reject) {
//判断参数类型
if (!Array.isArray(promiseArray)) {
return reject(new TypeError('arguments muse be an array'))
}
let counter = 0;
const promiseNum = promiseArray.length;
const resolvedArray = [];
for (let i = 0; i < promiseNum; i++) {
Promise.resolve(promiseArray[i])
.then((value) => {
resolvedArray[i] = {
status: 'fulfilled',
value
};
})
.catch(reason => {
resolvedArray[i] = {
status: 'rejected',
reason
};
})
.finally(() => {
counter++;
if (counter == promiseNum) {
resolve(resolvedArray)
}
})
}
})
}
4、Array
4.1 Array.flat
flat() 方法会按照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回;
const arr1 = [1, 2, [3, 4]];
arr1.flat(); // [1,2,3,4]
const arr2 = [1, 2, [3, 4, [5, 6]]];
arr2.flat(1); // [1,2,3,4,[5,6]]
模拟实现:
// 使用 reduce、concat 和递归展开无限多层嵌套的数组
const arr1 = [1, 2, 3, [1, 2, 3, 4, [2, 3, 4]]];
function flatDeep(arr, d = 1) {
if (d > 0) {
return arr.reduce((res, val) => {
if (Array.isArray(val)) {
res = res.concat(flatDeep(val, d - 1))
} else {
res = res.concat(val);
}
return res;
}, [])
} else {
return arr.slice()
}
};
console.log(flatDeep(arr1, Infinity))
// [1, 2, 3, 1, 2, 3, 4, 2, 3, 4]
不考虑深度:
function flatten(arr) {
let res = [];
let length = arr.length;
for (let i = 0; i < length; i++) {
if (Object.prototype.toString.call(arr[i]) === '[object Array]') {
res = res.concat(flatten(arr[i]))
} else {
res.push(arr[i])
}
}
return res
}
// 如果数组元素都是Number类型
function flatten(arr) {
return arr.toString().split(',').map(item => +item)
}
function flatten(arr){
while(arr.some(item=>Array.isArray(item))){
arr = [].concat(...arr);
}
return arr;
}
4.2 Array.includes
includes() 方法用来判断一个数组是否包含一个指定的值,根据情况,如果包含则返回 true,否则返回false。
- valueToFind,需要查找的元素值。
- fromIndex 可选,从fromIndex 索引处开始查找 valueToFind。如果为负值,则按升序从 array.length + fromIndex 的索引开始搜 (即使从末尾开始往前跳 fromIndex 的绝对值个索引,然后往后搜寻)。默认为 0。
4.3 Array.from
Array.from() 方法从一个类似数组或可迭代对象创建一个新的,浅拷贝的数组实例。
- arrayLike,想要转换成数组的伪数组对象或可迭代对象。
- mapFn 可选,如果指定了该参数,新数组中的每个元素会执行该回调函数。
Array.from() 可以通过以下方式来创建数组对象:
- 伪数组对象(拥有一个 length 属性和若干索引属性的任意对象)
- 可迭代对象(可以获取对象中的元素,如 Map和 Set 等) ```javascript console.log(Array.from(‘foo’));
console.log(Array.from([1, 2, 3], x => x + x));
const set = new Set([‘foo’, ‘bar’, ‘baz’, ‘foo’]); Array.from(set); // [ “foo”, “bar”, “baz” ]
const map = new Map([[1, 2], [2, 4], [4, 8]]); Array.from(map); // [[1, 2], [2, 4], [4, 8]]
const mapper = new Map([[‘1’, ‘a’], [‘2’, ‘b’]]); Array.from(mapper.values()); // [‘a’, ‘b’];
Array.from(mapper.keys()); // [‘1’, ‘2’];
<a name="QRVE9"></a>
## 4.4 Array.of
Array.of() 方法创建一个具有可变数量参数的新数组实例,而不考虑参数的数量或类型。
```javascript
Array.of(7); // [7]
Array.of(1, 2, 3); // [1, 2, 3]
模拟实现:
Array.of = function() {
return Array.prototype.slice.call(arguments);
};