当前位置: 首页> 健康> 科研 > 前端面试宝典【Javascript篇】【2】

前端面试宝典【Javascript篇】【2】

时间:2025/7/13 22:12:57来源:https://blog.csdn.net/BDawn/article/details/140661324 浏览次数:0次

在这里插入图片描述

欢迎来到《前端面试宝典》,这里是你通往互联网大厂的专属通道,专为渴望在前端领域大放异彩的你量身定制。通过本专栏的学习,无论是一线大厂还是初创企业的面试,都能自信满满地展现你的实力。

核心特色:

  • 独家实战案例:每一期专栏都将深入剖析真实的前端面试案例,从基础知识到前沿技术,从算法挑战到框架运用,让你在实战中迅速成长。
  • 深度技术解析:不再只是表面文章,我们将带你深入技术的核心,理解每一个知识点背后的原理与- - 应用场景,让你在面试中不仅知其然,更知其所以然。
  • 高质量内容保证:每一期内容都经过精心策划与打磨,确保信息的准确性和实用性,拒绝泛泛而谈,只提供真正有价值的内容。
  • 持续更新迭代:持续关注前端领域的最新动态,及时更新专栏内容,确保你始终站在技术的最前沿。

目录

      • 4. 深拷贝和浅拷贝是两种不同的对象复制策略,主要用于复制包含复杂数据结构的对象,如对象和数组。它们之间的主要区别在于如何处理对象中的引用类型属性。
        • 浅拷贝(Shallow Copy)
        • 深拷贝(Deep Copy)
        • 实现深拷贝
        • WeakMap 和 WeakSet
        • 函数
      • 5. 柯里化函数实现
        • 柯里化函数升级版
        • 柯里化函数应用场景
      • 6. 如何让 (a == 1 && a == 2 && a == 3) 的值为true

4. 深拷贝和浅拷贝是两种不同的对象复制策略,主要用于复制包含复杂数据结构的对象,如对象和数组。它们之间的主要区别在于如何处理对象中的引用类型属性。

浅拷贝(Shallow Copy)

浅拷贝只会复制对象的第一层属性,对于对象内部的引用类型(如数组、对象、函数等),它只会复制这些类型的引用,而不是复制这些类型的内容。这意味着,如果原始对象和拷贝对象中的引用类型被修改,它们都会反映相同的数据变化,因为它们指向的是同一个内存地址。

深拷贝(Deep Copy)

深拷贝会递归地复制对象的所有层级,包括对象内部的引用类型。这意味着拷贝后的对象和原始对象完全独立,修改其中一个对象不会影响另一个对象。

实现深拷贝

在JavaScript中,可以使用递归的方式来实现深拷贝。下面是一个使用JSON方法的简单深拷贝实现,适用于基本数据类型和可序列化的对象类型:

function deepCopy(obj) {return JSON.parse(JSON.stringify(obj));
}

但是,这种方法有一些限制:

  • 它不能处理循环引用的对象。

  • 它不能正确复制函数和正则表达式。

  • 它不能复制对象的原型链上的属性。

为了克服这些限制,可以使用递归函数来实现一个更完整的深拷贝:

function deepCopy(obj, hash = new WeakMap()) {if (obj instanceof Map) return deepCopyMap(obj);if (obj instanceof Set) return deepCopySet(obj);if (obj instanceof RegExp) return new RegExp(obj);if (obj instanceof Date) return new Date(obj);if (obj === null || typeof obj !== 'object') return obj;if (hash.has(obj)) return hash.get(obj);let copy = Array.isArray(obj) ? [] : {};hash.set(obj, copy);for (let key in obj) {if (obj.hasOwnProperty(key)) {copy[key] = deepCopy(obj[key], hash);}}return copy;
}function deepCopyMap(map) {const copy = new Map();for (let [key, value] of map.entries()) {copy.set(deepCopy(key), deepCopy(value));}return copy;
}function deepCopySet(set) {const copy = new Set();for (let value of set) {copy.add(deepCopy(value));}return copy;
}
WeakMap 和 WeakSet

WeakMap和WeakSet是弱引用集合,它们的键必须是对象,而且这些键在垃圾回收时会被自动清除。由于它们的弱引用性质,你无法遍历它们的所有键和值,因此通常认为它们是不可拷贝的。在实际应用中,如果你需要保存WeakMap或WeakSet的状态,可能需要考虑重新构建它们,而不是深拷贝。

函数

函数是不可深拷贝的,因为函数是代码的一部分,而不是数据。当你试图“拷贝”一个函数时,实际上你得到的是对原函数的引用。如果需要在不同上下文中使用相似的功能,应该考虑将函数定义为模块或库的一部分,而不是尝试拷贝它们。

这个函数首先检查传入的对象是否是正则表达式或日期对象,如果是,则创建相应的实例。接着,它检查对象是否是基本类型或null,如果是,则直接返回。对于引用类型,它使用WeakMap来存储已经处理过的对象,以防止无限递归。最后,它递归地复制对象的所有属性。

5. 柯里化函数实现

柯里化(Currying)是函数式编程中的一个重要概念,它允许我们将多参数函数转换为一系列单参数函数的链式调用。下面是一个通用的柯里化函数实现的例子,使用JavaScript编写:

function curry(fn) {const arity = fn.length; // 获取函数期望的参数数量function _curryInner(args) {// 如果提供的参数数量小于函数期望的参数数量,// 并且下一个参数不是一个函数(意味着用户不打算提前执行函数),// 则返回一个新的函数。if (args.length < arity) {return function(...moreArgs) {return _curryInner(args.concat(moreArgs));};}// 如果提供了足够的参数,或者用户传递了一个函数作为最后一个参数,// 则调用原始函数。return fn.apply(null, args);}return function(...initialArgs) {return _curryInner(initialArgs);};
}

使用这个curry函数,你可以将任何多参数函数转换为柯里化形式。例如,假设我们有一个求和函数:

function sum(a, b, c) {return a + b + c;
}

我们可以将其柯里化:

const curriedSum = curry(sum);

然后,我们可以按需逐步传递参数:

const result1 = curriedSum(1)(2)(3); // 结果是 6
const result2 = curriedSum(1)(2, 3); // 结果同样是 6
const result3 = curriedSum(1, 2, 3); // 结果同样是 6
柯里化函数升级版

比如要实现多个数值的相加,参数数量不定:

function curry(fn) {let args = [];// 无限制接收参数function _curryInner(...a) {args = args.concat(a);return _curryInner}// 终止函数,计算并返回结果_curryInner.endF = function(cb) {const res = fn(...args);cb(res);};return _curryInner
}
// 求和函数
function s(...args) {return args.reduce((accumulator, currentValue) => accumulator + currentValue, 0);
}
const curriedSum = curry(s);
// 可以传入任意数量的参数
const result1 = curriedSum(1)(2,67,5,4)(3)(2,1,2,3)(3).endF(res => {console.log(res);
})
柯里化函数应用场景
  1. 参数预填充(Partial Application): 柯里化可以用来预先设定函数的一些参数,从而创建特定功能的版本。例如,如果你有一个计算税费的函数,你可以预先设定税率,创建一个特定税率的计算税费的函数。
  2. 配置函数: 当你需要创建一组具有特定配置的函数时,柯里化可以派上用场。例如,你可以创建一个配置了特定数据库连接的查询函数。
  3. 事件处理: 在事件驱动的编程中,柯里化可以用来创建特定事件的处理器。例如,你可以创建一个处理点击事件的函数,其中按钮ID已经被预设。
  4. 数据处理管道: 柯里化函数可以链接在一起形成数据处理管道,每个函数负责数据流的一个特定阶段。这对于数据清洗、转换和分析特别有用。
  5. API设计: 在设计API时,柯里化可以提供更优雅的接口。例如,一个搜索API可以先设定搜索条件,然后再执行搜索,而不是一次提供所有参数。
  6. 库和框架: 许多库和框架使用柯里化来提供更灵活的API。例如,Lodash库中的许多函数都可以被柯里化。
  7. 函数组合: 柯里化函数可以更容易地与其他函数组合,形成复杂的逻辑流程。这是函数式编程中常见的模式。
  8. 减少重复代码: 通过柯里化,你可以避免在多个地方重复相同的函数调用模式,从而提高代码的可维护性。
  9. 延迟执行: 柯里化可以用来延迟函数的执行,直到所有必要的参数都被提供。这对于异步编程和需要按顺序处理参数的情况特别有用。
  10. 优化性能: 在某些情况下,柯里化可以帮助优化性能,尤其是当函数的参数集相对固定时,可以缓存中间结果以避免重复计算。

6. 如何让 (a == 1 && a == 2 && a == 3) 的值为true

a定义为一个对象,重写对象的toString方法,定义一个计数器,调用一次toString,+1,这样就可以实现这个需求

let counter = 0;
let a = {toString: () => {if (counter === 0) {counter++;return '1';} else if (counter === 1) {counter++;return '2';} else if (counter === 2) {counter++;return '3';} else {// 防止无限循环,当counter超过预期值时返回一个确定的值return '1';}}
}console.log((a == 1 && a == 2 && a == 3))
关键字:前端面试宝典【Javascript篇】【2】

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

责任编辑: