logo

JavaScript初级进阶:手写常用方法提升编码能力

作者:搬砖的石头2025.09.19 12:47浏览量:0

简介:本文通过手写实现JavaScript常用方法,帮助初级开发者深入理解语言特性,掌握核心编码技巧,提升实战能力。

JavaScript初级进阶:手写常用方法提升编码能力

在JavaScript学习过程中,理解内置方法的实现原理比单纯记忆API更重要。本文将通过手写实现数组、字符串、对象等常用方法,帮助初级开发者深入理解语言特性,掌握核心编码技巧。

一、数组方法的手写实现

1.1 数组去重方法

原生Setfilter结合是常见去重方案,但理解其原理更重要:

  1. // 方法1:双重循环去重(基础版)
  2. function unique1(arr) {
  3. const result = [];
  4. for (let i = 0; i < arr.length; i++) {
  5. if (result.indexOf(arr[i]) === -1) {
  6. result.push(arr[i]);
  7. }
  8. }
  9. return result;
  10. }
  11. // 方法2:对象属性去重(性能优化版)
  12. function unique2(arr) {
  13. const seen = {};
  14. return arr.filter(item => {
  15. return seen.hasOwnProperty(item) ? false : (seen[item] = true);
  16. });
  17. }

两种实现分别展示了O(n²)和O(n)的时间复杂度差异,第二种方法利用了对象属性访问的O(1)特性。

1.2 数组扁平化实现

处理嵌套数组是常见需求,递归实现最直观:

  1. // 基础递归实现
  2. function flatten(arr) {
  3. let result = [];
  4. for (let item of arr) {
  5. if (Array.isArray(item)) {
  6. result = result.concat(flatten(item));
  7. } else {
  8. result.push(item);
  9. }
  10. }
  11. return result;
  12. }
  13. // 扩展实现:控制扁平化深度
  14. function flattenDepth(arr, depth = 1) {
  15. if (depth <= 0) return arr.slice();
  16. return arr.reduce((acc, val) => {
  17. if (Array.isArray(val)) {
  18. acc.push(...flattenDepth(val, depth - 1));
  19. } else {
  20. acc.push(val);
  21. }
  22. return acc;
  23. }, []);
  24. }

递归实现需要注意调用栈深度,对于深度嵌套数组,建议使用迭代方案或ES6的flat()方法。

二、字符串方法的深度解析

2.1 字符串反转实现

看似简单的操作包含多种实现方式:

  1. // 方法1:数组转换法
  2. function reverseString1(str) {
  3. return str.split('').reverse().join('');
  4. }
  5. // 方法2:双指针法(空间复杂度O(1))
  6. function reverseString2(str) {
  7. const arr = str.split('');
  8. let left = 0;
  9. let right = arr.length - 1;
  10. while (left < right) {
  11. [arr[left], arr[right]] = [arr[right], arr[left]];
  12. left++;
  13. right--;
  14. }
  15. return arr.join('');
  16. }

第二种方法展示了原地修改的优化思路,对于超长字符串性能更优。

2.2 字符串截取与填充

实现substringpadStart等功能的替代方案:

  1. // 自定义substring实现
  2. function customSubstring(str, start, end) {
  3. let result = '';
  4. end = end || str.length;
  5. for (let i = start; i < end && i < str.length; i++) {
  6. result += str[i];
  7. }
  8. return result;
  9. }
  10. // 自定义padStart实现
  11. function customPadStart(str, targetLength, padStr) {
  12. padStr = padStr || ' ';
  13. const padLength = targetLength - str.length;
  14. if (padLength <= 0) return str;
  15. let padding = '';
  16. while (padding.length < padLength) {
  17. padding += padStr;
  18. }
  19. return padding.slice(0, padLength) + str;
  20. }

这些实现揭示了字符串操作的底层逻辑,特别是边界条件的处理。

三、对象方法的原理剖析

3.1 浅拷贝与深拷贝

对象复制是高频操作,实现需考虑多种数据类型:

  1. // 浅拷贝实现
  2. function shallowCopy(obj) {
  3. if (typeof obj !== 'object' || obj === null) {
  4. return obj;
  5. }
  6. const newObj = Array.isArray(obj) ? [] : {};
  7. for (let key in obj) {
  8. if (obj.hasOwnProperty(key)) {
  9. newObj[key] = obj[key];
  10. }
  11. }
  12. return newObj;
  13. }
  14. // 深拷贝实现(基础版)
  15. function deepCopy(obj, hash = new WeakMap()) {
  16. if (typeof obj !== 'object' || obj === null) {
  17. return obj;
  18. }
  19. // 处理循环引用
  20. if (hash.has(obj)) {
  21. return hash.get(obj);
  22. }
  23. const newObj = Array.isArray(obj) ? [] : {};
  24. hash.set(obj, newObj);
  25. for (let key in obj) {
  26. if (obj.hasOwnProperty(key)) {
  27. newObj[key] = deepCopy(obj[key], hash);
  28. }
  29. }
  30. // 处理Symbol属性
  31. const symbolKeys = Object.getOwnPropertySymbols(obj);
  32. for (let symKey of symbolKeys) {
  33. newObj[symKey] = deepCopy(obj[symKey], hash);
  34. }
  35. return newObj;
  36. }

深拷贝实现需要考虑循环引用、Symbol属性、函数等特殊情况,完整实现远比想象复杂。

3.2 对象合并实现

模拟Object.assign的功能:

  1. function objectAssign(target, ...sources) {
  2. if (target === null || target === undefined) {
  3. throw new TypeError('Cannot convert undefined or null to object');
  4. }
  5. const output = Object(target);
  6. for (let source of sources) {
  7. if (source !== null && source !== undefined) {
  8. for (let key in source) {
  9. if (source.hasOwnProperty(key)) {
  10. output[key] = source[key];
  11. }
  12. }
  13. }
  14. }
  15. return output;
  16. }

这个实现揭示了Object.assign的几个关键特性:浅拷贝、可枚举属性、原始类型转换等。

四、函数方法的实现技巧

4.1 柯里化函数实现

函数式编程的重要概念:

  1. // 基础柯里化实现
  2. function curry(fn) {
  3. return function curried(...args) {
  4. if (args.length >= fn.length) {
  5. return fn.apply(this, args);
  6. } else {
  7. return function(...args2) {
  8. return curried.apply(this, args.concat(args2));
  9. }
  10. }
  11. }
  12. }
  13. // 实际应用示例
  14. function sum(a, b, c) {
  15. return a + b + c;
  16. }
  17. const curriedSum = curry(sum);
  18. console.log(curriedSum(1)(2)(3)); // 6
  19. console.log(curriedSum(1, 2)(3)); // 6

柯里化实现了参数复用和延迟执行,是函数组合的基础。

4.2 防抖与节流实现

性能优化的重要手段:

  1. // 防抖实现(最后一次执行)
  2. function debounce(fn, delay) {
  3. let timer = null;
  4. return function(...args) {
  5. clearTimeout(timer);
  6. timer = setTimeout(() => {
  7. fn.apply(this, args);
  8. }, delay);
  9. }
  10. }
  11. // 节流实现(固定间隔执行)
  12. function throttle(fn, delay) {
  13. let lastTime = 0;
  14. return function(...args) {
  15. const now = Date.now();
  16. if (now - lastTime >= delay) {
  17. fn.apply(this, args);
  18. lastTime = now;
  19. }
  20. }
  21. }

两种技术分别适用于不同场景:防抖适合输入验证,节流适合滚动事件。

五、进阶实现与最佳实践

5.1 Promise的简易实现

理解异步编程的核心:

  1. class MyPromise {
  2. constructor(executor) {
  3. this.state = 'pending';
  4. this.value = undefined;
  5. this.reason = undefined;
  6. this.onFulfilledCallbacks = [];
  7. this.onRejectedCallbacks = [];
  8. const resolve = (value) => {
  9. if (this.state === 'pending') {
  10. this.state = 'fulfilled';
  11. this.value = value;
  12. this.onFulfilledCallbacks.forEach(fn => fn());
  13. }
  14. };
  15. const reject = (reason) => {
  16. if (this.state === 'pending') {
  17. this.state = 'rejected';
  18. this.reason = reason;
  19. this.onRejectedCallbacks.forEach(fn => fn());
  20. }
  21. };
  22. try {
  23. executor(resolve, reject);
  24. } catch (err) {
  25. reject(err);
  26. }
  27. }
  28. then(onFulfilled, onRejected) {
  29. // 实际实现需要处理thenable对象、链式调用等复杂情况
  30. // 此处为简化版
  31. if (this.state === 'fulfilled') {
  32. onFulfilled(this.value);
  33. } else if (this.state === 'rejected') {
  34. onRejected(this.reason);
  35. } else {
  36. this.onFulfilledCallbacks.push(() => onFulfilled(this.value));
  37. this.onRejectedCallbacks.push(() => onRejected(this.reason));
  38. }
  39. }
  40. }

完整实现需要考虑状态机、异步执行、链式调用等复杂机制。

5.2 性能优化建议

  1. 避免不必要的递归:对于大数据量操作,优先选择迭代方案
  2. 合理选择数据结构:如去重时根据数据特征选择Set或对象
  3. 注意边界条件:特别是处理null/undefined和数组越界
  4. 考虑可读性:在优化代码性能时,不要过度牺牲可维护性

六、总结与学习建议

手写实现常用方法不仅能加深对语言特性的理解,更能培养以下能力:

  1. 问题分解能力:将复杂功能拆解为可实现的小步骤
  2. 边界条件意识:考虑各种输入情况和异常处理
  3. 性能优化思维:在正确性和效率间找到平衡点
  4. 测试驱动意识:实现后应设计测试用例验证正确性

建议初级开发者:

  • 从简单方法开始,逐步增加复杂度
  • 对比原生实现,理解设计取舍
  • 编写单元测试验证实现正确性
  • 阅读优秀开源代码学习实现技巧

通过持续练习手写实现,开发者能建立更扎实的编程基础,为后续学习框架和高级特性打下坚实基础。

相关文章推荐

发表评论