关于js函数中this的指向的问题

309 阅读6分钟

@(javascript)[JavaScript中this的指向]

关于js函数中this的指向的问题

javasript函数中this的指向一直都是许多编程入门新手的一个问题,老师把这个this的指向弄错误。下面我们可以来看看关于this指向的几种情况。

不过在讲之前,请各位始终记住一句话: JavaScript中的this一直指向触发这个事件(函数)的这个Objoct(对象)。

第一种:函数直接添加在行内

<button onclick="alert(this.innerHTML);">this就是指向这个div盒子</button>

点击看看: this就是指向这个div盒子 此时的这个alert(this.innerHTML);事件是直接写在button这个元素行内的。是这个button元素触发的这个事件。

第二种:通过对象.事件名来实现

这是html代码

<button id="box">通过js事件的点击事件</button>

下面是js代码

document.getElementById("box").onclcik = function(){
	alert(this.innerHTML);
}

通过js事件的点击事件

以上的两种方式都很好理解,也不做过多的说明。

第三种:函数的调用的形式

这种方式也是存在许多的变种,先来看看上面例子的一个变种。 这是html代码

<button id="box1">通过js事件的点击事件</button>

下面是js代码

document.getElementById("box1").onclick = a;
function a(){
	alert(this);
}

这一种形式的事件与上面的其实是一样的,不过是单独定义了一个函数,然后点击调用这个函数。

在看看下面的例子

<button id="box1">通过js事件的点击事件</button>

下面是js代码

document.getElementById("box1").onclick = function(){
	a();
}
function a(){
	alert(this);
}

此时的这个与上面的有很相似,不同的就是此时的这个a函数是在一个事件函数里面执行的。此时的逻辑是:我点击这个ID为box1的button,让后触发事件后面的函数function(){a();}。此时呢里面的a函数其实还没有执行。到这个函数执行的时候,执行到里面的函数了,a函数自己调用执行(函数加括号执行),那么问题来了,是谁调用的这个a函数呢?答案是:window。因为不存在上面说的两种以及Object.函数的形式。这个就有点像是定义了一个函数自执行。

像这样

function t(){
	console.log(this);
}
/**
 * 此时的这个 t 函数是由谁调用的呢(也就是鼠标哪一个对象让他执行的?)
 * 答案是的 null
 * 而在 js 中,如果 this 的指向是null会自动转换为 window
 */
t();

或者是这样:

function t(){
	function t2(){
		console.log(this);
	}
	t2()
}
t();

OK!接下来看看这个:

function intro(){
	console.log('名字是'+ this.name,this );
}
var dog = {name: "狗"};
dog.intro = intro;
dog.intro(); // 狗

分析一下:此时先定义了一个名字是intro的一个函数,在定义一个dog对象并赋值name=狗。下面的代码就是在dog这个对象上添加一个intro对象,赋值为一开始定义的那个函数intro。此时的这个dog对象就多了一个属性intro,值是函数。接着在调用这个dog.intro();。那么是谁调用的呢?很明显是这个dog对象调用的这个函数。所以此时的这个this就是指向dog对象。那this.name就是了。

OK!再来:

function intro(){
	console.log('名字是'+ this.name,this );
}
var dog = {name: "狗"};
dog.intro = intro;
dog.intro(); // 狗

var cat = {name:"猫"};
cat.intro = dog.intro;
cat.intro(); // 猫

现在多了一个cat,看这句cat.intro = dog.intro;。这个是把dog下面的intro方法赋值给了cat下面新建的一个intro方法。此时涉及到一个内存的问题。js中的对象,函数等都是存放在内存中,给其他对象赋值为这个函数的时候就是让这个对象的这个属性指向内存中的这个函数,所以,不管上面的是怎样赋值,catdogintro的值是指向同一个函数的。不同的是调用他们的对象不同,导致this不同,所以最后的this.name的值不同。

再来看看这个呢?

function intro(){
	console.log('名字是'+ this.name,this );
}
var dog = {name: "狗"};
dog.intro = intro;
dog.intro(); // 狗

var cat = {name:"猫"};
cat.intro = dog.intro;
cat.intro(); // 猫

(cat.intro = dog.intro)(); // 空 this 指向window```javascript
function intro(){
	console.log('名字是'+ this.name,this );
}
var dog = {name: "狗"};
dog.intro = intro;
dog.intro(); // 狗

var cat = {name:"猫"};
cat.intro = dog.intro;
cat.intro(); // 猫

(cat.intro = dog.intro)(); // 空 this 指向window

此时的在上面的就是多了一句(cat.intro = dog.intro)();。那为什么就指向window了呢? 上面的这个例子,涉及到的是关于计算机里面的计算,cat.intro = dog.intro 这是一个赋值运算,而赋值运算的结果是 = 右侧的值,此时这个值并不属于任何一个对象,而这个值是一个空,也可以说是一个 null ,所以此是为 null 转为指向 window。可以这么做一个比喻:

一天张三去面馆吃面,给老板说:“老板,一碗牛肉面”,做好面后,此时的这碗牛肉面这个就有了。相当于上面的intro函数,此时呢他并不属于张三。当老板把这碗面端到张三面前,这是才属于张三。OK。李四也去吃面,进入面馆,看见张三的面。对老板说:“老板,来一碗和他一样的面”。此时老板需要计算,这碗面是什么面?相当于上面的cat.intro = dog.intro计算。老板计算后才知道这碗面是牛肉面。此时让一个加括号执行,那肯定就不行了。此时的这碗面并不属于李四。赋值的结果是是什么呢?当老板把这碗面端到李四面前,这是才属于李四。这就是结果。

需要注意的点就是cat.intro = dog.intro只是一个赋值过程,值是=右边的东西。执行cat.intro = dog.intro会导致一个结果就是cat下面多了一个intro属性并且有值。

基本普通的函数中的this存在的问题就是这么多,其他的也就是上面的一些变种。

不过,可以发现,因为this指向window的存在,会导致污染全局,所以多数时候使用构造函数new的方式更可取,这样this才不会指向全局避免污染全局。

上面说了这么多。其实也就是最开始说的那句: JavaScript中的this一直指向触发这个事件(函数)的这个Objoct(对象)。

更新于2019年09月11日

针对于ES6中的箭头函数目前用的非常多。使用箭头函数与普通函数相比,箭头函数有以下几点差异:

  1. 箭头函数中的this是在定义的时候所在的作用域的this而不再是函数执行时候确定
  2. 不可以使用arguments对象,该对象的在函数体内不存在。如果要使用,可以使用rest参数代替
  3. 不可以使用yield命令,因此箭头函数不能用作Generator函数
  4. 不可以使用new命令,因为(1):没有自己的this,无法使用call,apply。(2):没有prototype属性,而new命令在执行时需要将构造函数的prototype赋值给新的对象的proto