函数中的this指向:
函数中的this指向是在函数被调用的时候确定的,也就是执行上下文被创建时确定的。在一个执行上下文中,this由调用者提供,由调用函数的方式来决定。
类数组对象arguments:
arguments只在函数(除了箭头函数)中存在的类数组参数对象,储存了我们传入的所有参数。
一、call
call(this, 参1, 参2, ...)
,第一个参数为this,后面是函数的一系列参数- 当第一个this参数为null、undefind时,默认this指向window
- 函数立即调用
- 原理:实际就是把函数放到call传入的第一个参数上,然后再调用该函数。
- 实现:
/**
* 手写call
* @param {*} context
* @param {...any} args
* @returns
*/
Function.prototype.mycall = function (context, ...args) {
if (typeof this !== "function") {
throw new TypeError("Error");
}
//context不传,默认window
let _this = context || window;
//假如原来的context上存在fn属性会产生冲突,暂存一下。
let temp = null;
if (_this.fn) {
temp = _this.fn;
}
_this.fn = this;
// 调用函数
let res = _this.fn(...args);
if (temp) {
//恢复_this对象上的原来的fn属性
_this.fn = temp;
} else {
//删除_this对象上的fn属性
delete _this.fn;
}
return res;
// 测试
let num = 1;
let obj = {
num: 2,
fn: "this is obj.fn",
};
function test(a, b, c, d) {
let num = 1;
console.log(this.num, "test参数", a, b, c, d);
}
test(4, 3, 2, 1);
// 调用myCall函数
test.mycall(obj, 4, 3, 2, 1);
// 检查obj本身的fn是否被修改
console.log(obj.fn);
};
注意:这个 undefind,是由于我使用 let 声明的变量。var 命令和 function 命令声明的全局变量,依旧是顶层对象的属性。let 命令、const 命令、class 命令声明的全局变量,不属于顶层对象的属性,而是在一般声明环境 declsEnv
中。
从ES6开始,全局变量和顶层对象的属性开始分离,这意味着使用let和const声明的全局变量不再属于顶层对象的属性,如window对象。这是为了提供更好的模块化和封装,避免全局命名空间的污染。
二、apply
applyl(this, arr)
,第一个参数为this,第二个参数是一个参数数组- 当第一个this参数为null、undefind时,默认this指向window
- 函数立即调用
- 原理:和call类似,区别就是参数不同,call方法接受的参数是一个参数列表,而apply接受的是一个包含多个参数的数组。
- 实现:
// 和myCall的不同之处1:参数
Function.prototype.myApply=function(context){
if(typeof this!== 'function'){
throw new TypeError('type error')
}
console.log("myApply", arguments, context, this);
let _this = context || window;
let temp = null;
if (_this.fn) {
temp = _this.fn;
}
_this.fn = this;
let res;
// 判断是否存在第二个参数
if (arguments[1]) {
res = _this.fn(...arguments[1]);
} else {
res = _this.fn();
}
// 删除context对象上的fn属性
if (temp) {
_this.fn = temp;
} else {
delete _this.fn;
}
return res;
}
// 测试
let num = 1;
let obj = {
num: 2,
fn: "this is obj.fn",
};
function test(a, b, c, d) {
let num = 1;
console.log(this.num, "test参数", a, b, c, d);
}
test(4, 3, 2, 1);
// 调用myCall函数
test.myApply(obj, [4, 3, 2, 1]);
// 检查obj本身的fn是否被修改
console.log(obj.fn);
结果:
三、bind
bind(this, 参1, 参2, ...)
,第一个参数为this,后面是函数的一系列参数- 当第一个this参数为null、undefind时,默认this指向window
- bind方法的返回值是函数,不会立即调用
- 原理:(1)bind返回的函数作为构造函数使用的时候,bind绑定的this会失效,到那时参数有效。(2)如何判断bind 是正常使用还是当构造函数,根据this。当为构造函数时,this指向实例对象(this的prototype在该构造函数上)
- 实现:
Function.prototype.myBind = function (_this) {
if (typeof this !== "function") {
throw new TypeError("_this must be a function");
}
//获取参数
let args0 = [...arguments].slice(1);
//保存this,如果作为构造函数使用,此时this会指向实例
let that = this;
let context = _this||window;
return function Fn(...args) {
// 如果是new的形式来使用绑定函数的
if (this instanceof Fn) {
return new that(...args0, ...args);
} else {
return that.call(context , ...args0, ...args);
}
};
};
//测试:
function Point(x, y) {
this.x = x;
this.y = y;
}
// 情况1:正常调用bind函数
let testObj = {};
let MyPoint = Point.myBind(testObj, 0);
MyPoint(1);
console.log(testObj);
//情况2:bind返回的函数作为构造函数
let newObj = new MyPoint(2);
console.log(newObj);
结果:
四、区别:
1、相同点:
(1) call、apply、bind 都有改变this指向的作用。
(2)都可以给参数传参
2、不同点:
(1)call、bind 的第二个、后续参数是多个;而apply接收的第二参数是数组。
(2)call、apply会立即执行参数;而bind不会立即执行,得再调用才能执行。
参考:
1、https://blog.csdn.net/weixin_51472145/article/details/132566180
2、https://blog.csdn.net/weixin_40856652/article/details/124293144?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522171348813116800178533534%2522%252C%2522scm%2522%253A%252220140713.130102334…%2522%257D&request_id=171348813116800178533534&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2alltop_positive~default-1-124293144-null-null.142v100pc_search_result_base1&utm_term=call%E3%80%81apply%E5%92%8Cbind%E7%9A%84%E5%8C%BA%E5%88%AB&spm=1018.2226.3001.4187
3、https://blog.csdn.net/sinat_41904410/article/details/104396112