Javascript中的this指向问题

this,真的折磨了我蛮久,躺在了我的笔记本中也挺久....今天总结下,this的一系列问题吧.

1.this永远指向一个对象;

2.this的指向完全取决于函数调用的位置.

针对上面的两点,我们可以保证到一个,不管在什么地方使用this,它必然会指向某个对象;确定了第一点后,也引出了一个问题,就是this使用的地方到底在哪里,而this就是函数运行时所在的对象.这本来并不会让我们糊涂,但是在Javascript支持运行环境动态切换,也就是说,this的指向是动态的,很难事先会确定到底指向哪个对象,这才是最让我们感到困惑的地方.

先看原理吧:

function fun(){
console.log(this.s);
}

var obj={
s:'1',
f:fun
}

var s='3';

obj.f();//1
fun();//3

上述代码中,fun函数被调用了两次,但是两次执行的结果是不一样的.大多数的理解的话,是obj.f()的调用中,因为运行环境在obj对象内,因此函数中的this指向对象obj;而在全局作用域下调用fun(),函数中的this就会指向全局作用域对象window;但是,最开始还是并没有了解到this的指向为啥改变,this指向的改变是什么时候发生的.

首先,我们知道在JS中,数组函数和对象都是引用类型,在参数传递时也就是引用传递.比如上述的代码中,obj对象有两个属性,但是属性的值类型是不同的,在内存中的表现也是不同的.

调用的时候,就会变成

因为函数在JavaScript中,既可以当作值传递与返回,也可以当作对象与构造函数,所有函数在运行时需要确定其当前的运行环境,因此就引出了this,this会根据运行环境的改变而改变,同时,函数中的this也只能在运行时才能最终确定运行环境.

接下来再看个例子,有助于理解运行环境的动态切换规则:

var C={
name:"王五",
f:function(){
console.log("姓名" + this.name);
}

var D={
name:"李四"};

D.f = C.f;

D.f();//姓名李四
C.f();//姓名王五

上述代码中,C.f中的属性被赋给D.f,也就是C对象将匿名函数的地址赋值给了D对象;在调用时,函数分别根据运行环境的不同,指向不同的对象.

function baz(){
console.log(this.a);
}

var obj2={
a:2,
fn:baz
};

var obj1={
a:1,
o1:obj2};
obj1.o1.fn(); //2

obj1对象的o1属性值是obj2对象的地址,而obj2对象的fn属性的值是函数baz的地址;函数baz的调用环境始在obj2中的,因此this指向对象obj2.

接下来,再叙述下几种常用的this情况.

首先,我们先看下事件绑定情况下的,事件绑定的话一共有三种方式,分别为行内绑定、动态绑定、事件监听;

行内绑定的两种情况:

<input type='button' value="ljy的按钮"  onclick="btnclick()">
<script>
function btnclick(){
this//此函数的运行环境为window上,因此this指向window;}
</script>
<input type="button" value="ljy的button" onclick="this">
//运行环境在节点对象中,因此this指向本节点对象

当事件触发时,属性值就会作为JS代码被执行,当前运行环境没有btnclick函数,因此浏览器就需要跳出当前运行环境,在整个环境中寻找一个btnclick的函数并执行,所以函数内部的this指向了全局对象window;若不是一个函数调用,直接在当前节点对象环境下使用this,那么this显然指向本节点对象.

动态绑定与事件监听:

<input type="button" value="ljy的按钮" id="btn">
<script>
var btn= document.getElementById('btn');
btn.onclick= function(){
this;//this指向本节点对象}
</script>

因为动态绑定的事件本就是为节点对象的属性,重新复制一个匿名函数,因此函数在执行时就是在节点对象环境下,this自然就指向了本节点对象.

构造函数中的this:

function Person(){
this.x="11";
this.y = function(){};
}

var person=new Person();

new一个构造函数并执行函数内部代码的过程:

1.创建一个空对象;2.将本对象的原型指向Person.prototype(对象继承Pesron.prototype);3.将构造函数的this指向本对象;4.执行构造函数的代码为对象赋值;5.返回本对象地址;

当Javascript引擎指向到第三步的时候,会强制的将this指向新创建出来的这个对象.所以这里的this指向person.

定时器中的this:

var obj={
fun:function(){
this;}
}
setInterval(obj.fun,2000);//指向window
setInterval('obj.fun()',2000);//指向obj

setInterval()/setTimeout()是window对象下内置的一个方法,接受两个参数,第一个参数,可以是一个函数或者一段js代码,第二个参数实质性前面函数或者代码的时间间隔.

在上述代码中,setInterval(obj.fun,2000)的第一个参数是obj对象的fun,因为Javascript中函数可以被当作值来做引用传递,实际就是将这个函数的地址当作参数传递给了setInterval,那么2000毫秒后,函数的运行就已经是再window对象下了,也就是函数的调用者成为了window,因此指向window.那么下面一个,中的第一个参数,实际则是传入了一段JS代码,2000毫秒后,当JS引擎来执行这段代码的时候,则是通过obj对象来找到fun函数并执行调用的,那么函数依旧执行在对象obj内,所以函数内部的this指向了obj对象.

再来一个定时器的例子!

var name='My name is window';
var obj={
name:'I am obj.";
fun:function(){
var timer=null;
clerInterVal(timer);
timer=setInterval(function(){
console.log(this.name);//My name is window.
},1000)
}
}

在这里,从this.name可以看出this的指向是window.如果没有特殊指向,setInterval和setTimeout的回调函数中this的指向是window.那么怎么修改this的指向,这里总结了点:

在外部函数中,将this存为一个变量,回调函数中使用该变量,而不是直接使用this.

var name='My name is window';
var obj={
name:'I am obj.";
fun:function(){
var that=this;
var timer=null;
clerInterVal(timer);
timer=setInterval(function(){
console.log(that.name);//I am obj.
},1000)
}
}

还有一个非常妙的用法,箭头函数:

首先,先介绍下箭头函数中的this指向问题,箭头函数本身是没有自己的this的,它的this继承自外部函数的作用域.我们来举两个例子:

const obj={
num:10,
hello:function(){
console.log(this);//obj
setTimeout(()=>{
console.log(this);//obj
});
}
}
obj.hello()

这个箭头函数的上一级为hello函数,它是属于obj的,因此其this指向obj.

const obj={
radius:10;
diameter(){
return this.radius*2;
},
pre:()=>2*Math.PI*this.radius
}
console.log(obj.diameter())//20
console.log(obj.pre())//NaN

diameter的原因就不说了,pre是箭头函数,this应指向上下文函数this的指向,这里上下文没有函数对象,所以就默认为window,window里面没有radius属性,因此返回NaN.

那么,我们通过箭头函数也可以非常ok的修改setTimeInterval中的this指向.

var name='My name is window';
var obj={
name:'I am obj.";
fun:function(){
var that=this;
var timer=null;
clerInterVal(timer);
timer=setInterval(()=>{
console.log(this.name);},1000)//I am obj
}
}
原文地址:https://www.cnblogs.com/ljylearnsmore/p/14488646.html