前端面试(手写)

手写篇

  1. 手写 instenceof

原生的 instanceof

console.log([] instanceof Array) // true

console.log(’’ instanceof Array) // false
手写 myInstanceof :

function myInstanceof(left,right){

let proto = left.__proto__

let prototype = right.prototype

while(true){
    
    if(proto === null)return false
    
    if(proto === prototype)return true
    
    proto = proto.__proto__
    
}

}

console.log(myInstanceof([],Array))// true

console.log(myInstanceof(’’,Array))// false
实现原理:

通过不断的沿着原型链查找,如果找到顶端了即: proto === null ,那么就说明没有找到,返回false,说明 left 不是 right 构造函数的实例

如果找到隐式原型 proto 等于构造函数的原型 prototype ,那么说明 left 是 right 构造函数的实例,返回true

其它情况就是不断的改变 proto ,以便可以不断的往上查找

  1. 手写 flat

原生示例:

const arr1 = [1, 2, [3, 4]];
arr1.flat();
// [1, 2, 3, 4]

const arr2 = [1, 2, [3, 4, [5, 6]]];
arr2.flat();
// [1, 2, 3, 4, [5, 6]]

const arr3 = [1, 2, [3, 4, [5, 6]]];
arr3.flat(2);
// [1, 2, 3, 4, 5, 6]

const arr4 = [1, 2, [3, 4, [5, 6, [7, 8, [9, 10]]]]];
arr4.flat(Infinity);
// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
手写 flatDeep:

function flatDeep( arr, dep=1 ){
let ret = []

for(let i=0;i<arr.length;i++){
    
    if(Array.isArray(arr[i])){
        
        dep>0 ? (ret = ret.concat(flatter(arr[i],dep-1))):(ret.push(arr[i]))
        
    }else{
        
        ret.push(arr[i]) 
    }
}

return ret

}
实现原理:

第一个参数是数组,第二个是降维层级,

用for循环遍历这个数组,检测每一项

如果这项是不是数组则直接添加到 ret 结果数组里面

否则根据降维层级判断,默认是降一维层级,当递归降维不满足 ret>0 ,说明已经达到dep降维层数了,其它情况即 ret.push(arr[i])

  1. 手写 call

Function.prototype.myCall = function(context){

context =(context === null || context === undefined) ? window : context

context.fn = this// 其实就等价于 obj.fn = function say(){} 当指向 context.fn 时,say里面的this 指向obj [关键]
//obj 此时变成 var obj = {name:'innerName',fn:function say(){console.log(this.name)}}

let args = [...arguments].slice(1) //截取第二个开始的所有参数
let result= context.fn(...args)//把执行的结果赋予result变量

delete context.fn //删除执行上下文上的属性 (还原)由var obj = {name:'innerName',fn:function say(){console.log(this.name)}}删除fn
return result

}
var name = ‘outerName’
var obj = {
name:‘innerName’
}
function say(){
console.log(this.name)
}
say()//outerName 等价于 window.say this指向window
say.myCall(obj)//innerName
实现原理:

函数的原型方法call 第一个参数是传入的执行上下文,后面传入的都是参数,以逗号隔开

当传入的是null或undefined是执行上下文是指向 window ,否使为传入的对象,然后再传入的对象身上添加 fn 属性并把函数实例say函数赋值给fn,此时变成

var obj = {name:‘innerName’,fn:function say(){console.log(this.name)}} 此时 context 就是obj对象啦,所有你执行 context.fn(…args)

其实就是 obj.fn(…args) , fn 其值是 function say(){ console.log(this.name) } ,所以这个 this 就变成 obj 对象了

然后就是结果赋值,对象还原

返回结果

  1. 手写 apply

Function.prototype.myApply = function(context){

context =(context === null || context === undefined) ? window : context

let result

context.fn = this

result = arguments[1] ? context.fn(...arguments[1]) : context.fn()

delete context.fn

return result

}
同 myCall 实现原理大致相同,不同的是由于 call 和 apply 的传参方式不一样,

我们需要额外的对第二个参数做判断, apply 受参形式是数组,且再第二个参数位置,

一:如果第二个参数存在,执行的时候就把第二个参数(数组形式)用扩展运算符打散后传入执行

二:如果第二个参数不存在,执行执行

其它就于 call 的实现一样

  1. 手写 bind

Function.prototype.myBind = function(context){

context =(context === null || context === undefined) ? window : context

let o = Object.create(context)

o.fn = this

let args = [...arguments].slice(1)

let fn= function(){
    
    o.fn(...args)
}

return fn

}
bind 的手写实现,与其它两个区别是返回一个函数,并没返回函数执行的结果,并且受参形式不受限制

实现原理:

通过 Object.create 方法创建一个新对象,使用现有的对象来提供新创建的对象的 proto ,通过 中介对象 o 来实现,来达到不影响传入的对象

  1. 手写 new

new 一个函数的时候,会生成一个实例,该实例的隐式原型 proto ===该函数的 prototype 原型对象

在构造函数中this指向当前实例

最后再将实例对象返回

function myNew(func){

//第一步 将函数的 prototype 指向 o 对象的__proto__
let o = Object.create(func.prototype)

//第二步 通过call改变 this的指向,使之指向 o
let ret = func.call(o)

//第三步 如果构造函数里面有返回对象,则返回这个对象,没有则返回 o 对象
return typeof ret === 'object' ? ret : o

}
检测:

function M(){}

let m = myNew(M); // 等价于 new M 这里只是模拟
console.log(m instanceof M); // instanceof 检测实例
console.log(m instanceof Object);
console.log(m.proto.constructor === M);

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值