注:例子建立在非严格模式下,新版chrome浏览器下
严格模式下:window为undefined
node下:window为global
for(var i = 0 ; i < 3 ; i++){
setTimeout(()=>{
console.log(i);
},0)
}
输出
3
3
3
为什么?
因为for循环是同步任务顺序执行,var定义是全局变量,setTimeout为宏任务,会放到下一轮任务队列里面,所以等到setTimeout输出的时候,i的值就就是已经经过for循环变成3了。
所以如何修改使其输出0,1,2
1.闭包
将通过闭包将变量i变成局部变量
for(var i = 0 ; i < 3 ; i++) {
(function(i){
setTimeout(()=>{
console.log(i);
},0)
})(i);
}
2.let
for(let i = 0 ; i < 3 ; i++) {
setTimeout(()=>{
console.log(i);
},0)
}
这里的i只在块级作用域下生效
//块级作用域
{
setTimeout(()=>{
console.log(i);
},0)
}
const和let一样只在块级作用域下生效
但是不允许修改
var有变量提升的作用,let和const没有
console.log(a); //undefined
var a = '1';
console.log(b); //报错
let b = '2';
console.log(c); //报错
const c = '3';
变量提升等价
console.log(a);
var a = '1';
//等价于
var a;
console.log(a);
a = '1';
1.箭头函数不能被用作构造函数,普通函数可以
2.箭头函数没有arguments,普通函数有
3.箭头函数都是匿名函数,普通函数可以是匿名函数,也可以不是匿名函数
4.普通函数和箭头函数的this指向有区别
具体看我之前写的《一文彻底搞懂普通函数和箭头函数this指向区别》
class Test {
constructor(name) {
this.name = name;
}
static getFormatName() {
return `${this.name} - xixixi`
}
getName(){
return `${this.name} - xixi`
}
}
其中,静态方法的this指向Test类,普通方法的this指向实例化对象
const year = '2021';
const month = '10';
const day = '01';
let template = '${year}-${month}-${day}';
console.log(template); //2021-10-01
1.数组解构
每一个解构属性与数组索引对齐
// 基础类型数组解构
let [a, b, c] = [1, 2, 3]
console.log(a, b, c) // 1, 2, 3
// 对象数组解构
let [a, b, c] = [{name: '1'}, {name: '2'}, {name: '3'}]
console.log(a, b, c) // {name: '1'}, {name: '2'}, {name: '3'}
// ...解构
let [head, ...tail] = [1, 2, 3, 4]
console.log(head, tail) // 1, [2, 3, 4]
2.对象解构
每一个解构属性与对象属性名称一致
// 对象属性解构
let { f1, f2 } = { f1: 'test1', f2: 'test2' }
console.log(f1, f2) // test1, test2
// 可以不按照顺序,这是数组解构和对象解构的区别之⼀
let { f2, f1 } = { f1: 'test1', f2: 'test2' }
console.log(f1, f2) // test1, test2
// 解构对象重命名
let { f1: rename, f2 } = { f1: 'test1', f2: 'test2' }
console.log(rename, f2) // test1, test2
// 嵌套解构
let { f1: {f11}} = { f1: { f11: 'test11', f12: 'test12' } }
console.log(f11) // test11
3.解构的原理是什么?
针对可迭代对象的Iterator接⼝,通过遍历器按顺序获取对应的值进⾏赋值.
3.1 那么 Iterator 是什么?
Iterator是一种接口,它能为各种不一样的数据提供统一的访问机制。换句话说,任何数据有Iterator
接口了,都可以通过遍历器进行遍历操作。就比如ES6中的for of就是遍历器,它就可以遍历包含
Iterator接口的数据。
3.2 Iterator有什么⽤?
为各种数据解构提供统⼀的访问接⼝
使得数据解构能按次序排列处理
可以使⽤ES6最新命令 for of进⾏遍历
function generateIterator(array) {
let nextIndex = 0
return {
next: () => nextIndex < array.length ? {
value: array[nextIndex++],
done: false
} : {
value: undefined,
done: true
}
};
}
const iterator = generateIterator([0, 1, 2])
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
3.3 可迭代对象是什么?
可迭代对象是Iterator接⼝的实现。这是ECMAScript 2015的补充,它不是内置或语法,⽽仅
仅是协议。任何遵循该协议点对象都能成为可迭代对象。可迭代对象得有两个协议:可迭代协
议和迭代器协议。
可迭代协议:对象必须实现iterator⽅法。即对象或其原型链上必须有⼀个名叫
Symbol.iterator的属性。该属性的值为⽆参函数,函数返回迭代器协议。
迭代器协议:定义了标准的⽅式来产⽣⼀个有限或⽆限序列值。其要求必须实现⼀个next()⽅
法,该⽅法返回对象有done(boolean)和value属性。
3.4 手写一个可迭代对象
const obj = {
count:0,
[Symbol.iterator]: function() {
return {
next: () =>{
this.count++;
if(this.count >= 10){
return {
done:true,
value:undefined
}
}else{
return {
done:false,
value:this.count
}
}
}
}
}
}
for(let item of obj){
console.log(item);
}
//或者
const obj = {
0: 'a',
1: 'b',
2: 'c',
length:3,
[Symbol.iterator]:Array.prototype[Symbol.iterator]
}
for(let item of obj){
console.log(item);
}
1.for in
一般用于遍历对象属性
不建议遍历数组,因为for in会把原型链上的东西也遍历出来
没有break
2.for of
仅遍历当前对象
遍历可迭代对象,包括Array,Map,Set,String,arguments,NodeList等
可以与break,continue,return配合
1.Object.keys
该方法返回一个给定对象的自身可枚举属性组成的数组
const obj = { a: 1, b: 2 };
const keys = Object.keys(obj); // [a, b]
手写实现一个函数模拟Object.keys?
function getObjectKeys(obj) {
let result = [];
for(const prop in obj){
if(obj.has)
}
}
let obj = { a: 1, b: 2 };
obj.__proto__.c = 3;
console.log(getObjectKeys(obj));
2.Object.values
该⽅法返回⼀个给定对象⾃身的所有可枚举属性值的数组
const obj = { a: 1, b: 2 };
const keys = Object.values(obj); // [1, 2]
3.Object.entries
该⽅法返回⼀个给定对象⾃身可枚举属性的键值对数组。
const obj = { a: 1, b: 2 };
const keys = Object.entries(obj); // [['a',1], ['b',2]]
4.Object.create()
以方法第一个参数作为原形对象,创建一个新对象
当传入第二个参数时
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)
console.log(b.sex)
5.Object.assign()
浅拷贝,类似
function shallowClone(source) {
const target = {};
for (const i in source) {
if (source.hasOwnProperty(i)) {
target[i] = source[i];
}
}
return target;
}
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, 2, 3, 4, [5, 6]]
const arr3 = [1, 2, [3, 4, [5, 6]]];
arr3.flat(2);
// [1, 2, 3, 4, 5, 6]
//使⽤ Infinity,可展开任意深度的嵌套数组
const arr4 = [1, 2, [3, 4, [5, 6, [7, 8, [9, 10]]]]];
arr4.flat(Infinity);
// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
第一个参数为深度,当传Infinity时,无论深度是多少都直接打平
手写一个Array.flat
function arrayFlat(array,depth){
if(depth > 0){
array = array.reduce((res,item)=>{
if(!Array.isArray(item)){
res.push(item)
}else{
res = res.concat(arrayFlat(item,depth-1));
}
return res;
},[]).slice()
}
return array;
}
2.Array.includes
includes() ⽅法⽤来判断⼀个数组是否包含⼀个指定的值,根据情况,如果包含则返回 true,
否则返回false。
const array1 = [1, 2, 3];
console.log(array1.includes(2));//true
其实它有两个参数, 只不过我们平时只使⽤⼀个.
需要查找的元素值。
从fromIndex 索引处开始查找 valueToFind。如果为负值,则按升序从 array.length +
fromIndex 的索引开始搜 (即使从末尾开始往前跳 fromIndex 的绝对值个索引,然后往后搜
寻)。默认为 0。
3.Array.find
find() ⽅法返回数组中满⾜提供的测试函数的第⼀个元素的值。否则返回 undefined。
callback
在数组每⼀项上执⾏的函数,接收 3 个参数:
当前遍历到的元素。
当前遍历到的索引。
数组本身
const test = [
{name: 'a', age: 11 },
{name: 'b', age: 20 },
{name: 'c', age: 50}
];
function callback(element,index,array) {
return element.name === 'a';
}
console.log(test.find(callback));
4.Array.from
4.1 Array.from() ⽅法从⼀个类似数组或可迭代对象创建⼀个新的,浅拷⻉的数组实例。
arrayLike
想要转换成数组的伪数组对象或可迭代对象。
mapFn 可选
如果指定了该参数,新数组中的每个元素会执⾏该回调函数。
4.2 Array.from() 可以通过以下⽅式来创建数组对象:
伪数组对象(拥有⼀个 length 属性和若⼲索引属性的任意对象)
可迭代对象(可以获取对象中的元素,如 Map和 Set 等)
console.log(Array.from('foo'));//['foo']
console.log(Array.from([1, 2, 3], x => x + x));//[2,3,4]
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'];
5.Array.of
Array.of() ⽅法创建⼀个具有可变数量参数的新数组实例,⽽不考虑参数的数量或类型。
Array.of(7); // [7]
Array.of(1, 2, 3); // [1, 2, 3]