前端八股文自救指南——JS——Day2

JS

for...in & for...of

for...infor...of都是JavaScript中的循环语句。for...in 循环主要是为了遍历对象而生,不适用于遍历数组;for...of 循环可以用来遍历数组、类数组对象,字符串、SetMap 以及 Generator 对象。

  • for...of 遍历获取的是对象的键值for...in 获取的是对象的键名

  • for... in 会遍历对象的整个原型链 ,性能非常差不推荐使用,而 for ... of 只遍历当前对象不会遍历原型链;

  • 对于数组的遍历,for...in 会返回数组中所有可枚举的属性(包括原型链上可枚举的属性),for...of 只返回数组的下标对应的属性值;

使用for...of遍历对象

使用 Object.keys()Object.values()Object.entries() 方法:

使用 Object.keys() 遍历对象的键
JS 复制代码
const person = {
    name: 'John',
    age: 30,
    city: 'New York'
};

// 使用 Object.keys() 获取对象的键数组
for (const key of Object.keys(person)) {
    console.log(key);
}
使用 Object.values() 遍历对象的值
JS 复制代码
const person = {
    name: 'John',
    age: 30,
    city: 'New York'
};

// 使用 Object.values() 获取对象的值数组
for (const value of Object.values(person)) {
    console.log(value);
}
使用 Object.entries() 遍历对象的键值对
JS 复制代码
const person = {
    name: 'John',
    age: 30,
    city: 'New York'
};

// 使用 Object.entries() 获取对象的键值对数组
for (const [key, value] of Object.entries(person)) {
    console.log(`${key}: ${value}`);
}

forEach & map

forEach没有返回值,是一个纯粹的操作方法,用于执行副作用;map有返回值,用于根据原数组通过处理生成新数组。

  • forEach不同于map,缺少返回值,不支持链式调用。
  • forEach可以通过传入特定的回调函数来实现修改原数组,但map不行。

尾调用

尾调用(Tail Call)是编程领域,特别是函数式编程中的一个重要概念,在 JavaScript 等支持该特性的语言中有着广泛应用。尾调用就是在函数的最后一步调用函数 ,在一个函数里调用另外一个函数会保留当前执行的上下文 ,如果在函数尾部调用,因为已经是函数最后一步,所以这时可以不用保留当前的执行上下文,从而节省内存 。但是ES6的尾调用只能在严格模式下开启,正常模式是无效的。

深/浅拷贝

浅拷贝
定义

浅拷贝创建一个新对象或数组,新对象或数组会复制原始对象或数组的一层属性。如果属性是基本数据类型(如 numberstringboolean 等),则会复制其值;但如果属性是引用数据类型(如对象、数组等),则只会复制引用,即新对象和原始对象会共享同一个引用数据类型的实例。

实现方式
  • Object.assign() :用于将一个或多个源对象的所有可枚举属性复制到目标对象。
JS 复制代码
const originalObj = {
    name: 'John',
    hobbies: ['reading', 'swimming']
};
const shallowCopyObj = Object.assign({}, originalObj);

console.log(shallowCopyObj); 
// 修改浅拷贝对象的基本类型属性
shallowCopyObj.name = 'Jane';
console.log(originalObj.name); 
// 修改浅拷贝对象的引用类型属性
shallowCopyObj.hobbies.push('running');
console.log(originalObj.hobbies); 
  • 扩展运算符 (...) :可以用于对象和数组的浅拷贝。
JS 复制代码
const originalArray = [1, [2, 3]];
const shallowCopyArray = [...originalArray];

console.log(shallowCopyArray); 
// 修改浅拷贝数组的引用类型元素
shallowCopyArray[1].push(4);
console.log(originalArray[1]); 
特点
  • 浅拷贝只复制对象的一层属性,对于嵌套的引用类型属性,新对象和原始对象会指向同一个内存地址。
  • 操作浅拷贝对象的基本类型属性不会影响原始对象,但操作其引用类型属性会影响原始对象。
深拷贝
定义

深拷贝会递归地复制对象的所有属性,包括嵌套的对象和数组。这意味着深拷贝会创建一个完全独立的新对象,新对象和原始对象在内存中是完全分离的,修改新对象不会影响原始对象,反之亦然。

实现方式
  • JSON.parse(JSON.stringify()) :这是一种简单的深拷贝方法,适用于不包含函数、正则表达式、Symbol 等特殊类型的对象。
JS 复制代码
const originalObj = {
    name: 'John',
    hobbies: ['reading', 'swimming']
};
const deepCopyObj = JSON.parse(JSON.stringify(originalObj));

console.log(deepCopyObj); 
// 修改深拷贝对象的引用类型属性
deepCopyObj.hobbies.push('running');
console.log(originalObj.hobbies); 
  • 手动递归实现:通过递归遍历对象的所有属性,复制每个属性的值,如果属性是引用类型,则继续递归复制。
JS 复制代码
function deepCopy(obj) {
    if (typeof obj!== 'object' || obj === null) {
        return obj;
    }
    let newObj = Array.isArray(obj)? [] : {};
    for (let key in obj) {
        if (obj.hasOwnProperty(key)) {  //判断是否是自身的属性,而非原型链上的
            newObj[key] = deepCopy(obj[key]);
        }
    }
    return newObj;
}

const originalArray = [1, [2, 3]];
const deepCopyArray = deepCopy(originalArray);

console.log(deepCopyArray); 
// 修改深拷贝数组的引用类型元素
deepCopyArray[1].push(4);
console.log(originalArray[1]); 
特点
  • 深拷贝会完全复制对象的所有属性,包括嵌套的引用类型,新对象和原始对象在内存中是相互独立的。
  • 操作深拷贝对象的任何属性都不会影响原始对象。

let、const、var

作用域
  • var :使用 var 声明的变量具有函数作用域。这意味着变量在整个函数内部都是可见的,无论它是在函数的哪个位置声明的。如果在函数外部声明,那么它具有全局作用域,在整个全局环境中都可以访问。

  • letconstletconst 具有块级作用域。块级作用域是指由一对花括号 {} 包裹的代码块,如 if 语句、for 循环、while 循环等。在块级作用域内声明的变量只能在该块内访问。

变量提升
  • var :使用 var 声明的变量会发生变量提升,即变量的声明会被提升到当前作用域的顶部,但赋值不会提升。在变量声明之前访问该变量,会得到 undefined
JS 复制代码
console.log(a); 
var a = 10;

上述代码相当于:

JS 复制代码
var a;
console.log(a); 
a = 10;

所以输出结果为 undefined

  • letconstletconst 也存在变量提升,但它们在声明之前处于 "暂时性死区"(Temporal Dead Zone,TDZ)。在暂时性死区内访问变量会抛出 ReferenceError 错误。
JS 复制代码
console.log(b); 
let b = 20;

这段代码会抛出 ReferenceError 错误,因为 b 处于暂时性死区,不能在声明之前访问。

可修改性
  • varlet :使用 varlet 声明的变量可以被重新赋值。

  • const :使用 const 声明的常量必须在声明时进行初始化,并且一旦初始化后,就不能再重新赋值。不过,如果 const 声明的是一个对象或数组,对象的属性或数组的元素是可以修改的。

重复声明
  • var :使用 var 可以在同一作用域内重复声明同一个变量,后面的声明会覆盖前面的声明。

  • letconst :在同一作用域内,使用 letconst 不能重复声明同一个变量,否则会抛出 SyntaxError 错误。

箭头函数

  • 箭头函数是匿名函数 ,不能作为构造函数,使用new关键字。

  • 箭头函数没有arguments

  • 箭头函数没有自己的this,会获取所在的上下文作为自己的this

  • call()applay()bind()方法不能改变箭头函数中的this指向

  • 箭头函数没有prototype

  • 箭头函数不能用作Generator函数,不能使用yeild关键字

Set & Map & weakMap & Object

Set

  • Set 是一种无序且唯一的数据集合,它类似于数组,但成员的值都是唯一的,没有重复的值。它只存储值,不存储键值对。
  • 创建: new Set([1, 1, 2, 3, 3, 4, 2])
  • add(value):添加某个值,返回Set结构本身。
  • delete(value):删除某个值,返回一个布尔值,表示删除是否成功。
  • has(value):返回一个布尔值,表示该值是否为Set的成员。
  • clear():清除所有成员,没有返回值。

Map

  • Map 是一种键值对的集合,其中键和值可以是任意类型的数据,并且键是唯一的。其中的键和值是一一对应的关系。
  • set(key, val):Map中添加新元素
  • get(key): 通过键值查找特定的数值并返回
  • has(key): 判断Map对象中是否有Key所对应的值,有返回true,否则返回false
  • delete(key): 通过键值从Map中移除对应的数据
  • clear(): 将这个Map中的所有元素删除

weakMap

WeakMap 是一种键值对的集合,其中键必须是对象类型 ,而值可以是任意类型。与普通的 Map 不同,WeakMap 的键是弱引用。这意味着当这些键在其他地方不再有强引用时,它们会被垃圾回收机制自动回收,而不会像普通 Map 那样阻止垃圾回收。

Object

Object 是无序的数据集合,由键值对(也称为属性)组成。键通常是字符串(在 ES6 中也可以是 Symbol 类型),值可以是任意数据类型,包括基本数据类型(如 NumberStringBoolean 等)和引用数据类型(如 ObjectArrayFunction 等)。它也是所有对象的基类。

Promise

在 JavaScript 里,异步操作(如网络请求、文件读取)不会立即返回结果。Promise 代表一个异步操作的最终完成或失败,并返回其结果。它有三种状态:

  • pending(进行中) :初始状态,既不是成功,也不是失败状态。

  • fulfilled(已成功) :意味着操作成功完成。

  • rejected(已失败) :意味着操作失败。

一旦 Promise 状态变为 fulfilledrejected,就不会再改变,这种特性被称为 "不可变性"。

基本语法

创建一个 Promise 对象时,需要传入一个执行器函数,该函数接收两个参数:resolvereject

JS 复制代码
const myPromise = new Promise((resolve, reject) => {
    // 模拟异步操作
    setTimeout(() => {
        const success = true;
        if (success) {
            resolve('操作成功');
        } else {
            reject('操作失败');
        }
    }, 1000);
});
  • resolve:一个函数,当异步操作成功时调用,将 Promise 的状态从 pending 变为 fulfilled,并传递操作结果。
  • reject:一个函数,当异步操作失败时调用,将 Promise 的状态从 pending 变为 rejected,并传递错误信息。
处理 Promise 结果

使用 thencatch 方法来处理 Promise 的结果。

JS 复制代码
myPromise
   .then((result) => {
        console.log(result); 
    })
   .catch((error) => {
        console.error(error); 
    });
  • then:用于处理 Promise 成功的情况,接收一个回调函数,该回调函数的参数是 resolve 传递的值。
  • catch:用于处理 Promise 失败的情况,接收一个回调函数,该回调函数的参数是 reject 传递的错误信息。
链式调用

Promise 支持链式调用,允许按顺序执行多个异步操作。

JS 复制代码
function asyncOperation1() {
    return new Promise((resolve) => {
        setTimeout(() => {
            resolve('操作 1 完成');
        }, 1000);
    });
}

function asyncOperation2() {
    return new Promise((resolve) => {
        setTimeout(() => {
            resolve('操作 2 完成');
        }, 1000);
    });
}

asyncOperation1()
   .then((result1) => {
        console.log(result1);
        return asyncOperation2();
    })
   .then((result2) => {
        console.log(result2);
    })
   .catch((error) => {
        console.error(error);
    });
其他方法
  • Promise.all :接收一个 Promise 数组,当所有 Promise 都成功时,返回一个新的 Promise,其结果是所有 Promise 结果组成的数组;如果有任何一个 Promise 失败,则整个 Promise 立即失败。
JS 复制代码
const promise1 = Promise.resolve(1);
const promise2 = Promise.resolve(2);
const promise3 = Promise.resolve(3);

Promise.all([promise1, promise2, promise3])
   .then((results) => {
        console.log(results); 
    })
   .catch((error) => {
        console.error(error);
    });
  • Promise.race :接收一个 Promise 数组,返回一个新的 Promise,只要数组中的任何一个 Promise 率先改变状态(成功或失败),就以该 Promise 的结果作为最终结果。
缺点
  • 无法取消 Promise,一旦新建它就会立即执行,无法中途取消。
  • 如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。
  • 当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。
相关推荐
几度泥的菜花19 分钟前
使用jQuery实现动态下划线效果的导航栏
前端·javascript·jquery
思茂信息32 分钟前
CST直角反射器 --- 距离多普勒(RD图), 毫米波汽车雷达ADAS
前端·人工智能·5g·汽车·无人机·软件工程
星星不打輰37 分钟前
Vue入门常见指令
前端·javascript·vue.js
好_快1 小时前
Lodash源码阅读-isNative
前端·javascript·源码阅读
好_快1 小时前
Lodash源码阅读-reIsNative
前端·javascript·源码阅读
好_快1 小时前
Lodash源码阅读-baseIsNative
前端·javascript·源码阅读
好_快1 小时前
Lodash源码阅读-toSource
前端·javascript·源码阅读
Oneforlove_twoforjob2 小时前
volta node npm yarn下载安装
前端·npm·node.js
咖啡の猫2 小时前
npm与包
前端·npm·node.js
徐福记c2 小时前
npm install -g @vue/cli 方式已经无法创建VUE3项目
前端·vue.js·npm