前端面试中常常要求手写底层函数实现, 现在总结了一些,拿走不谢,并持续更新中...
1. new 关键字
-
思路: 改变this指向 + 原型继承
// new关键字使用 function Father(name) { this.name = name; } Father.prototype.getName = function() { return this.name; }; let child = new Father("ming"); // console.log("name", child.name); // console.log("name", child.getName());// 关键字实现 function newFn(fn) { const obj = {}; obj.__proto__ = fn.prototype; // 原型继承 fn.apply(obj, [...arguments].slice(1)); // 改变构造函数this指向 return obj; } function Fn(name) { this.name = name; } let exampleObj = newFn(Fn, "jojo"); // console.log("name", exampleObj.name);
2. instanceof 实现
- 判断对象是否在构造函数的原型链上
function instanceofFn(obj, Fn) {
let objProto = obj.__proto__; // 对象的__proto__
let Fnprototype = Fn.prototype; // 构造函数的原型
while (objProto !== null) { // 如果__proto__为null, 跳出循环
if ((objProto === Fnprototype)) {
return true;
}
objProto = objProto.__proto__;
}
return false;
}
// console.log("instance", instanceofFn({}, Object));
// console.log("instance", instanceofFn(Function, Object));
// console.log("instance", instanceofFn(instanceFn, Object));
3. call, apply, bind 函数实现
// call的使用
function fnCall() {
console.log(this.name);
console.log(arguments);
}
let callObj = {
name: "jojo"
};
// fnCall();
// fnCall.call(callObj);
// call函数实现
Function.prototype.myCall = function(context, ...args) {
context = typeof context === "object" ? context : window; // 如果是对象, this指向为当前对象, 如果不是指向window
const key = Symbol(); // key值唯一
context[key] = this;
context[key](...args); // 函数执行, 传参进去
delete context[key]; // 删除函数
};
// fnCall.myCall(callObj, 1, 2);
// apply函数实现
// 唯一区别在于传参
Function.prototype.apply = function(context) {
context = typeof context === "object" ? context : window;
const key = Symbol();
context[key] = this;
context[key]([...arguments].slice(1));
delete context[key];
};
// bind 函数实现
// 绑定对象且返回一个函数, 可接受传参
Function.prototype.myBind = function(context) {
context = typeof context === "object" ? context : window;
return (...args) => {
// 箭头函数内部没有arguments
this.call(context, ...args);
};
};
// fnCall.myBind(callObj)(1);
4. Object.create函数实现
- 创建一个对象, 新对象可以使用原来对象的属性和方法
function objectCreate(obj) {
function M() {}
M.prototype = obj; // 构造函数的原型指向原来的对象
return new M(); // 返回新对象
}
const obj2 = {
name: "jojo",
age: "18",
getName() {
console.log(this.name);
}
};
Object.prototype.getAge = function() {
console.log(1);
};
let objCreate = objectCreate(obj2);
objCreate.getName();
objCreate.getAge();
5. 函数科里化
- 闭包 + 递归
固定参数 sum(1,2,3)
// 常见面试题sum(1,2,3) sum(1,2)(3) sum(1)(2)(3) 等于6
function totalSum(a, b, c) {
return a + b + c;
}
function currySum(fn) {
const arg = [...arguments].slice(1); // 存储私有变量
const length = fn.length;
return function() {
const totalArgs = arg.concat([...arguments]);
if (totalArgs.length < length) {
return currySum.call(this, fn, ...totalArgs); // 如果totalArgs长度不等于函数的参数, 递归
} else {
return fn.apply(this, totalArgs);
}
};
}
let total = currySum(totalSum);
// total(2)(3)(4);
不定参数 sum(1,2)(3)(4)(5)...
function curry() {
let args = [...arguments] || [];
let fn = function() {
let totalArgs = args.concat([...arguments]);
return curry.apply(this, totalArgs);
};
// toString 隐式转换
fn.toString = function() {
return args.reduce(function(a, b) {
return a + b;
});
};
return fn;
}
curry(1)(2)(3, 4)(5).toString(); // 要调用toString() 方法
6. 深拷贝
function deepclone(target, map = new WeakMap()){
if(typeof(target) === 'object'){
const cloneTarget = Array.isArray(target) ? [] : {}
if(map.get(target)){
return map.get(target)
}
map.set(target, cloneTarget)
for(const key in target){
cloneTarget[key] = deepclone(target[key], map)
}
return cloneTarget
}else{
return target
}
}
let obj = {
a:1,
b:{
c:{
d:33
}
}
}
let obj1 = deepclone(obj)
- flat
[1,[2,3,4,[5,6]]] => [1,2,3,4,5,6]
Array.prototype.flat = function(){
this.toString().split(",").map(item => +item)
}