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)()
- 若
xx
为null
或undefined
- 则严格模式下,
this
为window
- 非严格模式下,
this
分别为null
和undefined
"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
为基础值类型- 严格模式下,
this
为xx
- 非严格模式下,
this
为xx
所对应的引用值类型
"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