很多初学JavaScript的同学,都找不准函数或者方法中的this到底指向谁,其实并没有那么复杂,只需要记住一个口诀就能正确找到this的指向:不管函数或者方法是如何声明的,要看这个函数或者方法最终是谁调用的,**谁最终调用这个函数或方法,那么这个函数或方法中的this就是谁**.下面从函数调用方式着手详细给大家介绍一下这个口诀.
1. 作为普通函数中的this
示例代码
function welcome( ) {
console.log("黑马程序员深圳中心前端与移动开发学科欢迎大家!");
console.log(this);
}
welcome(); //第5行
window.welcome();//第6行
大家都知道写一个全局函数相当于给window对象添加了一个方法,所以第5行调用welcome函数和第6行调用welcome函数本质是一样的,所以welcome函数最终是window对象在调用,所以welcome函数内部的this就指向了window对象.运行结果如下.
2. 作为对象的方法中的this
示例代码
var name = "黑马程序员";
var obj = {
name: "传智播客",
sayHi: function () {
console.log("深圳就在这里,黑马对你不离不弃!");
console.log(this);
}
};
obj.sayHi();
最后1行的sayHi方法是由obj对象点出来调用的,所以sayHi方法中的this指向obj对象.所以输出的是obj对象中的name的值, 结果如下:
3. 综合案例
3.1 案例一
function welcome( ) {
console.log("黑马程序员深圳中心前端与移动开发学科欢迎大家!");
console.log(this);
}
var name = "黑马程序员";
var obj = {
name: "传智播客",
sayHi: function () {
console.log("深圳就在这里,黑马对你不离不弃!"); //第10行
console.log(this);//第11行
}
};
welcome = obj.sayHi; //第15行
welcome(); //第16行 这个时候,这个welcome函数中的this指向谁呢?
这里要搞明白的是第15行代码的含义, 这句话的作用是把obj对象里面的sayHi函数的函数体赋值给welcome, 所以在第16行调用welcome函数的时候,执行的代码其实是obj中sayHi的函数体. 那么执行的就是第10、11行, 那么这个this是谁呢? 其实很简单,不要看这个方法是声明在obj对象里面的, 要看这个方法是在第16行调用,是window调用的,所以此时这个this是window,输出的结果就是window的name属性,就是那个全局name的值, 结果如下:
3.2 案例二
function welcome( ) {
console.log("黑马程序员深圳中心前端与移动开发学科欢迎大家!"); //第2行
console.log(this); //第3行
}
var name = "黑马程序员";
var obj = {
name: "传智播客",
sayHi: function () {
console.log("深圳就在这里,黑马对你不离不弃!"); //第10行
console.log(this);//第11行
}
};
//变化在这里.....
obj.sayHi = welcome; //第15行
obj.sayHi(); //第16行
这个时候,obj.sayH函数中的this指向谁呢?这里同样要搞明白第15行代码的含义,这句话的意思是把welcome的函数体赋值给obj对象的sayHi函数, 所以在第16行调用obj对象的sayHi函数实际上执行的代码是第2行第3行, 那第3行的this是谁呢? 此时不要看这个welcome是怎么声明的, 要看这个代码是在第16行调用的,是obj对象点出来调用的,所以this指向obj.结果如下:
3.3 案例三
var name = "黑马程序员";
var p1 = {
name: "深圳中心",
dog: {
name: "旺财",
sayHi: function(){
console.log(this);
}
}
}
p1.dog.sayHi();
最后一行这里给人的错觉好像sayHi方法是p1点出来调用的,事实这种想法是错误的,p1点出来的只是dog对象, sayHi方法还是由dog点出来调用的,所以sayHi方法中的this还是指向dog对象.所以输出的结果如下:
4. 构造函数中的this
4.1 直接调用构造函数
function Student(name, age){
this.name = name; //第2行
this.age = age;//第3行
}
var s1 = Student("黑马",12); //第6行
console.log(s1);
console.log(window.name);
console.log(window.age);
大家注意看第6行,调用Student函数的时候没有用new关键字,那么这就意味着Student函数是window点出来调用的,所以Student函数中的this就是window,那么第2行第3行就是在给window添加name和age属性,值分别是黑马和12. 然后函数内部没有写return关键字那默认返回值是undefined.所以s1接受到的是undefined. 整个输出结果如下:
4.2 使用new调用构造函数
function Student(name, age){
this.name = name; //第2行
this.age = age;//第3行
}
var s1 = new Student("黑马",12); //第6行
console.log(s1); //第7行
大家都知道new关键字会创建一个对象,并且会把构造函数中的this指向这个对象,并且还会把这个对象自动返回, 那么执行完第6行代码, s1就是那个new关键字创建并返回的对象.在第2行第3行也已经给这个对象添加了name和age属性. 所以上面的代码执行结果如下:
4.3 为构造函数手动return后的情况
这里再来看看如果给构造函数添加了return关键字会如何,代码如下:
function Student(name, age) {
this.name = name; //第2行
this.age = age;//第3行
console.log(this);
return [10, 20, 30];
}
var s1 = new Student("黑马", 12); //第8行
console.log(s1); //第9行
说到这里大家就要注意,因为new关键字会自动帮我们返回他创建的对象,所以一般在构造函数中是不用写return返回值的, 那万一构造函数中就是写了return返回值会怎么样呢? 这个时候就要看return关键字后面返回的值是什么类型的,如果返回的是数值类型/字符串类型/布尔类型/null/undefined,那么会忽略这个返回值. 如果返回的值不是这些类型的,比如是一个数组或者一个对象,那么这个返回值会覆盖原来new关键字自动返回的那个对象. 所以上面代码运行的结果如下:
但是构造函数中的this还是new关键字创建的那个对象(验证看第10行输出的结果), 只是这个对象返回的时候被return关键字返回的数组给覆盖了,所以s1接受不到而已.
5. 修改函数中this的默认指向
以上我们讨论的是,默认情况下,函数中this的指向。实际上JavaScript提供了一套机制,允许我们对函数中的this指向进行修改,可以让函数中的this指向我们想要让他指向的对象。
var obj = {
name: "张三"
};
function welcome() {
console.log(this);
}
welcome();
大家现在都知道上述代码中welcome函数中this指向window对象, 输出的是window, 但是我们现在想让welcome函数中的this指向obj对象,那应该怎么做呢? 肯定是不能直接修改函数中this的指向的,直接修改会报错, 因为this被JavaScript设计为只读,代码如下:
var obj = {
name: "张三"
};
function welcome() {
this = obj; //这句代码在执行的时候,会报错,无法执行
console.log(this);
}
welcome();
这个时候,要实现这个效果,要想让welcome函数中的this指向obj, 就要使用函数上下文调用模式,比如call、 apply、 bind。
var obj = {
name: "张三"
};
function welcome() {
console.log(this);
}
welcome.call(obj); //使用函数上下文调用模式,想要让welcome中的this指向谁,就把谁传入.
这个时候,welcome函数被调用,但是welcome函数内部的this就指向obj对象了。
关于函数上下文的调用模式,具体内容,我们将在后续章节继续为大家介绍,欢迎大家订阅关注。