前言
系列首发于公众号『前端进阶圈』 ,若不想错过更多精彩内容,请“星标”一下,敬请关注公众号最新消息。
面试必考: 手撕代码系列(一)
手写深拷贝 (deepClone)
/**
* deepClone 深拷贝
* @param {*} source 源对象(需要拷贝的对象)
* @returns 新对象
*/
const deepClone = source => {
// 存储结果
const obj = {};
// for...in 方法:遍历对象时 key 为对象的键;遍历数组时 key 为数组下标 index
for (const key in source) {
// Object.prototype.hasOwnProperty.call(); 方法是一个常用的,安全监测对象是否含有某个属性的方法,使用此方法可避免 hasOwnProperty 属性被污染或被重写的风险。
if (Object.prototype.hasOwnProperty.call(source, key)) {
// Object.prototype.toString.call(); 通过原型的方式判断一个值的类型
// 为什么使用 xxx.slice(8, -1)?
// 因为 Object.prototype.toString.call() 返回的值是 [object Object], 使用 slice 方法截取
if (Object.prototype.toString.call(source[key]).slice(8, -1) === 'Object') {
// 递归
obj[key] = deepClone(source[key]);
} else {
// 赋值
obj[key] = source[key];
}
}
}
return obj;
};
// test:
const a = {
age: 12,
otherInfo: {
sex: 0,
},
};
const b = deepClone(a);
b.age = 22;
b.otherInfo.sex = 1;
console.log('a------>', a); // a------> { age: 12, otherInfo: { sex: 0 } }
console.log('b------>', b); // b------> { age: 22, otherInfo: { sex: 1 } }
手写浅拷贝 (shallowClone)
逻辑同深拷贝类似,浅拷只会影响一层数据结构
/** * shallowClone 浅拷贝 * @param {*} source 源对象(需要拷贝的对象) * @returns 新对象 */ const shallowClone = source => { const obj = {}; for (const key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { obj[key] = source[key]; } } return obj; }; // test: const a = { age: 12, otherInfo: { sex: 0, }, }; const b = shallowClone(a); b.age = 22; b.otherInfo.sex = 1; console.log('a------>', a); // a------> { age: 12, otherInfo: { sex: 1 } } console.log('b------>', b); // b------> { age: 22, otherInfo: { sex: 1 } }
手写 new 操作符 (customNew)
/**
* customNew new操作符
* @param {*} fn 普通函数(!不能是箭头函数)
* @param {*} rest 参数
* @returns 新对象(类似于实例)
*/
const customNew = (fn, ...rest) => {
// 1.创建一个新对象
const obj = {};
// 2.让新对象的隐士原型等于函数的显示原型
obj.__proto__ = fn.prototype;
// 3.绑定 this, 让函数的 this 指向这个新对象(也就是绑定一些属性)
const res = fn.apply(obj, rest);
// 4.判断返回
return Object.prototype.toString.call(res).slice(8, -1) === 'Object' ? res : obj;
};
// test:
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.getName = function () {
return this.name;
};
const person = customNew(Person, 'John', 11);
console.log('person ------->', person); // person -------> Person { name: 'John', age: 11 }
console.log('person.getName() ------->', person.getName()); // person.getName() -------> John
手写节流 (throttle)
/**
* throttle 节流
* 某一段时间内只触发一次,按第一次来算
* @param {*} fn 方法
* @param {*} time 时间(默认 1500ms)
* @returns
*/
const throttle = (fn, time = 1500) => {
// 标记状态
let flag = null;
return function () {
// 若 flag 为 true, 则继续执行,不做任何操作
if (flag) return;
// 更改标记状态
flag = true;
// 执行方法
fn();
// 定时器
let timer = setTimeout(() => {
// time 结束,修改 flag = false
flag = false;
// 清除定时器标记
clearTimeout(timer);
}, time);
};
};
手写防抖 (debounce)
/**
* debounce 防抖
* 某一段时间内只触发一次,按最后一次来算
* @param {*} fn 方法
* @param {*} time 时间(默认 1500ms)
* @returns
*/
const debounce = (fn, time = 1500) => {
// 定期器标记
let timer = null;
return function () {
// 若 timer 为 true, 则停止之前方法,重新开始
timer && clearTimeout(timer);
timer = setTimeout(() => {
// 执行方法
fn();
}, time);
}
};
特殊字符描述:
- 问题标注
Q:(question)
- 答案标注
R:(result)
- 注意事项标准:
A:(attention matters)
- 详情描述标注:
D:(detail info)
- 总结标注:
S:(summary)
- 分析标注:
Ana:(analysis)
提示标注:
T:(tips)
往期推荐:
- 前端面试实录HTML篇
- 前端面试实录CSS篇
- JS 如何判断一个元素是否在可视区域内?
- Vue2、3 生命周期及作用?
- 排序算法:QuickSort
- 箭头函数与普通函数的区别?
- 这是你理解的CSS选择器权重吗?
- JS 中 call, apply, bind 概念、用法、区别及实现?
- 常用位运算方法?
- Vue数据监听Object.definedProperty()方法的实现
- 为什么 0.1+ 0.2 != 0.3,如何让其相等?
- 聊聊对 this 的理解?
JavaScript 为什么要进行变量提升,它导致了什么问题?
最后:
- 欢迎关注 『前端进阶圈』 公众号 ,一起探索学习前端技术......
- 公众号回复 加群 或 扫码, 即可加入前端交流学习群,一起快乐摸鱼和学习......
- 公众号回复 加好友,即可添加为好友
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。