JavaScript apply 的原理
什么是 apply 方法?
Function.prototype.call 允许调用一个函数,显式的指定执行上下文
js
function.apply(thisArg, argsArray)说明:
thisArg: 执行时传入的this的值。如果传入null或undefined, 在非严格模式下this会指向全局对象(浏览器中是window,Node.js 中 是global);严格模式下,this就是传入的值。argsArray是传递给函数的参数(一组参数,通常是数组)。
判断数据类型示例
js
Object.prototype.toString.apply(null).slice(8, -1) // 'Null'
Object.prototype.toString.apply([]).slice(8, -1) // 'Array'apply 的实现原理
js
Function.prototype.myApply = function(thisArg, argsArray) {
// 非函数调用处理
if (typeof this !== 'function') {
throw new TypeError("myApply must be called on a function");
}
// 判断是否 null 或者 undefined
if (thisArg == null) {
thisArg = typeof window !== 'undefined' ? window : global;
}
// 确保 thisArg 是对象
thisArg = Object(thisArg)
// 处理参数
argsArray = argsArray || []
// 生成临时函数的key, 使用 Symbol 确保不和其他属性撞车
const fnKey = Symbol('tempFn')
// 将函数调用时的 this 绑定到 thisArg 上,这时 this 指向的是要执行函数
thisArg[fnKey] = this
// 执行函数并拿到返回结果
const result = thisArg[fnKey](...argsArray)
// 删除临时属性
delete thisArg[fnKey]
// 返回函数执行结果
return result
}实现过程说明
- 处理
thisArg- 如果
thisArg是null或者undefined时,将其设置为全局对象 - 使用
Object将基本数据类型(数字、字符串)其转成对象,确保可以添加属性
- 如果
- 绑定函数
this是调用myApply的函数(即要执行的函数)- 将临时函数绑定到
thisArg上(使用Symbol确保不会和其他键名冲突)
- 执行函数
- 通过
thisArg[fnKey](...argsArray)执行函数,执行时的this指向了thisArg,达到了切换函数执行时上下文;参数通过拓展运算符...argsArray传递
- 通过
- 清理和返回
- 删除临时属性
- 返回函数执行结果
使用示例
js
const person = { name: 'Alice' }
function greet(greeting, end) {
console.log(`${greeting}, ${this.name}${end}`)
}
greet.myApply(person, ['Hi', '!'])测试 null 上下文
js
const person = { name: 'Alice' }
function greet(greeting, end) {
console.log(`${greeting}, ${this.name}${end}`)
}
greet.myApply(person, ['Hello', '!'])
// null 作为上下文
greet.myApply(null, ['Hi', '?']) // 输出 Hi, ?严格模式下的实现
js
Function.prototype.myApply = function(thisArg, argsArray) {
'use strict';
// 处理参数
argsArray = argsArray || []
// 创建key, 严格模式下,thisArg 无需做强制转换成对象的处理
const fnKey = Symbol('tempFn')
// 临时存储上下文, null 或者 undefined 使用全局对象作为上下文
const context = thisArg ?? globalThis
// 绑定函数
context[fnKey] = this
// 执行函数
const result = context[fnKey](...argsArray)
// 删除临时属性
delete context[fnKey]
// 返回结果
return result
}