在一些笔试题和项目中经常会出现一些this指向的问题。特此记录一下
this的默认指向
1.全局环境下this指向window
console.log(this) //window
2.函数独立调用,函数内部的this也指向window
3.被嵌套的函数独立调用时,this默认指向了window
function fn1(){ console.log(this); //window } fn1() //fn1() === window.fn1(),所以指向了window var a = 1; var obj = { a:2, foo:function(){ // 函数当作对象的方法来调用时候,this指向obj var a = 3; var that = this; console.log(that.a) //2 function test(){ console.log(this.a); //1 } test() } } obj.foo() //独立函数调用指向window
4.IIFE 自执行函数(自执行函数默认指向window,跟函数内部调用一致)
var a = 10; var obj = { a:2, foo:function(){ console.log(this); //函数当作对象的方法来调用时候,this指向obj (function test(){ console.log(this.a); })() //自执行函数默认指向window,跟函数内部调用一致 } } obj.foo() // 10 var a = 10; var obj = { a:2, foo:function(){ console.log(this); //函数当作对象的方法来调用时候,this指向obj (function test(that){ console.log(that.a); })(this) //自执行函数默认指向window,跟函数内部调用一致 } } obj.foo() // 2
5.闭包 :this默认指向了window
var a = 0; var obj = { a:2, foo:function(){ return function test(){ return this.a //闭包 :this默认指向了window } } } var bb = obj.foo() console.log(bb()) //0
6.隐式绑定
//当函数当作方法来调用,this指向了直接对象 function foo(){ console.log(this.a) } var obj = { a:1, foo:foo, obj2:{ a:2, foo:foo } } // foo()函数的直接对象是obj,this的指向指向了直接对象 obj.foo(); //1 obj.obj2.foo(); //2
7.隐式丢失
//隐式丢失就是指被隐式绑定的函数丢失了绑定对象,从而默认绑定到window
// 这种情况就是比较容易出错却又非常常见
一、隐式丢失:函数别名
var a = 0; function foo(){ console.log(this.a) } var obj = { a:1, foo:foo } // 把obj.foo()赋值给别名bar,造成隐式丢失的情况,因为只是把obj.foo()赋值了给了bar变量,而bar与obj对象毫无关系 var bar = obj.foo; bar(); //0 相当于 var a = 0; var bar = function foo(){ console.log(this.a) } bar()
二、参数传递
// 2.参数传递 var a = 0; function foo(){ console.log(this.a) } function bar(fn){ fn() } var obj = { a:1, foo:foo } // 把obj.foo当作参数传递到bar函数中,有隐式的函数复制fn = obj.foo, 只是把foo函数赋值给了fn,而fn与obj对象毫无关系,所以当前foo函数内部的this指向了window bar(obj.foo) //0 等价于 var a = 0; function bar(fn){ fn() } bar(function foo(){ console.log(this.a); })
三、内置函数
// 3.内置函数 setTimeout()和setInterval()第一个参数的回调函数中的this默认指向了window,跟第二种情况是类似 var a = 0; function foo(){ console.log(this.a) } var obj = { a:1, foo:foo } setTimeout(obj.foo,1000) //0 window
四、间接调用
// 4。间接调用 function foo(){ console.log(this.a) } var a = 0; var obj = { a:1, foo:foo } var p = {a:4} // obj.foo(); //1 // // 立即调用 // // 将obj.foo函数对象赋值给p.foo函数,然后立即执行。相当与仅仅是foo()函数的立即调用,内部的this默认指向了window // (p.foo = obj.foo)() //0 p.foo = obj.foo // 将obj.foo赋值给p.foo函数,之后p.foo()函数再执行,其实是属于p对象的函数的指向,this指向当前的p对象 p.foo() //4
五、其他情况
//5.其他情况(指向了window的特殊情况) function foo(){ console.log(this.a) } var a = 0; var obj = { a:1, foo:foo } // 都是立即执行函数,指向window (obj.foo = obj.foo)() //0 (false || obj.foo)() //0 (1,obj.foo)() //0
8.显示绑定
一、call() apply() bind()
9.new绑定
// call() apply() bind() 把对象绑定到this上,叫做显示绑定 var a = 0; function foo(){ console.log(this.a); } var obj = { a:2 } foo() //0 window.foo() foo.call(obj) //2 foo.apply(obj) //2 var fn = foo.bind(obj) //2 fn()
二、硬绑定是显示绑定的一个变种,使得this不能再被改变
var a = 0; function foo(){ console.log(this.a); } var obj = { a:2 } var bar = function(){ foo.call(obj) } setTimeout(bar,2000) //2 bar.call(window) //2
三、 数组的forEach(fn,对象) //map() filter() some() every()第二个参数改变this指向
// 数组的forEach(fn,对象) //map() filter() some() every()第二个参数改变this指向 var id = 'window'; var obj = { id:'fn' } var arr = [1,2,3] arr.forEach(function(el,index){ console.log(el,index,this); },obj)
function Fn() { //如果是new关键来执行函数,相当于构造函数来实例化对象,那么内部的this指向了当前实例化的对象 console.log(this); //Fn {} } var fn = new Fn() console.log(fn); //Fn {} function Fn2(){ // this还是指向了当前的对象 console.log(this); // 使用return关键来返回对象的时候,实例化出来的对象是当前的返回对象 return { name:'zhen' } } var fn2 =new Fn2() //Fn2 {} console.log(fn2) // {name: "zhen"} var Person ={ fav:function(){ return this } } var p = new Person.fav(); console.log(p); //fav {} console.log(p===Person.fav); //false // 实例化出来的对象内部的属性constructor属性指向了当前的构造函数 console.log(p.__proto__.constructor === Person.fav);
10.严格模式下this指向
//1.独立调用的函数内部的this指向了undefined function fn(){ 'use strict' console.log(this); } fn() //undefined //2.严格模式下,函数apply()和call()内部this始终是他们的第一个参数 var color = 'red'; function showColor(){ 'use strict' console.log(this.color); } showColor.call(undefined)