登录
原创

手写 call,apply,bind,new原生方法

专栏ECMAScript 6 简介
发布于 2020-10-10 阅读 906
  • JavaScript
原创

前言

记录一下最近学习的一些原生 JS 方法的实现,中间会用到一些es6的语法,有兴趣的朋友可以参考阮一峰 ES6 入门教程,留个赞再走呗。

实现 call 方法

// 将方法挂在到顶层函数 Function 的原型上,这样保证都能访问
Funtion.prototype.myCall = function (...rest) {
  // 获取第一个参数为目标函数
  var thisArg = rest[0]
    
  // 判断第一参数是否为原始值,如果是则通过构造函数包装成对象
  var thisArgType = typeof thisArg

  if (thisArgType === 'number') {
    thisArg = new Number(thisArg)
  } else if (thisArgType === 'string') {
    thisArg = new String(thisArg)
  } else if (thisArgType === 'boolean') {
    thisArg = new Boolean(thisArg)
  }

  // 截取索引 1 开始的剩余参数
  var invokeParams = rest.slice(1)

  // 获取当前 this
  var invokeFunc = this

  // 如果 thisArg  是 null or undefined,返回当前函数执行的结构就可以
  if (thisArg === null || thisArg === undefined) {
    return invokeFunc(...invokeParams)
  }

  // 生成唯一值
  var uniqueProName = Symbol(thisArg)
  
  // 将当前 this 放到目标函数下
  thisArg[uniqueProName] = invokeFunc
  
  // 返回目标函数执行结果
  return thisArg[uniqueProName](...invokeParams)
}

// 测试代码1
function test (a, b) {
  var args = [].slice.myCall(arguments, a, b)
  console.log(args)
}

test(1, 2)

// 测试代码2
var obj = {
  name: 'wex'
}
var name = 'global'
function getName () {
  console.log(this)
  console.log(this.name)
}
getName()
getName.myCall(obj)

实现 apply 原生方法

Function.prototype.myApply = function (thisArg, thisArray) {
  // 处理第二个参数
  const paramsArray = Array.isArray(thisArray) ? thisArray : []
  
  // 第一个参数如果为基本类型,转化为包装函数
  var thisArgType = typeof thisArg
  if (thisArgType === 'number') {
    thisArg = new Number(thisArg)
  } else if (thisArgType === 'string') {
    thisArg = new String(thisArg)
  } else if (thisArgType === 'boolean') {
    thisArg = new Boolean(thisArg)
  }

  // 获取当前 this 指向
  var invokeFunc = this
  
  // 判断 目标函数 thisArg 是否为 null or undefined,如果是,返回当前函数执行结果
  if (thisArg === null || thisArg === undefined) {
    return invokeFunc(...paramsArray)
  }

  var uniqueProName = Symbol(thisArg)
  thisArg[uniqueProName] = invokeFunc
  return thisArg[uniqueProName](...paramsArray)
} 

// 测试代码1
var num = Math.max.myApply(null, [1, 29, 10])
console.log(num)

// 测试代码2
function test (a, b, c) {
  let num = Math.max.myApply(arguments, [a, b, c])
  console.log(num)
}
test(1, 22, 33)

实现 bind 原生方法

Function.prototype.myBind = function (...rest) {
  // 拿到绑定的函数
  var boundThis = rest[0]

  // 获取从 1 开始的剩余参数
  var boundParams = rest.slice(1)

  // 获取绑定目标的函数
  var boundTargetFunc = this
  
  // 判断绑定目标必须是函数,否则抛出错误
  if (typeof boundTargetFunc !== 'function') {
    throw new Error('绑定目标必须为函数')
  }

  // 新函数
  function fBound () {
    var restParams = [].slice.call(arguments)
    // 获取所有参数
    var allParams = [...boundParams, ...restParams]
    
    // 通过 intanceof 判断 this 是不是 fBound 的实例
    var isConstructor = this instanceof fBound
    if (isConstructor) {
      // 如果是,说明是通过 new 调用的
      return new boundTarfetFunc(...allparams)
    } else {
      return boundTargetFunc.apply(boundThis, allParams)
    }
  }
  // 返回 fBound
  return fBound
}

实现 new 原生方法

function myNew(func, ...rest) {
  // 创建一个空对象,并指定原型为 func.prototype
  var obj = Object.create(func.prototype)

  // new 构造函数时要执行的函数,同时指定 this
  func.call(obj, ...rest)

  // 最后返回这个对象
  return obj
}
// 测试代码
function TestNew (name, age) {
  this.name = name 
  this.age = age
}
var Test = myNew(TestNew, '小明', 20)
console.log(Test.name)

评论区

wex
3粉丝

雾锁山头山锁雾,天连水尾水连天

0

1

0

举报