JavaScript初级进阶:手写常用方法提升编码能力
2025.09.19 12:47浏览量:0简介:本文通过手写实现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)); // 6
console.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和数组越界
- 考虑可读性:在优化代码性能时,不要过度牺牲可维护性
六、总结与学习建议
手写实现常用方法不仅能加深对语言特性的理解,更能培养以下能力:
- 问题分解能力:将复杂功能拆解为可实现的小步骤
- 边界条件意识:考虑各种输入情况和异常处理
- 性能优化思维:在正确性和效率间找到平衡点
- 测试驱动意识:实现后应设计测试用例验证正确性
建议初级开发者:
- 从简单方法开始,逐步增加复杂度
- 对比原生实现,理解设计取舍
- 编写单元测试验证实现正确性
- 阅读优秀开源代码学习实现技巧
通过持续练习手写实现,开发者能建立更扎实的编程基础,为后续学习框架和高级特性打下坚实基础。
发表评论
登录后可评论,请前往 登录 或 注册