JS中this的指向问题

全局环境下

全局环境下,this始终指向全局对象(window), 无论是否严格模式;

console.log(this);	//window
this.a = 35; 
conosle.log(window.a);	//37

函数上下文调用

普通函数的调用

普通函数内部的this分两种情况,严格模式和非严格模式。
①非严格模式下,this 默认指向全局对象window

var a = 25;
function fun(){
	var a = 30;
	console.log(a);	//30
	console.log(this.a);	//25
}
fun();

②严格模式下,this为undefined

var a =30;
function fun(){
	"use strict"
	var a = 60 ; 
	console.log(a);	//60
	console.log(this.a)//报错Uncaught TypeError: Cannot read property 'a' of undefined
}
fun();
对象中函数的this指向

对象内部方法的this指向调用这些方法的对象(谁调用就指向谁),两条口诀:
①函数的定义位置不影响其this指向,this指向只和调用函数的对象有关。

var prop = '全局环境';
var obj1 ={
	prop: '对象属性值',
	decript: '另一个属性值',
	fun: function(){
		return this.prop;
	}
}
function globels(){
	return this.descript;
}
//例一,指向调用方法的对象
console.log(obj1.fun());  // '对象属性值'
var foo = obj1.fun;
console.log(foo());		  // '全局环境'

//例二,跟函数的定义位置无关
obj1.bob = globels;
console.log(obj1.bob());    // '另一个属性值'

②多层嵌套的对象,内部方法的this指向离被调用函数最近的对象(window也是对象,其内部对象调用方法的this指向内部对象, 而非window)。

function independent(){
	return this.prop;
} 
var obj2 ={
	prop: 25,
	foo: {
    	fob: independent,
    	prop: 42
 	}
}
console.log(obj2.foo.fob());    // 42
总结
隐式绑定和隐式丢失
①一般被直接对象包含的函数调用时,也叫方法调用,this存在隐式的绑定到该对象,即隐式绑定。
②隐式丢失是指被隐式绑定的函数丢失绑定对象,从而默认绑定到window
//隐式绑定丢失
//例一
var a=0;
function fun(){
	console.log(this.a);
}
var obj1={
    a : 1,
    foo : fun
}
obj1.foo();     //这是一个对象下的方法的隐式绑定,所以直接对象是obj1
var bar=obj1.foo;   //把obj.foo赋值给bar变量    ,造成隐式丢失因为这个变量和这个对象毫无关系,就会到全局
bar();	//打印0

//例二
var a=0;
function fun(){
    console.log(this.a);
}
function bar(fn){
	fn();
}
var obj={
	a : 2,
	foo : fun 
}
obj.foo();//打印 2,对象方法来执行函数声明,隐式绑定该对象
bar(obj.foo);//打印 0 函数bar此处执行,传递实参obj.foo这个对象下的属性值是fun;-----函数在作为一个参数传递的时候,隐式丢失,this就默认指向到window
改变this指向的三个方法

针对隐式绑定或隐式丢失带来的this指向问题,提供了三个函数用来改变this的指向问题。分别是call() && apply() && bind()。这三个方法可以显示的将函数内部的this值可绑定到 call() & apply() 方法指定的第一个对象上, 如果第一个参数不是对象,JavaScript内部会尝试将其转换成对象然后指向它
call()apply() 方法会自执行,bind() 方法不会自执行。
②当 call不传递参数的时候,默认指向window 。在传递参数的情况下,第一参数必须指向摸一个对象 ,后面的参数就是形参。

var json ={
	a : 40,
    b : 70
}
function foo(x){
    console.log(this.a + this.b);   //110
    console.log(x); //2
}
foo()  //NAN undefined
foo.call()	//NAN undefined
foo.call(json,2); //110 2

apply() 接受两个参数,第一个接受一个对象,第二个参数接收一个数组,数组里面是形参需要接收的实参.

function add(c, d){
  return this.a + this.b + c + d;
}
var o = {a:1, b:3};
add.call(o, 5, 7); // 1 + 3 + 5 + 7 = 16
add.apply(o, [10, 20]); // 1 + 3 + 10 + 20 = 34
function tt() {
  console.log(this);
}
// 返回对象见下图(图1)
tt.call(5);  // Number {[[PrimitiveValue]]: 5} 
tt.call('asd'); // String {0: "a", 1: "s", 2: "d", length: 3, [[PrimitiveValue]]: "asd"}

在这里插入图片描述

箭头函数中的this指向问题

箭头函数不绑定this, 它会捕获其所在(即定义的位置)上下文的this值, 作为自己的this值。

function Person() {  
    this.age = 0;  
    setInterval(() => {
        // 回调里面的 `this` 变量就指向了期望的那个对象了
        this.age++;
    }, 3000);
}
var p = new Person();

以上代码可以得到我们所以希望的值,下图可以看到,在setTimeout中的this指向了构造函数新生成的对象,而普通函数指向了全局window对象
在这里插入图片描述
除此之外箭头函数中的this还有两个特点:
① call() / apply() / bind() 方法对于箭头函数来说只是传入参数,对它的 this 毫无影响。

var adder = {
	base : 1,
	add : function(a) {
	    var f = v => v + this.base;
	    return f(a);
	},
	reduce: function(b){
	    var f = function(v){
	        return v + this.base;
	    }
	    return f(b)
	},
	addThruCall: function inFun(a) {
	    var f = v => v + this.base;
	    var b = {
	    base : 2
	    };     
	    return f.call(b, a);
	}
};
console.log(adder.add(1));         // 输出 2
console.log(adder.reduce(1));       //输出NAN,隐式绑定丢失,指向window.
console.log(adder.addThruCall(1)); // 仍然输出 2(而不是3,其内部的this并没有因为call() 而改变,其this值仍然为函数inFun的this值,指向对象adder

②严格模式中与 this 相关的规则都将被忽略。(可以忽略是否在严格模式下的影响)

var f = () => {'use strict'; return this};
var p = () => { return this};
console.log(1,f() === window);
console.log(2,f() === p());
//1 true
//2 true

在这里插入图片描述

定时器setTimeout&setInterval中函数的this指向问题

①对于延时函数内部的普通回调函数的this指向全局对象window(当然我们可以通过bind方法改变其内部函数的this指向)
看下边代码及截图

//默认情况下代码
function Person() {  
    this.age = 0;  
    setTimeout(function() {
        console.log(this);
    }, 3000);
}

var p = new Person();//3秒后返回 window 对象
==============================================
//通过bind绑定
function Person() {  
    this.age = 0;  
    setTimeout((function() {
        console.log(this);
    }).bind(this), 3000);
}
var p = new Person();//3秒后返回构造函数新生成的对象 Person{...}

在这里插入图片描述
在这里插入图片描述
②对于箭头函数,定时器内部的this指向会绑定到对应的实例的对象上,不会绑定到window上。

补充

ES6教程上的几个例子
function foo() {
  setTimeout(() => {
    console.log('id:', this.id);
  }, 100);
}
var id = 21;
foo.call({ id: 42 });
// id: 42

上面代码中,setTimeout的参数是一个箭头函数,这个箭头函数的定义生效是在foo函数生成时,而它的真正执行要等到 100 毫秒后。如果是普通函数,执行时this应该指向全局对象window,这时应该输出21。但是,箭头函数导致this总是指向函数定义生效时所在的对象(本例是{id: 42}),所以输出的是42。

箭头函数可以让setTimeout里面的this,绑定定义时所在的作用域,而不是指向运行时所在的作用域。下面是另一个例子。

function Timer() {
  this.s1 = 0;
  this.s2 = 0;
  // 箭头函数
  setInterval(() => this.s1++, 1000);
  // 普通函数
  setInterval(function () {
    this.s2++;
  }, 1000);
}
var timer = new Timer();
setTimeout(() => console.log('s1: ', timer.s1), 3100);
setTimeout(() => console.log('s2: ', timer.s2), 3100);
// s1: 3
// s2: 0

上面代码中,Timer函数内部设置了两个定时器,分别使用了箭头函数和普通函数。前者的this绑定定义时所在的作用域(即Timer函数),后者的this指向运行时所在的作用域(即全局对象)。所以,3100 毫秒之后,timer.s1被更新了 3 次,而timer.s2一次都没更新。

请问下面的代码之中有几个this?

function foo() {
  return () => {
    return () => {
      return () => {
        console.log('id:', this.id);
      };
    };
  };
}
var f = foo.call({id: 1});
var t1 = f.call({id: 2})()(); // id: 1
var t2 = f().call({id: 3})(); // id: 1
var t3 = f()().call({id: 4}); // id: 1

上面代码之中,只有一个this,就是函数foo的this,所以t1、t2、t3都输出同样的结果。因为所有的内层函数都是箭头函数,都没有自己的this,它们的this其实都是最外层foo函数的this。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值