JavaScript初级进阶:手写常用方法提升编码能力
2025.09.19 12:47浏览量:3简介:本文通过手写实现JavaScript常用方法,帮助初级开发者深入理解语言特性,掌握核心编码技巧,提升实战能力。
JavaScript初级进阶:手写常用方法提升编码能力
在JavaScript学习过程中,理解内置方法的实现原理比单纯记忆API更重要。本文将通过手写实现数组、字符串、对象等常用方法,帮助初级开发者深入理解语言特性,掌握核心编码技巧。
一、数组方法的手写实现
1.1 数组去重方法
原生Set和filter结合是常见去重方案,但理解其原理更重要:
// 方法1:双重循环去重(基础版)function unique1(arr) {const result = [];for (let i = 0; i < arr.length; i++) {if (result.indexOf(arr[i]) === -1) {result.push(arr[i]);}}return result;}// 方法2:对象属性去重(性能优化版)function unique2(arr) {const seen = {};return arr.filter(item => {return seen.hasOwnProperty(item) ? false : (seen[item] = true);});}
两种实现分别展示了O(n²)和O(n)的时间复杂度差异,第二种方法利用了对象属性访问的O(1)特性。
1.2 数组扁平化实现
处理嵌套数组是常见需求,递归实现最直观:
// 基础递归实现function flatten(arr) {let result = [];for (let item of arr) {if (Array.isArray(item)) {result = result.concat(flatten(item));} else {result.push(item);}}return result;}// 扩展实现:控制扁平化深度function flattenDepth(arr, depth = 1) {if (depth <= 0) return arr.slice();return arr.reduce((acc, val) => {if (Array.isArray(val)) {acc.push(...flattenDepth(val, depth - 1));} else {acc.push(val);}return acc;}, []);}
递归实现需要注意调用栈深度,对于深度嵌套数组,建议使用迭代方案或ES6的flat()方法。
二、字符串方法的深度解析
2.1 字符串反转实现
看似简单的操作包含多种实现方式:
// 方法1:数组转换法function reverseString1(str) {return str.split('').reverse().join('');}// 方法2:双指针法(空间复杂度O(1))function reverseString2(str) {const arr = str.split('');let left = 0;let right = arr.length - 1;while (left < right) {[arr[left], arr[right]] = [arr[right], arr[left]];left++;right--;}return arr.join('');}
第二种方法展示了原地修改的优化思路,对于超长字符串性能更优。
2.2 字符串截取与填充
实现substring和padStart等功能的替代方案:
// 自定义substring实现function customSubstring(str, start, end) {let result = '';end = end || str.length;for (let i = start; i < end && i < str.length; i++) {result += str[i];}return result;}// 自定义padStart实现function customPadStart(str, targetLength, padStr) {padStr = padStr || ' ';const padLength = targetLength - str.length;if (padLength <= 0) return str;let padding = '';while (padding.length < padLength) {padding += padStr;}return padding.slice(0, padLength) + str;}
这些实现揭示了字符串操作的底层逻辑,特别是边界条件的处理。
三、对象方法的原理剖析
3.1 浅拷贝与深拷贝
对象复制是高频操作,实现需考虑多种数据类型:
// 浅拷贝实现function shallowCopy(obj) {if (typeof obj !== 'object' || obj === null) {return obj;}const newObj = Array.isArray(obj) ? [] : {};for (let key in obj) {if (obj.hasOwnProperty(key)) {newObj[key] = obj[key];}}return newObj;}// 深拷贝实现(基础版)function deepCopy(obj, hash = new WeakMap()) {if (typeof obj !== 'object' || obj === null) {return obj;}// 处理循环引用if (hash.has(obj)) {return hash.get(obj);}const newObj = Array.isArray(obj) ? [] : {};hash.set(obj, newObj);for (let key in obj) {if (obj.hasOwnProperty(key)) {newObj[key] = deepCopy(obj[key], hash);}}// 处理Symbol属性const symbolKeys = Object.getOwnPropertySymbols(obj);for (let symKey of symbolKeys) {newObj[symKey] = deepCopy(obj[symKey], hash);}return newObj;}
深拷贝实现需要考虑循环引用、Symbol属性、函数等特殊情况,完整实现远比想象复杂。
3.2 对象合并实现
模拟Object.assign的功能:
function objectAssign(target, ...sources) {if (target === null || target === undefined) {throw new TypeError('Cannot convert undefined or null to object');}const output = Object(target);for (let source of sources) {if (source !== null && source !== undefined) {for (let key in source) {if (source.hasOwnProperty(key)) {output[key] = source[key];}}}}return output;}
这个实现揭示了Object.assign的几个关键特性:浅拷贝、可枚举属性、原始类型转换等。
四、函数方法的实现技巧
4.1 柯里化函数实现
函数式编程的重要概念:
// 基础柯里化实现function curry(fn) {return function curried(...args) {if (args.length >= fn.length) {return fn.apply(this, args);} else {return function(...args2) {return curried.apply(this, args.concat(args2));}}}}// 实际应用示例function sum(a, b, c) {return a + b + c;}const curriedSum = curry(sum);console.log(curriedSum(1)(2)(3)); // 6console.log(curriedSum(1, 2)(3)); // 6
柯里化实现了参数复用和延迟执行,是函数组合的基础。
4.2 防抖与节流实现
性能优化的重要手段:
// 防抖实现(最后一次执行)function debounce(fn, delay) {let timer = null;return function(...args) {clearTimeout(timer);timer = setTimeout(() => {fn.apply(this, args);}, delay);}}// 节流实现(固定间隔执行)function throttle(fn, delay) {let lastTime = 0;return function(...args) {const now = Date.now();if (now - lastTime >= delay) {fn.apply(this, args);lastTime = now;}}}
两种技术分别适用于不同场景:防抖适合输入验证,节流适合滚动事件。
五、进阶实现与最佳实践
5.1 Promise的简易实现
理解异步编程的核心:
class MyPromise {constructor(executor) {this.state = 'pending';this.value = undefined;this.reason = undefined;this.onFulfilledCallbacks = [];this.onRejectedCallbacks = [];const resolve = (value) => {if (this.state === 'pending') {this.state = 'fulfilled';this.value = value;this.onFulfilledCallbacks.forEach(fn => fn());}};const reject = (reason) => {if (this.state === 'pending') {this.state = 'rejected';this.reason = reason;this.onRejectedCallbacks.forEach(fn => fn());}};try {executor(resolve, reject);} catch (err) {reject(err);}}then(onFulfilled, onRejected) {// 实际实现需要处理thenable对象、链式调用等复杂情况// 此处为简化版if (this.state === 'fulfilled') {onFulfilled(this.value);} else if (this.state === 'rejected') {onRejected(this.reason);} else {this.onFulfilledCallbacks.push(() => onFulfilled(this.value));this.onRejectedCallbacks.push(() => onRejected(this.reason));}}}
完整实现需要考虑状态机、异步执行、链式调用等复杂机制。
5.2 性能优化建议
- 避免不必要的递归:对于大数据量操作,优先选择迭代方案
- 合理选择数据结构:如去重时根据数据特征选择Set或对象
- 注意边界条件:特别是处理null/undefined和数组越界
- 考虑可读性:在优化代码性能时,不要过度牺牲可维护性
六、总结与学习建议
手写实现常用方法不仅能加深对语言特性的理解,更能培养以下能力:
- 问题分解能力:将复杂功能拆解为可实现的小步骤
- 边界条件意识:考虑各种输入情况和异常处理
- 性能优化思维:在正确性和效率间找到平衡点
- 测试驱动意识:实现后应设计测试用例验证正确性
建议初级开发者:
- 从简单方法开始,逐步增加复杂度
- 对比原生实现,理解设计取舍
- 编写单元测试验证实现正确性
- 阅读优秀开源代码学习实现技巧
通过持续练习手写实现,开发者能建立更扎实的编程基础,为后续学习框架和高级特性打下坚实基础。

发表评论
登录后可评论,请前往 登录 或 注册