前言
大家好 , 我是一名在校大二学生 , 今日学习apply的实现机制 , 和大家一起分享 ~
题目分析
JavaScript 中的 apply方法
-
用于调用一个函数
-
并显式地设置 this 的值
-
同时传入一个数组或类数组对象作为参数
apply方法与call方法类似,不同之处在于
- apply接受的是参数数组,
- 而call接受的是参数列表。
我的代码
Function.prototype.myApply = function (thisArg , argsArray){
//1. 确保 this 是一个函数
if(typeof this !== 'function'){
throw new TypeError(this + 'is not a function');
}
// 2.处理thisArg , 如果为null 或者 undefined , 则指向全局对象
thisArg = thisArg ? Object(thisArg) : globalThis;
// 3.创建一个唯一的属性名 , 避免覆盖原有属性
const fnSymbol = Symbol();
// 4. 将函数作为thisArg 的属性
thisArg[fnSymbol] = this ; // 将函数挂载到thisArg上
// 5.调用函数并传入参数数组
const result = thisArg[fnSymbol](...argsArray);
// 6. 删除临时属性
delete thisArg[fnSymbol] ;
return result ;
}
const obj = {
name : 'ganzhibin'
}
function sayHello(age,hobby){
console.log(`hello ${this.name} ,you are ${age} ,your hobby is ${hobby}`);
}
sayHello.myApply(obj,[18,'coding'])
解释代码
逐行解释
-
确保
this是一个函数if (typeof this !== 'function') { throw new TypeError(this + ' is not a function'); }- 为什么这样写:
myApply是Function.prototype的方法,因此this应该是一个函数。如果不是函数,抛出一个TypeError。 - 作用:确保
myApply只能被函数调用,防止错误的调用导致未定义行为。
- 为什么这样写:
-
处理
thisArg, 如果为null或者undefined, 则指向全局对象thisArg = thisArg ? Object(thisArg) : globalThis;- 为什么这样写:根据规范,如果
thisArg为null或undefined,this应该指向全局对象(在浏览器中是window,在 Node.js 中是global)。 - 作用:确保
thisArg在null或undefined时指向正确的全局对象。 - 细节:
Object(thisArg)用于将原始值(如字符串、数字、布尔值)转换为相应的对象包装器。
- 为什么这样写:根据规范,如果
-
创建一个唯一的属性名, 避免覆盖原有属性
const fnSymbol = Symbol();- 为什么这样写:使用
Symbol创建一个唯一的属性名,确保不会覆盖thisArg上已有的属性。 - 作用:避免与
thisArg上的现有属性发生冲突。
- 为什么这样写:使用
-
将函数作为
thisArg的属性thisArg[fnSymbol] = this; // 将函数挂载到 thisArg 上- 为什么这样写:将当前函数(即
this)挂载到thisArg对象上,以便可以通过thisArg[fnSymbol]()调用该函数。 - 作用:实现将函数绑定到指定的
this上下文。
- 为什么这样写:将当前函数(即
-
调用函数并传入参数数组
const result = thisArg[fnSymbol](...argsArray);- 为什么这样写:使用扩展运算符
...将argsArray展开为单独的参数,并调用thisArg[fnSymbol]。 - 作用:确保参数正确传递给函数,并调用该函数。
- 为什么这样写:使用扩展运算符
-
删除临时属性
delete thisArg[fnSymbol];- 为什么这样写:删除之前添加的临时属性,保持
thisArg对象的干净状态。 - 作用:避免在
thisArg上留下不必要的属性,保持对象的整洁。
- 为什么这样写:删除之前添加的临时属性,保持
-
返回结果
return result;- 为什么这样写:返回函数调用的结果。
- 作用:确保
myApply的返回值与原生apply方法一致。
详细解释
-
确保
this是一个函数- 原因:
myApply是Function.prototype的方法,只能被函数调用。 - 替代方案:可以在调用
myApply之前手动检查,但直接在方法内部检查更为合理。
- 原因:
-
处理
thisArg- 原因:根据规范,
thisArg为null或undefined时,this应该指向全局对象。 - 替代方案:可以使用
thisArg || globalThis,但thisArg ? Object(thisArg) : globalThis更加严谨,处理了原始值的情况。
- 原因:根据规范,
-
创建唯一属性名
- 原因:避免与
thisArg上的现有属性发生冲突。 - 替代方案:可以使用随机字符串,但
Symbol更加安全和简洁。
- 原因:避免与
-
将函数作为
thisArg的属性- 原因:实现将函数绑定到指定的
this上下文。 - 替代方案:可以使用其他方法绑定
this,但这种方法最为直接。
- 原因:实现将函数绑定到指定的
-
调用函数并传入参数数组
- 原因:确保参数正确传递给函数,并调用该函数。
- 替代方案:可以使用
Function.prototype.call,但apply更适合处理可变参数。
-
删除临时属性
- 原因:保持
thisArg对象的干净状态。 - 替代方案:如果不删除,可能会留下不必要的属性,但删除可以确保对象的整洁。
- 原因:保持
通过这些步骤,myApply 函数实现了与 JavaScript 内置 apply 方法类似的功能,确保函数能够在指定的 this 上下文中正确调用,并传递参数。
拓展
apply 和 call方法都可以用来调用一个函数,并显式地设置 this 的值。不同之处在于:
- apply 方法接受两个参数:一个是 this 的值,另一个是参数数组。
- call方法接受多个参数:第一个是 this 的值,后续是函数的参数。
function example(a, b) {
console.log(this.name, a, b);
}
const obj = { name: 'ganzhibin' };
example.apply(obj, [1, 2]); // 输出: ganzhibin 1 2
example.call(obj, 1, 2); // 输出: ganzhibin 1 2
Symbol
Symbol是 ES6 引l入的一种新的原始数据类型,表示独一无二的值。使用 Symbol 可以创建唯一的属性名,避免属性名冲突。
const sym1 = Symbol('foo');
const sym2 = Symbol('foo');
console.log(sym1 === sym2); // 输出: false
globalThis
globalThis 是 ES2020 引I入的一个全局对象,它在不同的环境中指向全局对 象。在浏览器中,globalThis 等同于 window,在 Node.js 中等同于 global。
console.log(globalThis === window);// 在浏览器中输出:true
console.log(globalThis === global); // 在 Node.js 中输出:true
要实现 apply方法,我们需要:
1)确保 this 是一个函数。
2)处理thisArg,即调用函数时的this值。
3)处理参数数组,并调用原函数。