IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> 面试题手写call、apply、bind -> 正文阅读

[JavaScript知识库]面试题手写call、apply、bind

关于手写 call、apply、bind 的一些个人见解及具体代码和注释,有错误的地方往评论以纠正

call

Function.prototype._call = function (uThis, ...args) {
  // 获取 this
  let _this = uThis === undefined || uThis === null ? globalThis : Object(uThis);
  // 方法的唯一键
  const key = Symbol()
  // 将方法挂载到 this
  _this[key] = this
  // 调用,获取返回值
  const res = _this[key](...args)
  // 删除方法键
  Reflect.deleteProperty(_this, key)
  return res
}
测试代码(通用),所有测试均在 node 环境下
obj = {
  name: '小王',
  age: 18
}

function test (doingSomething, makeSomething) {
  console.log(this);
  console.log(this.name + '  ' + this.age + '  ' + doingSomething + '  ' + makeSomething);
}

function test2 () {
  console.log(arguments);
}
测试
test2._call({}) // { [Symbol()]: [Function: test2] },[Arguments] {}
test2.call({}) // {}, [Arguments] {}
test2._call(null, '') // global, [Arguments] { '0': '' }
test2.call(null, '') // global, [Arguments] { '0': '' }
test2._call(obj, 'eating') // { name: '小王', age: 18, [Symbol()]: [Function: test2] }, [Arguments] { '0': 'eating' }
test2.call(obj, 'eating') // { name: '小王', age: 18 },[Arguments] { '0': 'eating' }

apply

使用 apply 传入的第二个参数需要为数组,但存在几种特殊的的参数情况:

  1. {} 对象有内容、无内容都可,通过 new 得到的也可

  2. undefined

  3. null
    注意点:有一些特殊情况第二个参数的值会发生转变

      传入参数                apply处理                 调用函数中
    {length:2}       =>[undefined,undefined]      =>[Arguments]{'0':undefined,length;1}
    undefined                =>[]                 =>[Arguments]{} 
    null                     =>[]                 =>[Arguments]{}
    new String('abc')    =>['a','b','c']          =>[Arguments]{ '0': 'a', '1': 'd', '2': 'v' }
    new Number、Set、Map      =>[]                 =>[Arguments]{}
    
总结:
  1. 类似数组的对象(一些通过 new 得到的对象加了 length 属性就属于类似数组的对象)、通过 new String() 得到的对象都会做转换
  2. 只要是对象一律符合传入参数的规则(通过 new 得到的也一样),一律转为空数组
  3. undefined、null一律转为空数组
    代码
Function.prototype._apply = function (uThis, args = []) {
  // 获取 this  
  let _this = uThis === undefined || uThis === null ? globalThis : Object(uThis);
  function isObject (args) {// 判断是否为对象
    // 注意: typeof null 虽然为 object 但 null 实际并不为对象,具体原因可以百度
    return typeof args === 'object' && args !== null
  }
  function isLikeArray (o) {  // 类数组对象的判断
    if (isObject(o) && Number.isFinite(o.length) && o.length >= 0 && o.length < 4294967296) {
      // 4294967296: 2^32
      return true
    } else {
      return false
    }
  }
  (function () {
    // 传入的第二个参数的处理
    if (isLikeArray(args)) {
      args = Array.from(args) // 转为数组
    }
    else if (isObject(args)) {
      args = []
    }
    else if (!Array.isArray(args)) {
      throw new TypeError('second argument to apply must be an array')
    }
  })(args)
  // const res = this.call(_this, ...args) // 可以通过这样调用减少代码量,就是会多一次获取this的步骤
  const key = Symbol()  // 用于临时存储函数
  _this[key] = this // 隐式绑定this指向到context上
  const res = _this[key](...args); // 执行函数并展开数组,传递函数参数
  Reflect.deleteProperty(_this, key)
  return res
}

测试

test1._apply(obj) // [Arguments] {}
test1.apply(obj)// [Arguments] {}
test2._apply(obj, { length: 2 }) // [Arguments] { '0': undefined, '1': undefined }
test2.apply(obj, { length: 2 }) // [Arguments] { '0': undefined, '1': undefined }
test._apply(obj, '') // throw new TypeError('second argument to apply must be an array')
test.apply(obj, '') // TypeError: second argument to apply must be an array
test._apply(obj, 1) // throw new TypeError('second argument to apply must be an array')
test.apply(obj, 1) // throw new TypeError('second argument to apply must be an array')
test._apply(obj, []) // { name: '小王', age: 18, [Symbol()]: [Function: test] },小王  18  undefined  undefined
test.apply(obj, []) // { name: '小王', age: 18 },小王  18  undefined  undefined
test._apply(obj, ['Eating']) // { name: '小王', age: 18, [Symbol()]: [Function: test] },小王  18  Eating  undefined
test.apply(obj, ['Eating']) // { name: '小王', age: 18 },小王  18  Eating  undefined

bind

Function.prototype._bind = function (uThis, ...args) {
  // 拿到方法
  const fn = this
  // 处理this
  uThis = uThis === undefined || uThis === null ? globalThis : Object(uThis);
  // 包装新方法
  const newFn = function (...newargs) {
    const byNew = this instanceof newFn // this 是否为 newFn 的实例 也就是返回的 newFn 是否通过 new 调用
    const _this = byNew ? this : Object(uThis) // new 调用就绑定到 this 上,否则就绑定到传入的 uThis 上
    // 改变 this 指向并调用
    return fn.call(_this, ...args, ...newargs)
  }
  if (fn.prototype) {
    // 复制源函数的 prototype 给 newFn 一些情况下函数没有 prototype ,比如箭头函数
    newFn.prototype = Object.create(fn.prototype);
  }
  return newFn
}
测试
test2._bind()() // global,[Arguments] {}
test2.bind()() // global,[Arguments] {}
test2._bind(null, 'eating')('make rice') // global, [Arguments] { '0': 'eating', '1': 'make rice' }
test2.bind(null, 'eating')('make rice') // // global, [Arguments] { '0': 'eating', '1': 'make rice' }
console.log(new (student._bind(obj))('小王', 18)); // student { name: '小王', age: 18 }
console.log(new (student.bind(obj))('小王', 18)); // student { name: '小王', age: 18 }

参考文章

  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2022-01-29 22:59:45  更:2022-01-29 23:02:19 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 14:09:47-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码