this的指向,始终坚持一个原理:this永远指向最后调用它的那个对象。下面我们来看几个简单的例子:
例1:在下面的例子中,非严格模式下,this.name打印出的是橘猫吃不胖,因为最后调用fun()的地方是全局对象window,相当于window.fun(),所以this指向了Window。
var name = "橘猫吃不胖";
function fun() {
var name = "小明";
console.log(this.name); // 橘猫吃不胖
console.log("fun内this:", this); // fun内this:Window
}
fun();
console.log("fun外this:" + this); // fun外this:Window
例2:在下面这个例子中,函数fun是被对象obj调用的,所以this的指向就是obj,this.name就是对象obj中的name的值。
var name = "橘猫吃不胖";
var obj = {
name: "小明",
fun: function () {
console.log(this.name); // 小明
}
}
obj.fun();
例3:下面的例子中,虽然对象obj中的fun函数被赋值给了a,但是调用函数a()的是全局对象Window,因此this.name就是橘猫吃不胖。
var name = "橘猫吃不胖";
var obj = {
name: "小明",
fun: function () {
console.log(this.name); // 橘猫吃不胖
}
}
var a = obj.fun;
a();
例4:在箭头函数中,this引用的是定义箭头函数的上下文。下面的例子中,箭头函数是在Window上定义的,因此箭头函数的this会保留定义该函数的上下文。
let name = "橘猫吃不胖";
let obj = {
name: "小明"
};
let fun = () => console.log(this.name);
fun(); //橘猫吃不胖
obj.fun = fun;
obj.fun(); // 橘猫吃不胖
所以this指向的场景总结如下:
1、全局单独使用的
this,与普通函数中的this,都指向全局对象Window,严格模式下,this指向undefined;
2、调用对象中的方法时,this指向该对象;
3、构造函数中的this,指向了该构造函数的实例对象;
4、箭头函数中的this,指向了定义箭头函数的上下文;
使用call、apply、bind函数可以改变this的指向。
下面会详细说明这三个函数的运用与实现。
call()方法接收一些参数:第一个参数是函数内this的值,剩下的参数是其他的参数。它以指定的this值调用函数,即设置调用函数时函数体内this对象的值。用法如下:
函数.call(this值, 参数1, 参数2, 参数3......);
例如:
fun.call(this, 10, 20, 30);
示例代码:
let obj = { num: 10 };
function sum(num1, num2) {
console.log(this.num + num1 + num2);
}
sum.call(obj, 10, 20); // 40
在上面的例子中,sum使用了call方法,使this从原本的Window变成了obj对象,那么sum函数内的this.num就是obj.num就是10,因此最后输出40。
首先,我们在调用call方法时,是直接在函数上调用的,说明call方法定义在Function的原型对象上,第一个参数是其内部this指向的对象,其余参数用rest参数接收:
// obj是内部this要指向的对象
Function.prototype.myCall = function (obj, ...args) {
// ...
}
接下来,判断调用该方法的是不是一个函数,如果不是函数,应该报错:
// obj是内部this要指向的对象
Function.prototype.myCall = function (obj, ...args) {
// 如果调用该方法的不是函数,即this不是function,报错
if (typeof this !== "function") return new TypeError("TypeError");
}
判断是否传入了obj,如果没有传入,应该让其指向Window:
// obj是内部this要指向的对象
Function.prototype.myCall = function (obj, ...args) {
// 如果调用该方法的不是函数,即this不是function,报错
if (typeof this !== "function") return new TypeError("TypeError");
// 如果没有传入obj,那么this要指向Window
obj = obj ? obj : window;
}
设置result存放最终的结果,给obj定义fun函数,使其等于this,也就是调用myCall方法的函数,再执行obj.fun,并传入参数args,结果赋值给result:
// obj是内部this要指向的对象
Function.prototype.myCall = function (obj, ...args) {
// 如果调用该方法的不是函数,即this不是function,报错
if (typeof this !== "function") return new TypeError("TypeError");
// 如果没有传入obj,那么this要指向Window
obj = obj ? obj : window;
// 存放最终的结果
let result = null;
// 将调用myCall的函数赋值给obj.fun
obj.fun = this;
// 调用obj.fun,使this指向obj
result = obj.fun(...args);
}
最后,我们将obj上新增的方法fun删去,返回结果result即可,整体实现代码如下:
// obj是内部this要指向的对象
Function.prototype.myCall = function (obj, ...args) {
// 如果调用该方法的不是函数,即this不是function,报错
if (typeof this !== "function") return new TypeError("TypeError");
// 如果没有传入obj,那么this要指向Window
obj = obj ? obj : window;
// 存放最终的结果
let result = null;
// 将调用myCall的函数赋值给obj.fun
obj.fun = this;
// 调用obj.fun,使this指向obj
result = obj.fun(...args);
//删除obj上新增的方法fun
delete obj.fun;
// 返回结果
return result;
}
apply()与call()的不同之处在于第二个参数的形式。
apply()方法接收两个参数:函数内this的值和一个参数数组。第二个参数可以是数组的字面量,也可以是Array的实例对象,但也可以是arguments对象。它也是以指定的this值调用函数,即设置调用函数时函数体内this对象的值。用法如下:
函数.apply(this值, Array);
例如:
fun.apply(this, ["张三", "李四"]); // 第二个参数是数组字面量表示方式
fun.apply(this, new Array("张三", "李四")); // 第二个参数是Array的实例对象
示例代码:
let obj = { num: 10 };
function sum(num1, num2) {
console.log(this.num + num1 + num2);
}
sum.apply(obj, [10, 20]); // 40
在上面的例子中,sum使用了apply方法,使this从原本的Window变成了obj对象,那么sum函数内的this.num就是obj.num就是10,因此最后输出40。
同样,调用apply方法时,是直接在函数上调用的,说明apply方法定义在Function的原型对象上,第一个参数是其内部this指向的对象,第二个参数用args参数接收:
// obj是this需要指向的对象
Function.prototype.myApply = function (obj, args) {
// ...
}
判断是否是函数调用了myApply,不是则报错:
// obj是this需要指向的对象
Function.prototype.myApply = function (obj, args) {
// 如果不是函数调用myApply,报错提示
if (typeof this !== "function") return new TypeError("TypeError");
}
判断是否传入了obj,如果没有传入,应该让其指向Window:
// obj是this需要指向的对象
Function.prototype.myApply = function (obj, args) {
// 如果不是函数调用myApply,报错提示
if (typeof this !== "function") return new TypeError("TypeError");
// 是否传入了obj,不是则指向window
obj = obj ? obj : window;
}
设置result存放最终的结果,给obj定义fun函数,使其等于this,也就是调用myApply方法的函数,再执行obj.fun,并传入参数args,结果赋值给result:
// obj是this需要指向的对象
Function.prototype.myApply = function (obj, args) {
// 如果不是函数调用myApply,报错提示
if (typeof this !== "function") return new TypeError("TypeError");
// 是否传入了obj,不是则指向window
obj = obj ? obj : window;
// 存放最终的结果
let result = null;
// 将调用myApply的函数this赋值给obj.fun
obj.fun = this;
// 调用obj.fun,使this指向obj
result = obj.fun(...args);
}
最后,我们将obj上新增的方法fun删去,返回结果result即可,整体实现代码如下:
// obj是this需要指向的对象
Function.prototype.myApply = function (obj, args) {
// 如果不是函数调用myApply,报错提示
if (typeof this !== "function") return new TypeError("TypeError");
// 是否传入了obj,不是则指向window
obj = obj ? obj : window;
// 存放最终的结果
let result = null;
// 将调用myApply的函数this赋值给obj.fun
obj.fun = this;
// 调用obj.fun,使this指向obj
result = obj.fun(...args);
// 删掉obj.fun
delete obj.fun;
return result;
}
bind()方法会创建一个新的函数实例,其this值会被绑定到传给bind()的对象。它的返回值也是方法,接受的第一个参数是一个对象,其余参数会作为新的函数的参数。用法如下:
函数.bind(this值, 参数1, 参数2, 参数3......);
例如:
fun.bind(this, "aaa", "bbb", "ccc");
示例代码:
let obj = { num: 20 };
function num() {
console.log(this.num);
console.log(Math.max(...arguments));
}
let pnum = num.bind(obj, 2, 3, 5, 7, 68, 8);
pnum(); // 20 68
在上面的代码中,num使用了bind方法,但是该函数并未立即被调用,而是使其this从原本的Window变成了obj对象,返回了一个函数并赋值给了pnum,所以调用pnum函数,this.num就是obj.num就是20,其余的参数执行后会输出68。
调用bind方法时,是直接在函数上调用的,说明bind方法定义在Function的原型对象上,第一个参数obj是其内部this指向的对象,第二个参数用args参数接收:
Function.prototype.myBind = function (obj, ...args) {
// ...
}
判断是否是函数调用了myBind,不是则报错:
Function.prototype.myBind = function (obj, ...args) {
// 如果不是函数调用了myBind,则报错
if (typeof this !== "function") return new TypeError("TypeError");
}
判断是否传入了obj,如果没有传入,应该让其指向Window:
Function.prototype.myBind = function (obj, ...args) {
// 如果不是函数调用了myBind,则报错
if (typeof this !== "function") return new TypeError("TypeError");
// 判断是否传入了obj,如果没有传入,应该让其指向Window
obj = obj ? obj : window;
}
将调用myBind的函数赋值给fun,并在myBind函数最后返回一个函数,该函数就是fun使用了apply方法:
Function.prototype.myBind = function (obj, ...args) {
// 如果不是函数调用了myBind,则报错
if (typeof this !== "function") return new TypeError("TypeError");
// 判断是否传入了obj,如果没有传入,应该让其指向Window
obj = obj ? obj : window;
// 将调用myBind的函数存放起来
let fun = this;
// 返回一个函数,并为该函数预留出传参数的空间
return function Fn(...rest) {
return fun.apply(obj, args.concat(rest));
}
}
call和apply的区别:
call方法接受的是若干个参数列表,而apply接收的是一个包含多个参数的数组
bind和apply、call区别:
1、
bind不会立即调用函数,call和apply会立即调用
2、bind与call接收的参数形式相同,第一个都是this要指向的对象,其余的参数是其他参数,apply第一个参数是this要指向的对象,第二个参数是数组