Qingquan-Li / blog

My Blog
https://Qingquan-Li.github.io/blog/
132 stars 16 forks source link

Javascript的this用法 #82

Open Qingquan-Li opened 6 years ago

Qingquan-Li commented 6 years ago

阮一峰 2018 - JavaScript 的 this 原理:由于函数可以在不同的运行环境执行,所以需要有一种机制,能够在函数体内部获得当前的运行环境(context)。所以,this 就出现了,它的设计目的就是在函数体内部,指代函数当前的运行环境。


下文转载自:http://www.ruanyifeng.com/blog/2010/04/using_this_keyword_in_javascript.html

相关链接:Java的this关键字 相关链接:this - JavaScript | MDN - Mozilla

this是Javascript语言的一个关键字。


它代表函数运行时,自动生成的一个内部对象,只能在函数内部使用。比如,


function test(){
    this.x = 1;
}


随着函数使用场合的不同,this的值会发生变化。但是有一个总的原则,那就是this指的是,调用函数的那个对象


下面分四种情况,详细讨论this的用法。


情况一:纯粹的函数调用

这是函数的最通常用法,属于全局性调用,因此this就代表全局对象Global

请看下面这段代码,它的运行结果是1。


function test(){
    this.x = 1;
    alert(this.x);
}
test(); // 1


为了证明this就是全局对象,我对代码做一些改变:


var x = 1;
function test(){
    alert(this.x);
}
test(); // 1


运行结果还是1。再变一下:


var x = 1;
function test(){
    this.x = 0;
}
test();
alert(x); // 0



情况二:作为对象方法的调用

函数还可以作为某个对象的方法调用,这时this就指这个上级对象。


function test(){
    alert(this.x);
}
var o = {};
o.x = 1;
o.m = test;
o.m(); // 1



作为构造函数调用

所谓构造函数,就是通过这个函数生成一个新对象(object)。这时,this就指这个新对象。


function test(){
    this.x = 1;
}
var o = new test();
alert(o.x); // 1


运行结果为1。为了表明这时this不是全局对象,我对代码做一些改变:


var x = 2;
function test(){
    this.x = 1;
}
var o = new test();
alert(x); // 2


运行结果为2,表明全局变量x的值根本没变。



情况四 apply调用

apply() 是函数对象的一个方法,它的作用是改变函数的调用对象,它的第一个参数就表示改变后调用这个函数的对象。因此,this指的就是这第一个参数。


var x = 0;
function test(){
    alert(this.x);
}
var o = {};
o.x = 1;
o.m = test;
o.m.apply(); // 0


apply()的参数为空时,默认调用全局对象。因此,这时的运行结果为0,证明this指的是全局对象。


如果把最后一行代码修改为

o.m.apply(o); // 1


运行结果就变成了1,证明了这时this代表的是对象o。




附:let _this = this 的作用

1. 首先介绍一下 var 和 let 、const 的区别

  1. var 创建的变量是函数作用域的,let 是块作用域的。
  2. let 是 ES2015(ES6)引入了一种创建变量的新方法。
  3. 使用 let 关键字创建的变量可以在创建它的“块”内以及嵌套块内访问。这里所说的“块”是指用大括号 { } 包围的任何东西,比如 for 循环或 if 语句。
  4. const 与 let 几乎完全相同。唯一的区别是,一旦使用 const 为变量赋值,就无法对其重新赋值。

参考: InfoQ: var、let 和 const 应该怎么用?


2. 为什么要使用 let _this = this

  1. 在严格模式下通过 this 传递给一个函数的值不会被强制转换为一个对象。如果在函数里打印 this ,会显示为 unundefined 。
  2. 对一个普通的函数来说,this 总会是一个对象,这种自动转化为对象的过程不仅是一种性能上的损耗,同时在浏览器中暴露出全局对象也会成为安全隐患,因为全局对象提供了访问那些所谓安全的 JavaScript 环境必须限制的功能的途径。
  3. 所以对于一个开启严格模式的函数,指定的 this 不再被封装为对象,而且如果没有指定 this 的话它值是 undefined

es5 开启严格模式,需要在所有语句之前加一句: use strict 。 es6 默认开启严格模式。

参考:

在 es5 的语法里:

  1. 非严格模式下,函数里的 this 指向这个函数对象(this 是函数运行时所在的环境对象);
  2. 在严格模式下,函数里是没有 this 绑定的,所以为 undefined ,所以需要 let _this = this 把 this 的对象,指向到 _this 变量。


3. 箭头函数之 this

ES6 允许使用“箭头” => 定义函数。

  1. 箭头函数的 this 是指向父级对象,所以箭头函数里可以直接使用 this ,不需要 let _this = this
  2. 箭头函数会默认帮我们绑定外层 this 的值,所以在箭头函数中 this 的值和(最)外层的 this 是一样的。
// ES6
function foo() {
  setTimeout(() => {
    console.log('id:', this.id);
  }, 100);
}

// ES5
function foo() {
  var _this = this;

  setTimeout(function () {
    console.log('id:', _this.id);
  }, 100);
}

上面代码中,转换后的 ES5 版本清楚地说明了,箭头函数里面根本没有自己的 this ,而是引用外层的 this 。