前端基础:call、apply、bind、instanceof、typeof、new 的实现

348 阅读2分钟

前提

学习本篇前提应具备原型、原型链的相关知识,详情可见【JS 原型与原型链】

本文为了也检验自己对原型、原型链的掌握程度。 故本文没有实现过程,只有示例和实现结果,如有错误的欢迎留言指正。

1. call

Function.prototype.call 函数,接收一个或多个参数来调用,第一个参数是this。详情可见MDN Call

call使用示例

let obj = {
    name: 'hahaha',
    Func: function ( num1,num2) {
        console.log(this.name, num1,num2)
    }
}
let p = { name: 'guoby' }

obj.Func(1,2) // hahaha, 1, 2
obj.Func.call(p,1,2) // guoby, 1, 2

实现MyCall函数

  • 让所有函数都有MyCall这个方法,既需要在Function的原型上挂载该函数。
  • MyCall 函数第一个值是this非严格模式默认是window。
  • 通过传递过来的this去调用 MyCall函数的调用者。
Function.prototype.MyCall = function (context, ...args) {
      context = context || window
      const fn = Symbol('fn');
      context[fn] = this // this指向调用者,即当前作用域this为调用MyCall的函数
      let res = context[fn](...args) // this指向调用者,既fn()中的this指向 context
      delete context[fn]
      return res
}

2. apply

第一个参数是this,和call不同的是 其余参数以一个数组的形式提供的参数。详情可见MDN apply

apply使用示例

obj.Func.apply(p,[1,2,3]) // guoby [1, 2, 3]

实现MyApply

Function.prototype.MyApply = function (context, args) {
      context = context || window
      const fn = Symbol('fn');
      contex[fn] = this // this指向调用者,即当前作用域this为调用MyCall的函数
      let res = context.[fn](...args) // this指向调用者,既fn()中的this指向 context
      delete context._fn
      return res
}

3. bind

bind不立即执行,接受多个参数。返回一个函数。详情可见MDN bind

bind使用示例:

function Func () {
    console.log(this)
}
let obj = {
    name: 'guoby'
}
let fn = Func.bind(obj,1,2);
// 第一种用法:
fn(2,3)   // {name: 'guoby'} [1, 2, 2, 3]
// 第二种用法
new fn(4,5) // Func {}  [1, 2, 4, 5]
// 【注意:此时this的指向为所在函数】

实现MyBind

Function.prototype.MyBind = function (context, ...args) {
    let that = this;
    const MyBindCb  = function (...args2) {
        let context_ = context
        // new调用的情况 即:this.__proto__ == MyBindCb.prototype // true
        if(this instanceof MyBindCb) {
            context_ = that
        }
        // todo: 校验args和args2 都没有则 that.apply(context_)
        return that.apply(context_,args.concat(args2))
    }
    return MyBindCb
}

instanceof

instanceof 判断某个对象是不是某个构造函数的实例对象,详情可见MDN instanceof

使用示例

function Func () {}
let fn = new Func()
fn instanceof Func  // true
fn instanceof Object  // true

实现MyInstanceof

function MyInstanceof (objInstance, Func) {
    // todo: 校验 objInstance, Func 是否有原型
    // todo:校验 Func 是否为函数
    
   let objProto = objInstance.__proto__
   let FuncP = Func.prototype
    
   while(true) {
       if(!objProto) return false
       if(objProto == FuncP) return true
       objProto = objProto.__proto__ // 指针向下找
    }
}

typeof

返回元素的类型,typeof 不能判断对对象的实际类型,详情可见MDN tyoeof

使用示例

typeof 2  // number
typeof '' // string
typeof {} // object
typeof [] // object
typeof null // object
function Func () {}
typeof Func // function

实现MyTypeof

function MyTypeof(context){
  return context.constructor.name 
} // 对null undefined不好使
// 使用 Object.prototype.toString.call()
function MyTypeof2(context){
  return Object.prototype.toString.call(context).slice(8,-1).toLowerCase();
}

new

可参见原型链的这节探索使用new有什么不一样?