首页 > 其他分享 >this 的指向与 call, apply, bind 的模拟实现

this 的指向与 call, apply, bind 的模拟实现

时间:2022-10-03 22:24:23浏览次数:53  
标签:function obj bind console window call apply fn

1. this 的指向问题

关于函数的 this 的指向并不是一个很复杂的问题

我们首先要明确一个定义:
fn = function () {...} 指的是 fn 这个属性存储着一个函数的地址 fn_addr, 而我们通过 fn_addr() 来执行相应地址的函数

接下来我们仍将 fn 称为 fn 函数, 只需知道它只是一个存储函数地址的变量即可

1.1 普通函数的 this 指向

对于普通函数 fn, 若

  • xx.fn(), 其中 fn 为对象 xx 上的一个属性, 则执行 fn 函数时, 其 this 指向的是 xx(这个 xx 可以是单个的对象, 也可以是由成员访问符 . 连接起来的嵌套对象: xx === obj.obj1.obj2.obj3)
    function fn() { console.log(this) }
    
    let obj = { val: 1, fn }
    
    obj.fn() //=> {val: 1, fn: ƒ}
    
  • fn(), 则
    • 在严格模式下, 其 this 指向为 undefined
    • 在非严格模式下, 其 this 指向为 window
    "use strict"
    
    function fn() { console.log(this) }
    
    fn() //=> undefined
    
    function fn() { console.log(this) }
    
    fn() //=> window
    
  • 出现 call, apply, bind, 对于 fn.call/apply(xx)fn.bind(xx)()
    • xxnullundefined
      • 则严格模式下, thiswindow
      • 非严格模式下, this 分别为 nullundefined
      "use strict"
      
      function fn() { console.log(this) }
      
      fn.call(null) //=> null
      fn.call(undefined) //=> undefined
      
      function fn() { console.log(this) }
      
      fn.call(null) //=> window
      fn.call(undefined) //=> window
      
    • xx 为引用类型, 则 this 指向为 xx
    • xx 为基础值类型
      • 严格模式下, thisxx
      • 非严格模式下, thisxx 所对应的引用值类型
      "use strict"
      
      function fn() { console.log(this) }
      
      fn.call(1) //=> 1
      
      function fn() { console.log(this) }
      
      fn.call(1) //=> Number {1}
      

1.2 箭头函数的 this 指向

箭头函数没有 this
当在箭头函数中访问 this 时, 其实还是获取的是其所在上下文中的 this

const obj = {val: 1}

const fn = () => console.log(this) // 定义时所在的上下文 this 为 window
let f

function f0() {
  console.log(this) // 输出 f0 上下文的 this
  fn()
  fn.call(obj)
}

function f1() {
  console.log(this) // 输出 f1 上下文的 this
  const fn = () => {console.log(this)} // 所在上下文 this 等到执行 f1 时才知道
  fn()
  fn.call(window) 
  f = fn
}

f0.call(obj)
//=> obj
//=> window
//=> window (该输出证明了箭头函数中的 this 不会被 call 改变)
f1.call(obj) 
//=> obj
//=> obj
//=> obj (该输出证明了箭头函数中的 this 不会被 call 改变)
f() 
//=> obj (即使将 fn 从 f1 里面提出到全局中, 其 this 仍然不变, 表示箭头函数的 this 只与其定义时所在的上下文有关, 而不是与运行时所在的上下文有关)

1.3 立即执行函数的 this 指向

  • 在严格模式下, 其 this 指向 undefined
  • 在非严格模式下, 其 this 指向 window
(function () {
  console.log(this) //=> window
})()
"use strict"
;(function () {
  console.log(this) //=> undefined
})()

1.4 回调函数的 this 指向

  • 若是绑定在 dom 元素上, 用来监听事件的非箭头函数, 其 this 为对应的 dom 元素
  • 若是当作普通的回调函数使用, 则若该回调函数非箭头函数, 其 this 与立即执行函数的 this 指向相同
function fn() {
  console.log(this)
}

[1, 2, 3].reduce(fn, 0)
//=> window
//=> window
//=> window
"use strict"
function fn() {
  console.log(this)
}

[1, 2, 3].reduce(fn, 0)
//=> undefined
//=> undefined
//=> undefined

2. call/apply 的模拟实现

2.1 call 的模拟实现

Function.prototype.call = function(obj, ...args) {
  obj = obj == null ? window : obj // 如果被改变的 this 指向 null 或者 undefined, 则 this 指向 window
  const objType = typeof obj
  obj = (objType !== "object" && objType !== "function") ? Object(obj) : obj // 如果被改变的 this 指向基础类型, 则将 this 改为指向引用类型

  obj.fn = this
  const res = obj.fn(...args)
  delete obj.fn
  return res
}

2.2 apply 的模拟实现

Function.prototype.apply = function(obj, arr) {
  obj = obj == null ? window : obj
  const objType = typeof obj
  const arrType = typeof arr
  obj = objType !== "function" && objType !== "object" ? Object(obj) : obj
  obj.fn = this

  if (arr != null && arrType !== "function" && arrType !== "object") // 若 arr 是非 null 和 undefined 的基础类型, 则报错
    new TypeError("CreateListFromArrayLike called on non-object")
  arr = Array.isArray(arr) ? arr : (arr == null ? [] : Array.from(arr)) // 若 arr 是 null 或 undefined, 则令 arr 为空数组, 若 arr 为非数组的引用类型, 则通过 Array.from 将 arr 转换为数组
  const res = obj.fn(...arr)
  delete obj.fn
  return res
}

2.3 bind 的模拟实现

Function.prototype.bind = function(obj, ...args1) {
  obj = obj == null ? window : obj
  const self = this
  const objType = typeof obj
  obj = objType !== "object" && objType !== "function" ? Object(obj) : obj

  const retFn = function(...args2) {
    if (this instanceof self)
      return self.apply(this, args1.concat(args2))
    return self.apply(obj, args1.concat(args2))
  }

  // 为 retFn.prototype 赋值, 以保证在对 bind 所返回的方法进行 new 操作时, 相应的实例对象原型仍然是调用 bind 的那个函数.prototype
  // 这里为了防止 retFn.prototype 被更改导致 new 出来的实例对象的原型被更改, 所以使用了 Object.create 方法
  retFn.prototype = Object.create(self.prototype)
  retFn.constructor = retFn

  return retFn
}

标签:function,obj,bind,console,window,call,apply,fn
From: https://www.cnblogs.com/suzukaze/p/this-call-apply-bind.html

相关文章

  • CSR1000V demo license apply
    Beforethedemolicenseapplyprocess,wecheckthelicensestatusfirstly.CSR1000V#showversionCiscoIOSXESoftware,Version03.16.09.S-ExtendedSuppor......
  • 备库状态为read only with apply 但v$managed_standby却未见rfs进程信息
    问题描述:备库状态为readonlywithapply,但查看v$managed_standby视图却未见rfs进程信息,如下所示.SQL>selectopen_modefromv$database;OPEN_MODE--------------------......
  • CALLBACK
    CloseHandleconstCreateCReateEventCreateWindowCS_HREDRAWCS_VREDRAWDWORDgetModuleHandle(1)HANDLEHBRUSHhWndINFINITEjoinLoadCursorlParamLPCTSTRLP......
  • 初识:Precision、Recall、Accuracy、F1-Score
    一、定义对于一个数据集的测试,一般会产生四种结果:TP、TN、FP、FN(T:true,表示正确;F:false,表示错误;P:positive;N:negative)TP:truepositive,正样本,预测为正样本;TN:true......
  • Spring源码-applyPropertyValues
    applyPropertyValuesprotectedvoidapplyPropertyValues(StringbeanName,BeanDefinitionmbd,BeanWrapperbw,PropertyValuespvs){ if(pvs.isEmpty()){ ret......
  • 【python】Error:'int' object is not callable
    1、问题rpc接口在调用的时候报错   2、原因这个报错的意思就是int转换时,转换的对象不能调用本次错误,主要是因为int(obj.a)写成了int(obj.a()),导致属性获取失败......
  • Locally Managed Tablespaces本地管理的表空间
    本地管理的表空间使用位图跟踪表空间本身中的所有扩展信息。本地管理的表空间提供以下好处:快速、并发的空间操作。空间分配和释放修改本地管理的资源(存储在头文件中的......
  • 使用ViewBinding后button按钮失效
    今天学习《第一行代码》时使用ViewBinding代替findViewById(),发现代替以后button无法激活intent,从https://www.jianshu.com/p/86f780f3aabd找到了问题所在问题代码o......
  • 执行x.call.call发生了什么(JS)
    call方法(Function.prototype.call)是用来改变某个方法被调用时的this指向。官方描述:使用一个指定的this值和单独给出的一个或多个参数来调用一个函数。首先,call的常规用......
  • Apply的理解
    Apply和Call一样在函数调用的时候会改变函数内的this指向,让this变成Apply或Call的第一个参数。举个例子:varObjEngineer={name:'程序员努力的一天',a......