前端面试常考的编程题

1、手写深拷贝

什么是浅拷贝?

如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝的就是内存地址 ,所以如果其中一个对象改变了这个地址,就会影响到另一个对象。

什么是深拷贝?

将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象,且修改新对象不会影响原对象
常用的深拷贝方法:

JSON.parse(JSON.stringify(obj))

缺点:不能拷贝 undefined、函数、正则会转成空对象、data 会调用toString()等。
手写深拷贝:

function deepClone(obj){
    if(obj===null||typeof obj !=='object') return obj
    if(obj instanceof RegExp) return new RegExp(obj)
    if(obj instanceof Date) return new Date(obj)
    if(obj instanceof Function) return new Function(obj)
    if(Array.isArray(obj)) return obj.map(item=>deepClone(item))
    const newObj={}
    for(let i in obj){
        if(obj.hasOwnProperty(i)){
           newObj[i]= deepClone(obj[i])
        }
        
    }
    return newObj
}

const obj={
    name:'zhangssn',
    address:{
        x:100,
        y:200
    }
}
let obj1=deepClone(obj)
console.log(obj,obj1)
console.log(obj==obj1)

2、防抖、节流

2.1 节流

节流之时间戳版

let throttle = function(func, delay){
       let prev = Date.now();
       return function(){
           var now = Date.now();
           if(now-prev>=delay){
               func.apply(this, arguments);
               prev = Date.now();
           }
       }
 }

节流之定时器版

let throttle = function(func, delay) {
    let timer = null;
    return function() {
      if (!timer) {
        timer = setTimeout(function() {
          func.apply(this, arguments);
          timer = null;
        }, delay);
      }
    };
  };

定时器节流,由于定时器的特性,无法首次立即执行,但可以确保必定会执行最后一次
时间戳节流,可以首次立即执行,但无法确保会执行最后一次

2.2 防抖

function debounce(fn, wait) {
    let timeout = null;
    return function() {
      // 如果多次触发将上次记录延迟清除掉
      if (timeout !== null) clearTimeout(timeout);
      timeout = setTimeout(function() {
          fn.apply(this, arguments);
          timeout = null;
        }, wait);
    };
  }

3、手写call、apply

改变this指向,语法:
fun.call(thisArg, arg1, arg2, …),调用一个函数, 其具有一个指定的this值和分别地提供的参数(参数的列表)。
func.apply(thisArg, [argsArray]),调用一个函数,以及作为一个数组(或类似数组对象)提供的参数。

手写call代码:

//通过对象调用方法来修改this指向
Function.prototype.call2 = function (context){
  const ctx = context || window
  ctx.func = this
  // 测试用例中这个this打印出来是:函数test [function test]
  const args = [...arguments].slice(1) // 保存剩余参数
  const res = arguments.length > 1 ? ctx.func(...args) : ctx.func()
  delete ctx.func // 避免造成全局污染
  return res
}

let foo = {
    value: 1
}
function test(name) {
    console.log(name)
    console.log(this.value);
}
test.call2(foo, 'black') // black 1

手写apply代码:

Function.prototype.apply2 = function (context){
  const ctx = context || window
  ctx.func = this
  // 测试用例中这个this打印出来是:函数test [function test]
  const res = arguments[1] ? ctx.func(...arguments[1]) : ctx.func()
  delete ctx.func // 避免造成全局污染
  return res
}

let foo = {
    value: 1
}
function test(name) {
    console.log(name)
    console.log(this.value);
}
test.call2(foo, 'black') // black 1

4、手写instanceof

instanceof 可以判断一个引用是否属于某构造函数;
还可以在继承关系中用来判断一个实例是否属于它的父类型

// left 必须是对象
// right 必须是函数
function myInstance(left, right){    
    // 验证如果为基本数据类型,就直接返回false
    const baseType = ['string', 'number','boolean','undefined','symbol']
    if(baseType.includes(typeof(L))) return false
    
    // 取 right 的显示原型
    let RP  = right.prototype;
    //取 left 的隐式原型 
    left = left.__proto__;
    while(true){
    	// 找到最顶层
        if(left === null){    
            return false;
        }
        if(left === RP){
            return true;
        }
        // 没找到继续向上一层原型链查找
        left = left.__proto__;
    }
}
  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值