zoukankan      html  css  js  c++  java
  • 前端面试手写篇

    手写篇

    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,那么说明 leftright 构造函数的实例,返回true

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

    2. 手写 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])

    3. 手写 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对象了

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

    返回结果

    4. 手写 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实现原理大致相同,不同的是由于callapply的传参方式不一样,

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

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

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

    其它就于call的实现一样

    5. 手写 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来实现,来达到不影响传入的对象

    6. 手写 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);
    
  • 相关阅读:
    WPF一步一脚印系列(1):万事起头难
    php设置时区
    关于我的几个博客
    php如何实现页面跳转
    穷人与富人的区别
    如何抓取关键字在百度搜索的排名
    我的博客园开通了
    在Foxmail中出现SSL连接错误应该如何解决
    javascript实现键盘按下回车时触发
    关于网站分页
  • 原文地址:https://www.cnblogs.com/studyshufei/p/13096239.html
Copyright © 2011-2022 走看看