模拟实现 JavaScript 常见的内置方法

  • JavaScript
  • 梁凤波
  • 192
  • 8
  • call 模拟实现
  • apply 模拟实现
  • bind 模拟实现
  • new 模拟实现
  • instanceof 模拟实现

call 模拟实现

> call() 方法在使用一个指定的 this 值和若干个指定的参数值的前提下调用某个函数或方法

实现的原理:利用 this 绑定的一条规则:函数在作为对象的方法调用时,this 指向的是该对象。那么实现的步骤为:

  • 首先,把传入绑定的 context 对象上新增一个 fn 方法
  • 然后,把原调用的方法赋给这个 fn 方法
  • 再处理一下传入的参数,传入 context.fn() 参数进行调用
  • 最后删除 fn 方法,返回结果
Function.prototype.call2 = function (context) {
  // 判断一下是否是函数调用
  if (typeof this !== 'function') {
    throw new Error('需要函数调用')
  }

  // 判断一下传入绑定的 this 是否为 null
  context = context || window
  // 把调用的函数赋给绑定对象
  context._fn = this

  // 处理参数
  var args = []
  for (var i = 1, len = arguments.length; i < len; i++) {
    args.push('arguments[' + i + ']')
  }

  // args 等于 [arguments[1], arguments[2]]
  // 'context._fn(' + args + ')' 等于 'context._fn(arguments[1], arguments[2])'

  var r = eval('context._fn(' + args + ')')
  delete context._fn

  return r
}

ES6 模拟实现

Function.prototype.call2 = function (context) {
  if (typeof this !== 'function') {
    throw new Error('需要函数调用')
  }

  context = context || window
  context._fn = this

  const args = [...arguments].slice(1)
  const r = context._fn(...args)

  delete context._fn
  return r
}

apply 模拟实现

实现的原理和 call 一样,不过 apply 传入第二个参数为数组,所以要处理一下。

Function.prototype.apply2 = function (context, arr) {
  if (typeof this !== 'function') {
    throw new Error('需要函数调用')
  }

  context = context || window
  context._fn = this

  var r = null
  if (!arr) {
    r = context._fn()

  } else {
    var args = []
    for (var i = 0, len = arr.length; i < len; i++) {
      args.push('arr[' + i + ']')
    }
    r = eval('context._fn(' + args + ')')
  }

  delete context._fn
  return r
}

ES6 模拟实现

Function.prototype.apply2 = function (context) {
  if (typeof this !== 'function') {
    throw new Error('需要函数调用')
  }

  context = context || window
  context._fn = this

  let r = null
  if (arguments[1]) {
    r = context._fn(...arguments[1])
  } else {
    r = context._fn([1])
  }

  delete context._fn
  return r
}

bind 模拟实现

> bind() 方法会创建一个新函数。当这个新函数被调用时,bind() 的第一个参数将作为它运行时的 this,之后的一序列参数将会在传递的实参前传入作为它的参数。(来自于 MDN )

实现原理:

  • 第一次调用时,可以传参,所以要处理第一次调用函数传入的参数
  • 调用时,返回一个函数
  • 第二次调用时候,也可以传参,也需要处理传入的参数
  • 第二次调用可以分为普通函数调用,也可以当做构造函数调用,此时传入的 this 会失效
Function.prototype.bind2 = function (context) {
  if (typeof this !== 'function') {
    throw new Error('需要函数调用')
  }

  context = context || window
  var args = Array.prototype.slice.call(arguments, 1)
  var _this = this

  function F() {
  }

  var B = function () {
    var bindArgs = Array.prototype.slice.call(arguments)

    // 当作为构造函数时,this 指向实例,此时结果为 true,将绑定函数的 this 指向该实例,可以让实例获得来自绑定函数的值
    // 当作为普通函数时,this 指向 window,此时结果为 false,将绑定函数的 this 指向 context
    return _this.apply(this instanceof F ? this : context, args.concat(bindArgs))
  }

  // 修改返回函数的 prototype 为绑定函数的 prototype,实例就可以继承绑定函数的原型中的值
  F.prototype = this.prototype
  B.prototype = new F()

  return B
}

ES6 实现

Function.prototype.bind2 = function (context) {
  if (typeof this !== 'function') {
    throw new Error('需要函数调用')
  }

  context = context || window
  const args = [...arguments].slice(1)
  const _this = this

  return function F() {
    if (this instanceof F) {
      return new _this(...args, ...arguments)
    } else {
      return _this.apply(context, args.concat(...arguments))
    }
  }
}

new 模拟实现

步骤分为:

  • 创建一个新对象
  • 这个新对象会进行原型链连接
  • 将构造函数的作用域赋给新对象(将新创建的对象作为 this 上下文)
  • 执行构造函数中的代码(为这个新对象添加属性)
  • 如果该函数没有返回对象,返回新对象
function new2() {
  var obj = {}
  var Constructor = Array.prototype.shift.call(arguments)
  obj.__proto__ = Constructor.prototype
  var r = Constructor.apply(obj, arguments)

  return r instanceof Object ? r : obj
}

ES6 模拟实现

function new2(Constructor, ...args) {
  const obj = {}
  Object.setPrototypeOf(obj, Constructor.prototype)
  const r = Constructor.apply(obj, args)

  return r instanceof Object ? r : obj
}

instanceof 模拟实现

instanceof 运算符用于测试构造函数的 prototype 属性是否出现在对象的 __proto__ 中的任何位置

function instanceof2(L, R) {
  L = L.__proto__
  R = R.prototype

  while (true) {
    if (L == null) {
      return false
    }
    if (L === R) {
      return true
    }
    L = L.__proto__
  }
}
欢迎评论
评论列表
avatar

22

zen testing
...

来自「test」的回复

alert(1)
...

来自「test」的回复

testestest
avatar

11

123123
...

来自「Zen」的回复

zen testing
avatar

ceshi

ceshi

avatar

测试南瓜

最后一次测试

avatar

测试南瓜

我又来测试 了

avatar

测试南瓜2

我来测试的

avatar

测试南瓜

我来瞅瞅的

TOP