JS - this 指东指西,到底指向哪里 ...

287 阅读4分钟

对于初学 JS 的同学来说,可能存在完全搞不懂 this 到底指向哪里,指东指西,指向意料之外的对象。

    —— what's fuckkkk... 你是否也有这种感觉呢?

好了,我们就不要逼叨叨了, 进入主题一起来看看 this 到底指向哪里吧:

this:被谁调用,就指向谁。

如何理解这句话呢?我们稍稍放慢脚步,结合案例一点点进行分析:

  • 案例一:
 var a = 1;
 console.log(a)
 console.log(this.a);  // 1
 console.log(window.a) // 1
 console.log(this == window) //true

案例一分析:

1:全局执行 var a = 1, 相当于给 wiondow 对象添加了 a 属性,并赋值为 1 
2:在全局下执行 console.log(...),此时的 this 就是指向 window 且是同一个对象
  • 案例二
function test() {
  var a = 1;
  console.log(a) // 1
  console.log(this); //window
  console.log(this.a); // undefined
}
test();
console.log(this.a); // undefined

案例二分析:

结合预编译来分析
GO:{
	test: function(){}
}
AO: {
	a: undefined --> 1
}

在哪里调用,就指向哪里
1:全局调用 test 函数,相当于执行了:window.test() 
2:此时 test 函数中的 this 指向 window (在哪里调用,就指向哪里)
3:预编译的信息可以看出,GO 中没有 a 变量 ---> undefined
  • 案例三:
function test() {
   var a = 1;
 	console.log(a) // 1
 	console.log(this); //window
 	console.log(this.a); // undefined
 	function inner() {
   		console.log(this.a); // undefined
   		console.log(this); // window
 	}
 	inner();
}
test();

案例三分析:

结合预编译来分析
GO:{
	test: function(){}
}
AO: {
	a: undefined --> 1
	inner: function() {}
}

在哪里调用,就指向哪里
1:全局调用 test 函数,相当于执行了:window.test() 
2:此时 test 函数中的 this 指向 window
3:预编译的信息可以看出,GO 中没有 a 变量 ---> undefined
4:虽然在 test() 中执行 inner(), 但不要忘记了是 test() 是在全局下被调用的,因此 inner() this 指向 wiondow 
  • 案例四:
var obj = {
	names: '人间富贵花',
  	fn: function() {
   		console.log(this.names) // 人间富贵花
   		console.log(this) //  {name: '人间富贵花, fn: function(){}}
   	}
}
obj.fn()

案例四分析:

结合预编译来分析
GO:{
	obj: undefined ---被赋值为一个对象--> {}
}      
1:被谁调用就指向谁
2: obj对象有上挂载了a 、 fn 两个属性
3:obj.fn()执行,fn 函数中的 this 指向 obj
  • 案例五:(注意
var obj = {
	names: '人间富贵花',
  	fn: function() {
   		console.log(this.names) // 人间富贵花
   		console.log(this) //  {name: '人间富贵花, fn: function(){}}
   	}
}
window.obj.fn()

很多同学看到这里就会认为:函数是在 windon 下调用的, this 为什么不指向 window 呢?

说明: 如果一个函数中有 this , 但是他没有被上一级对象调用,那么 this 指向 window;如果被上一级对象调用,则指向上一级对象。

案例五分析:

1: 案例五和案例四,其实是没有任何区别的,都是在 window 下执行的
2:为什么 window.obj.fn() 中的 this 没有指向 window 呢?
3:函数中有 this ,但是 fn() 被上一级 obj 调用, 那么 this 指向 obj,而不会指向 window

结合案例五的分析我们可以看看以下的特殊情况了

  • 案例六:
var obj = {
      names: "人间富贵花",
      info: {
        skill: "JavaScript",
        fn: function () {
          console.log(this); // fn 函数的上一级 info 对象 {skill: '...', fn:function(){}}
          console.log(this.names); // undefined
          console.log(this.skill); // JavaScript
        }
      }
    };
    obj.info.fn();

案例六分析:

1:obj.info.fn() 等同于 window.obj.info.fn()
2: 被谁调用,层级比较多的情况,存在被上一级对象调用,this 指向上一级对象
  • 案例七:
 var obj = {
      names: "人间富贵花",
      info: {
        skill: "JavaScript",
        fn: function () {
          console.log(this); //window
          console.log(this.names); // undefined
          console.log(this.skill);// undefined
        }
      }
    };
    var test = obj.info.fn;
    test();

哈哈 是不是又懵逼了,为什么:this.names 、this.skill 为 undefined

案例七分析:

1:被谁调用 this 指向谁
2window 下执行 test() 此时函数中的 this 指向 window
3window 下是没有names、skill 属性 
  • 案例八:构造函数
function Person() {
   this.name = "人间富贵花";
   this.skill = "JavaScript";
   console.log(this); // Person{name: '...',skill: '...'}
 }
 var p = new Person();
 console.log(p); // Person{name: '...',skill: '...'}

案例八分析:

1:构造函数 this 指向 我们需要跳出:哪里调用指向哪里的思想
2:通过 new 关键字改变了 this 的指向,将这个 this 指向了实例化后的对象 p
  • 案例九: call、apply
var obj = {
    names: '人间富贵花',
    fn: function(val1, val2) {
       console.log(this.names) 
       console.log(val1 + val2) 
   }
}
var info = obj.fn
info.call(obj, 1, 2)  // '人间富贵花'  3

var test = {
	names: 'RJFGH'
}
info.apply(test, [2, 2]) // 'RJFGH'  4

案例九分析:

1:info: function() {}
2object.call(target, val1, val2...)  object.apply(target, [val1, val2...])  
3:call 方法改变 this 的指向,指向 obj (target),
4:obj.names: '人间富贵花' 
5:info.call(obj, 1, 2) ---> '人间富贵花'  3
6:info.apply(test,[2, 2]) --->  'RJFGH'  4

补充说明:构造函数 return 的问题

function Person1() {
	this.name = '人间富贵花'
 	console.log(this) // Person1:{...}
  	return ''
}
var p1 = new Person1()
console.log(p1.name)  // 人间富贵花
// -----------------------------------------------------------------------------

function Person2() {
	this.name = '人间富贵花'
  	console.log(this) // Person2:{...}
  	return  1
}
var p2 = new Person2()
console.log(p2.name) // 人间富贵花
// -----------------------------------------------------------------------------

function Person3() {
   this.name = '人间富贵花'
   console.log(this) // Person3:{...}
 	return null
}
var p3 = new Person3()
console.log(p3.name) //人间富贵花
// -----------------------------------------------------------------------------

function Person4() {
	this.name = '人间富贵花'
 	console.log(this) // Person4:{...}
  	return []
}
var p4 = new Person4()
console.log(p4.name) // undefined 
// -----------------------------------------------------------------------------

function Person5() {
   this.name = '人间富贵花'
   console.log(this) // Person5:{...}
 	return function(){}
}
var p5 = new Person5()
console.log(p4.name) // undefined
// -----------------------------------------------------------------------------

function Person6() {
	this.name = '人间富贵花'
  	console.log(this) // Person6:{...}
   	return {}
}
var p6 = new Person6()
console.log(p6.name) // undefined

从 Person1、Person2、Person3、Person4、Person5、Person6 例子可以看出:

如果返回值 是一个对象,this 指向被改变且指向返回的这个对象

如果返回值不是一个对象,this 指向不变还是指向构造函数的实例

总结:

  • 谁调用指向谁
  • 存在对象层级调用的,被上一级调用,this 就指向上一级,否则指向 window
  • 构造函数,指向实例化的对象
  • call、apply,this 指向 target
  • 构造函数 return 的结果如果是一个对象,则会改变 this 的指向,否则不会改变指向,依旧指向实例化的对象