JS经典面试题手撕代码

329 阅读3分钟

手写call、apply、bind

公共部分
function show(...args) {
   console.log(this.name);
   console.log(...args);
}
const person = {name: 'xyz'};

call

语法:

fn.call(obj,...args)

实现代码:

Function.prototype.myCall = function (obj, ...args) {
   // 在这里判断是否有obj
   obj= obj|| window
   /*给obj添加一个属性fn,并把要改变指向的函数赋值给他这里的this是show,那么obj.fn = show */
   obj.fn = this;
   //调用obj中的show,并且把参数传递进去,把他保存在result中
   const result = obj.fn(...args);

  // 因为obj是另一个函数或者对象,所以不能改变他原来的结构,把fn属性用完删掉  delete obj.fn;
  //返回执行结果
  return result;
}
show.myCall(person, 1, 2, 3); //xyz 123

apply

语法:

fn.apply(obj,[...args])

注意点:

apply第二个参数接收的是一个数组。将要传的数据放入数组中,如果不是数组就会报错。其他的和call差不多。

代码实现:

Function.prototype.myApply = function (obj, args = []) {
  //因为也可以不传参数,所以可以先给第二个参数赋值个空数组
  // 判断是否是一个数组
  if (args && !Array.isArray(args)){
    throw new Error('这不是一个数组')
  }
  obj= obj|| window
  obj.fn = this;
  const result = obj.fn(...args);
  delete obj.fn;
  return result;
}
show.myApply(person,["xyz","zxy"]); // xyz xyz zxy

bind

语法:

fn.bind(obj,...args)()

bind的注意点:

  1. 不会立即执行会返回一个新函数
  2. 柯里化特征
  3. 返回的新函数可以实例化

这几点就比较麻烦了,所以比较call和apply比较难理解一点。

Function.prototype.myBind = function (obj, ...args1) {
  // 拿到要修改this,就是show函数
  const that = this;
  //这里的o就是个跳板
  const o = function () {};
  // 因为bing是会返回的,不是立即执行的
  const newFn = function (...args2) {
  // 将参数合并到一个数组
  const args = args1.concat(args2);
  // 因为bind可以返回后,手动执行,并且可以实例化
  //所以我们要判断this的指向,如果返回过去实例化了,就用实例化后的this
    if (this instanceof o) {
        that.apply(this, args);
    } else {
    // 如果没有就用原来的
        that.apply(obj, args);
    }
  }
  // 还要把原型给改下
  o.prototype = that.prototype;
  newFn.prototype = new o;
  return newFn;
}
show.myBind(person, 5, 6)(2, 1);  // xyz 5 6 2 1

new的过程

公共部分
function Person(name) {
this.name = name
}

new的注意点:

  如果是构造函数里面return 的是一个对象,那么new出来的就是这个对象

function Person(name) {    
this.name  = name    
return {        
age:23    
}
}
let a = new Person('xyz')
console.log(a); 

代码实现:

function myNew(myObj,...args) {
  // new就是先创建一个对象
  let obj = {}
  // 将实例的__proto__指向构造函数的prototype
  obj.__proto__ = myObj.prototype
  // 利用apply给父类的属性赋值
  // 如果里面返回的是一个对象那么myObj.apply(obj,args)就是返回的那个对象,如果没有就是返回的undefined
  let result = myObj.apply(obj,args)
  // 那么实例拿到的就是这个对象,所以在return的时候判断下
  // 如果是return的对象,那么返回result如果没有就返回obj
  return result instanceof Object ? result : obj
}
let a = myNew(Person,"xyz")
console.log(a.name); // xyz


instanceof

公共部分
function Person(name) {this.name = name}
let a = new Person('xyz')

instanceof依靠的是原型链进行判断

代码实现:

function myInstanceof (left,right) {
   let rightValue = right.prototype
   left = left.__proto__
   while (true){
       if (left === null){
           return false
       }else if (left === rightValue){
          return true
       }
       left = left.__proto__
   }
}
myInstanceof(a,Person) //true

小结

学习前端一年多了,平时都是看看别人的文章。这些都是当时学习js自己看文章,加上自己的理解。第一次写有什么不足之处,希望大家评论区留言😊