前端开发人员在进阶过程中总会遇到一个疑难杂症:Javascript的this
this到底指向什么哪个对象啊?
要爆炸了有木有?
准备了几个测试,快来看看你能否完全答对输出?
测试前先背下口诀
姜浩五大定律
① 通过函数名()直接调用:this指向window;
② 通过对象.函数名()调用的:this指向这个对象;
③ 函数作为数组的一个元素,通过数组下标调用的:this指向这个数组;
④ 函数作为window内置函数的回调函数调用:this指向window,setTimeout,setInterval等……;
⑤ 函数作为构造函数,用new关键字调用时:this指向新new出的对象。
对于this指向谁,我们记住一句就行:谁最终调用函数,this就指向谁!
记住:
① this指向的永远只可能是对象!
② this指向谁,永远不取决于this写在哪!而是取决于函数在哪调用!!!
来猜猜this的输出吧
1. 作为普通函数调用
// 在非严格模式下
var name = 'hello';
function f1(){
console.log(this);
console.log(this.name);
name = this.name + 'world'
}
f1();
console.log(this.name);
// 在严格模式下
function f2() {
"use strict";
console.log(this);
}
f2();
展开查看答案
答案:window hello helloworld undefined
// 在非严格模式下
var name = 'hello';
function f1(){
console.log(this); // window
console.log(this.name); // hello
name = this.name + 'world'
}
f1();
console.log(this.name); // helloworld
// 在严格模式下
function f2() {
"use strict";
console.log(this); // undefined
}
f2();
解析:
-
直接调用的情况下,不论是严格模式还是非严格模式,this的指向都是全局对象,在浏览器中这个值是window对象。
-
一个普通的函数调用时,在非严格模式下this指向window或global对象。严格模式下,this指向undefined。
2.作为对象的方法调用
var name = 'globalName'
var obj = {
name : 'Yushia',
getName : function(){
console.log(this.name);
}
}
obj.getName();
var fun2 = obj.getName;
fun2();
展开查看答案
答案:Yushia globalName
var name = 'globalName'
var obj = {
name : 'Yushia',
getName : function(){
console.log(this.name);
}
}
obj.getName(); // Yushia
var fun2 = obj.getName;
fun2(); //globalName
解析:
-
当函数作为一个对象里的方法被调用,this 指向该对象
-
把对象的方法赋值给一个变量,再调用这个变量,此时 this 指向了全局对象。给 fun2 赋值,其实是相当于:
var fun2 = function(){
console.log(this);
}
可以看出,此时的 this 已经跟 obj 没有任何关系了。这时 fun2 是作为普通函数调用。
对象多层嵌套
function foo(){
console.log(this.name)
}
var shiny={
name:'shiny',
foo:foo
}
var red={
name:'red',
obj:shiny
}
red.obj.foo()
展开查看答案
答案:shiny
function foo(){
console.log(this.name)
}
var shiny={
name:'shiny',
foo:foo
}
var red={
name:'red',
obj:shiny
}
red.obj.foo()//shiny
解析:
不管你的对象嵌套多深,this只会绑定为直接引用该函数的地址属性的对象
3. 作为构造器调用
无返回值
function Person(n, a){
this.name = n;
this.age = a;
console.log(this);
}
var obj = new Person("Yushia", 18);
console.log(obj.name);
Person("Yushia", 18);
展开查看答案
答案:Person {name: "Lily", age: 18} Window
function Person(n, a){
this.name = n;
this.age = a;
console.log(this);
}
//作为构造函数使用
var obj = new Person("Yushia", 18); //this为当前对象 Person {name: "Lily", age: 18}
console.log(obj.name); // Yushia
//作为普通函数使用
Person("Yushia", 18); //Window
解析: js中没有类,但是可以从构造器中创建对象,并提供了 new 运算符来进行调用该构造器。构造器的外表跟普通函数一样,大部分的函数都可以当做构造器使用。当构造函数被调用时,this 指向了该构造函数实例化出来的对象。
- 用new创建对象的时候调用了构造函数。
- 构造函数和普通函数的区别在于调用方式,而不是定义方式。
- new关键字改变了函数内this的指向,使其指向刚创建的对象。
有返回值--返回一个对象
function Person(n, a){
this.name = n;
this.age = a;
return {
name: "Lucy",
};
}
var obj = new Person("Yushia", 18);
Person("Yushia", 18);
console.log(obj.name);
console.log(obj.age);
展开查看答案
答案:Lucy undefined
function Person(n, a){
this.name = n;
this.age = a;
return {
name: "Lucy",
};
}
var obj = new Person("Yushia", 18);
console.log(obj.name); // Lucy 如果构造函数显式的返回一个对象,那么 this 则会指向该对象。
console.log(obj.age); // undefined
解析: 如果构造函数显式的返回一个对象,那么 this 则会指向该返回对象。
有返回值--返回基本数据类型非对象
function Person(n, a){
this.name = n;
this.age = a;
return "Lucy";
}
var obj = new Person("Yushia", 18);
Person("Yushia", 18);
console.log(obj.name);
console.log(obj.age);
展开查看答案
答案:Yushia 18
function Person(n, a){
this.name = n;
this.age = a;
return "Lucy"; //返回一个数据类型
}
var obj = new Person("Yushia", 18);
console.log(obj.name); // Yushia
console.log(obj.age); // 18
解析: 构造函数的返回值如果是基本数据类型,那返回值和得到的对象无关
4. bind() 、apply() 、call() 方法改变调用函数内的this的值
var name = "小王",age=18;
var obj = {
name: '小明',
age:20,
myFun:function(province,city){
console.log( this.name + "年龄" + this.age +",来自" + province + "省" + city +"市" ) ;
}
}
var db = {
name:"Yushia",
age:18
}
obj.myFun.call(db,'福建','福州');
obj.myFun.apply(db,['福建','福州']);
obj.myFun.bind(db,'福建','福州')();
obj.myFun.bind(db,['福建','福州'])();
展开查看答案
答案:
var name = "小王",age=18;
var obj = {
name: '小明',
age:20,
myFun:function(province,city){
console.log( this.name + "年龄" + this.age +",来自" + province + "省" + city +"市" ) ;
}
}
var db = {
name:"Yushia",
age:18
}
obj.myFun.call(db,'福建','福州'); // Yushia年龄18,来自福建省福州市
obj.myFun.apply(db,['福建','福州']); // Yushia年龄18,来自福建省福州市
obj.myFun.bind(db,'福建','福州')(); // Yushia年龄18,来自福建省福州市
obj.myFun.bind(db,['福建','福州'])(); //Yushia年龄18,来自福建,福州省undefined市
解析: 从上面四个结果不难看出:
call 、bind 、 apply 这三个函数的第一个参数都是 this 的指向对象,第二个参数差别就来了:
call 的参数是直接放进去的,第二第三第 n 个参数全都用逗号分隔,直接放到后面 obj.myFun.call(db,'成都', ... ,'string' )。
apply 的所有参数都必须放在一个数组里面传进去 obj.myFun.apply(db,['成都', ..., 'string' ])。
bind 除了返回是函数以外,它 的参数和 call 一样。
当然,三者的参数不限定是 string 类型,允许是各种类型,包括函数 、 object 等等!
5. setTimeout & setInterval
function Person() {
this.age = 0;
setTimeout(function() {
console.log(this);
}, 1000);
}
var p = new Person();
function Person2() {
this.age = 0;
setTimeout((function() {
console.log(this);
}).bind(this), 1000);
}
var p = new Person2();
展开查看答案
答案:
function Person() {
this.age = 0;
setTimeout(function() {
console.log(this);
}, 1000); //1秒后返回 window 对象
}
var p = new Person();
function Person2() {
this.age = 0;
setTimeout((function() {
console.log(this);
}).bind(this), 1000); //1秒后返回构造函数新生成的对象 Person{age: 0}
}
var p = new Person2();
解析:
- 对于延时函数(setTimeout & setInterval)内部的回调函数的this指向全局对象window
- 可以通过bind方法改变其内部函数的this指向
6. DOM 事件处理函数中的 this & 内联事件中的 this
<input type="button" value="Click Me" onclick="consloe.log(this.value)">
<input type="button" id="myBtn" >
<script>
var btn=document.getElementById('myBtn');
btn.onclick=function(){
console.log(this.id);
}
</script>
展开查看答案
答案:
<!-- 输出"Click Me" -->
<input type="button" id="myBtn" value="Click Me" onclick="consloe.log(this.value)">
<script>
var btn=document.getElementById('myBtn');
btn.onclick=function(){
console.log(this.id); //"myBtn"
}
</script>
解析: 其 this 指向触发该事件的元素
7. 箭头函数中的 this
function Person() {
this.age = 0;
setTimeout(() => {
console.log(this)
}, 1000);
}
var p = new Person();
var obj = {
a: () => {
console.log(this)
}
}
obj.a()
var obj2 = {
a: () => {
console.log(this)
}
}
obj2.a.call('123')
展开查看答案
答案:
function Person() {
this.age = 0;
setTimeout(() => {
console.log(this)
}, 1000);
}
var p = new Person(); // Person{age:0}
var obj = {
a: () => {
console.log(this)
}
}
obj.a() // window
var obj2 = {
a: () => {
console.log(this)
}
}
obj2.a.call('123') // 打出来的结果依然是window对象
解析:
- 箭头函数会默认绑定外层this的值,所以在箭头函数中this的值和外层的this是一样的。 2.不能用call方法修改里面的this
8. 闭包中的 this
var name = "window";
var obj = {
name: "Yushia",
say:function(){
var that = this;
console.log(this.name);
return function(){
console.log(this.name);
console.log(that.name);
}
}
}
obj.say()();
obj.say().call(obj);
展开查看答案
答案:
var name = "window";
var obj = {
name: "Yushia",
say:function(){
var that = this;
console.log(this.name);
return function(){
console.log(this.name);
console.log(that.name);
}
}
}
obj.say()(); // Yushia window Yushia
obj.say().call(obj); // Yushia Yushia Yushia
解析:
因为闭包并不属于这个对象的属性或方法。所以在闭包中的this是指向window.
- 闭包中的this指向的是window对象,this.name=window.name
- obj.say().call(obj) 这里通过call方法把this的指向换成了obj
- 在方法内部改变this指向,对象中的say方法中this是指向obj,使用that代替this,在闭包函数中写成that.name 那么结果指向obj