函数中的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不传,默认windowlet _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