定义
先来看下call()方法,在mdn中的定义:
call() 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。(其实就是改变函数的this指向,并且调用该函数)
语法
Function.call(thisObj, res1, res2, …);
call方法传递两个参数,第一个参数传递的是所要改变指向的值(当该函数处于非严格模式下,则指定为 null 或 undefined 时会自动替换为指向全局对象,原始值会被包装。),后面是所要传递的参数(参数可以为多个)。
返回值
返回调用函数的返回值。若该函数没有返回值,则返回 undefined。
使用
首先看下传入引用类型的值;如:
let myName = {
nameStr: 'dahua'
}
function sayName(res, res2) {
console.log(this);
console.log(this.nameStr);
console.log(res, res2);
}
sayName();// window undefined
sayName.call(myName, '我是第一个参数1', '我是第一个参数2'); // {nameStr: "dahua"} dahua 我是第一个参数1
sayName.call([]); // [] undefined
sayName.call(function() {}); // function() {} undefined
上述代码可以看到,当直接调用时this指向window。当第一个参数为引用类型值时指向这个参数。接下来我们看下当传入普通类型的值,如:
function sayName() {
console.log(this);
console.log(this.nameStr);
}
sayName.call(1); // Number {1} undefined
sayName.call('2'); // String {"2"} undefined
sayName.call(true); // Boolean {true} undefined
sayName.call(Symbol(1)); // Symbol {Symbol(1)} undefined
以上代码可以看出,当传入普通数据类型时this指向的是普通值的包装对象。我们在来看下当第一个参数传入空值时,this的指向。如:
function sayName(res) {
console.log(this);
console.log(this.nameStr);
}
sayName.call(''); // String {""} undefined
sayName.call(undefined); // Window undefined
sayName.call(null); // Window undefined
sayName.call(NaN); // Number {NaN}
上述代码可以看到,当传入空字符串时,当做字符类型进行调用,而传入undefined和null时this就指向了window。
总结:
1、 第一个参数传入为引用类型的值时,直接将this指向该值。
2、 当第一个参数传入的是普通类型的值时,会将this指向该值的包装类
3、 当传入的值为undefined和null时,this指向window(严格模式下指向)
模拟实现自己的call方法
接下来就根据上述的使用规则模拟自己的call方法
思路:
核心就是根据this的指向规则。(this详细指向规则可参开这篇文章--> 关于this的解析)
步骤:
1、 因为是所有函数的方法,所以直接在原型上创建一个方法
2、 先判断传入的第一个参数thisArg的类型,进行分别处理
3、给thisArg添加一个属性,并把this赋值给它
4、调用该方法,并将参数传入进去
5、删除改thisArg创建的属性
6、返回函数调用的返回值
代码如下:
// 因为是所有函数都可以访问的方法,所以挂在Function的原型上
Function.prototype.myCall = function(thisArg, ...res) {
// 判断第一个参数thisArg是否是undefined或者null,是的话就赋值给window
if (typeof thisArg === 'undefined' || thisArg === null) {
thisArg = window;
}
// 对传入的值thisArg就行包装
thisArg = Object(thisArg);
// 给传入的值thisArg创建属性,为防止thisArg已经有对应的对应进行覆盖,所以使用Symbol创建唯一值
const keyFn = Symbol('__myKeyFn__');
// 此时的this指向调用myCall方法的函数,将它指向thisArg[keyFn]。
// 这里也就是利用了this的指向规则
thisArg[keyFn] = this;
// 调用该方法传入参数
const reasult = thisArg[keyFn](...res);
// 删除thisArg创建的属性
delete thisArg[keyFn];
// 返回函数的返回值
return reasult;
}
测试
上面模拟实现了自己的call方法,下面就测试一下功能是否正常:
let myName = {
nameStr: 'dahua'
}
function sayName(res) {
console.log(this);
console.log(this.nameStr);
console.log(res);
}
sayName.myCall(''); // String {"", Symbol(__myKeyFn__): ƒ} undefined undefined
sayName.myCall(undefined, '1'); // Window undefined '1'
sayName.myCall(null); // Window undefined undefined
sayName.myCall(NaN); // Number {NaN, Symbol(__myKeyFn__): ƒ} undefined undefined
sayName.myCall(myName, '我是第一个参数1', '我是第一个参数2'); // {nameStr: "dahua", Symbol(__myKeyFn__): ƒ} dahua 我是第一个参数1
sayName.myCall([]); // [Symbol(__myKeyFn__): ƒ] undefined undefined
sayName.myCall(function() {}); // function() {} undefined undefined
sayName.myCall(1); // Number {1, Symbol(__myKeyFn__): ƒ} undefined undefined
sayName.myCall('2'); // String {"2", Symbol(__myKeyFn__): ƒ} undefined undefined
sayName.myCall(true); // Boolean {true, Symbol(__myKeyFn__): ƒ} undefined undefined
sayName.myCall(Symbol(1)); // Symbol {Symbol(1), Symbol(__myKeyFn__): ƒ} undefined undefined
测试结果和预期结果一致。(注意: 只是模拟了非严格模式下的实现)
网友评论