你不知道的this(1)

时间:2022-07-28
本文章向大家介绍你不知道的this(1),主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

关于this

this关键字是JavaScript中最复杂的机制之一,同时,它也是JavaScript中最重要的机制之一。但是,即使是非常有经验的JavaScript开发者也很难说清楚它到底是什么。

我们为什么需要 this

如果对于有经验的开发者来说this都是一个非常复杂的机制,那么它到底有用在哪里?真的值得我们付出那么大的代价来学习吗?

function identify() {
  return this.name.toUpperCase();
}

function speak() {
  var greeting = "Hello, I'm " + identify.call(this);
  console.log(greeting);
}

var me = {
  name: 'kyle';
}
var you = {
  name: 'Reader'
}

identify.call(me); // kyle
identify.call(you); // Reader

speak.call(me); // Hello, 我是 Kyle
speak.call(me) // Hello, 我是 Reader

这段代码可以在不同的上下文对象(meyou)中重复使用函数identify()speak(),不用针对每个对象编写不同版本的函数

如果不使用this,那就需要给identify()speak()显示传入一个对象。

function identify(context) {
  return context.name.toUpperCase();
}
function speak(context) {
  var greeting = "Hello, I'm " + identify(context);
  console.log(greeting);
}
var me = {
  name: 'kyle';
}
var you = {
  name: 'Reader'
}
identify(you);
speak(me);

this提供了一种更优雅的方式来隐式传递一个对象的引用,因此可以将API设计更加简洁且易于复用。

随着使用模式越来越复杂,显示传递上下文对象会让代码变得越来越混乱,使用this则不会这样。当我们介绍到原型和对象的时候,我们就会明白函数可以自动医用合适的上下文对象多么重要。

误解

我们介绍this到底如何工作之前,要解除一些关于this的错误认知。如果太拘泥于this字面的意思就会产生一些误解。

指向自身

人们很容易把this理解未指向函数本身体,这个推断从英语的角度上是说得通的。那么为什么需要从函数内部引用函数自身呢?常见的原因是递归(从函数内部调用这个函数)或者我们写一个在第一次被调用后自己可以接触绑定的事件处理器。Javascript新手开发者通常会认为,既然把函数看作是一个对象,那就可以在调用函数时存储状态(属性的值)。

function foo(num) {
  console.log("foo: " + num);
  this.count++;
}
foo.count = 0;
var i;
for (i = 0; i < 10; i++) {
  if (i > 5) {
    foo(i);
  }
}
// foo: 6
// foo: 7
// foo: 8
// foo: 9

console.log(foo.count); // 0

执行 foo.count = 0 的时候,的确向函数对象foo添加了一个属性count,但是函数内部代码this.count中的this并不是指向那个函数对象,所以虽然属性名相同,根对象却并不相同。

负责的开发一定会问 :"如果我增加的count属性和预期的不一样,那我增加的是哪个count?"

实际上,如果他深入探索的话,他就会发现这段代码在无意间创建了一个全局变量count,他的值为NaN。当然,如果他发现了这个奇怪的结果,一定会问 :“为什么时全局,为什么它的值是NaN而不是其他更适合的值”

function foo(num) {
  console.log('foo: ' + num);

  // 记录foo被调用的次数
  data.count++;
}

var data = {
 count: 0;
};

var i;
for (i = 0; i < 10; i++) {
  if (i > 5) {
    foo(i);
  }
}

// foo: 6
// foo: 7
// foo: 8
// foo: 9

console.log(data.count); // 4

从某种角度上来说这个方法解决了问题,但可惜它忽略了真正的问题————无法理解this的含义和工作原理————而是返回了舒适区,使用了一种我们熟悉的技术:词法作用域。

它的作用域

第二种常见的误解是,this指向函数的作用域。这个问题有点复杂,因为在某种情况下它是正确的,但是在其他情况下它却是错误的。需要明确的是,this在任何情况下都不指向函数的词法作用域。在JavaScript内部,作用域确实和对象类似,可见的标识符都是它的属性。但是作用域"对象"无法通过JavaScript代码访问,它存在于JavaScript引擎内部。思考一下下面的代码,它试图(但是没有成功)跨越边界,使用this来隐式引用函数的词法作用域:

function foo() {
  var a = 2;
  this.bar();
}
function bar() {
  console.log(this.a);
}
foo(); // ReferenceError: a is not defined

首先,这段代码试图通过this.bar()来引用bar()函数。这是绝对不可能成功的,我们之后会解释原因。调用bar()最自然的方法是省略前面的this,直接使用词法引用标识符。此外,编写这段代码的开发者还试图使用this联通foo()bar()的词法作用域,从而让bar()可以访问foo()作用域里的变量a。这是不可能实现的,你不能使用this来引用一个词法作用域内部的东西。每当你想要把this和词法作用域的查找混合使用时,一定要提醒自己,这是无法实现的

this 到底是什么

this是在运行时进行绑定的,并不是在编写时绑定,它的上下文取决于函数调用时的各种条件。

this的绑定和函数声明的位置没有任何关系,只取决于函数的调用方式。

当一个函数被调用时,会创建一个活动记录(有时候也称为执行上下文)。这个记录会包含函数在哪里被调用(调用栈)、函数的调用方法、传入的参数等信息。this就是记录其中的一个属性,会在函数执行的过程中用到。

小结

对于那些没有投入时间学习 this机制的JavaScript开发者来说,this的绑定一直是一件非常令人困惑的事。this是非常重要的,但是猜测、尝试并出错和盲目地从Stack Overflow上复制和粘贴答案并不能让你真正理解this的机制。学习this的第一步是明白this既不指向函数自身也不指向函数的词法作用域,你也许被这样的解释误导过,但其实它们都是错误的。this实际上是在函数被调用时发生的绑定,它指向什么完全取决于函数在哪里被调用。