【前端--JavaScript】知识点(二)——关于this你不知道的事 (一)| 8月更文挑战

145 阅读4分钟

this关键字是JavaScript中最复杂的机制之一,它是一个很特别的关键字,被自动定义在所有函数的作用域中,即使是非常有经验的JavaScript开发者也很难说清楚它到底指向什么

今天Y同学在写小程序的时候不禁有个疑问,为什么要用this,有时候this还会失效,那this到底指向谁呢?那就一点点看看this这个关键字......

为什么要使用this?

首先看一段代码

function identhfy(){
  return this.name.toUpperCase(); 
}
function speak(){
  var greeting = "Hello, I'm " + identify.call( this );
}
var me = {
  name: "Kyle"
};
var you = {
  name: "Reader"
};
identify.call( me ); // KYLE
identify.call( you ); // READER
speak.call( me ); // Hello, I'm KYLE
speak.call( you ); // Hello,I'm READER

这段代码可以在不同的上下文对象(me和you)中重复使用函数identify()speak(),不用针对每个对象编写不同版本的函数。 如果不使用this,那就需要给identify()speak()显式传入一个上下文对象。

function identhfy(context){
  return context.name.toUpperCase(); 
}
function speak(){
  var greeting = "Hello, I'm " + identify.call( context );
}
var me = {
  name: "Kyle"
};
var you = {
  name: "Reader"
};
identify( you ); // READER
speak( me ); // Hello, I'm KYLE

this指向误解一

在很多人一直以来的认识里,this指向谁是一个很难分辨的问题,常常很容易把this理解成指向函数本身,当然这个推断从英语的语法角度来说是说得通的。

现在来看这段代码,看this并不像我们所想的那样指向函数本身

// 记录函数foo被调用的次数
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

// foo被调用多少次
console.log( foo.count ); // 0

我们看到,console.log产生了四句输出,证明foo()确实被调用了四次,但是foo.count仍然是0,显然this在这里是不指向函数本身的。

在开发过程中遇到这个问题,我们常见的解决办法是创建一个带有count属性的对象

// 记录函数foo被调用的次数
function foo(num) {
  console.log( "foo:" + num);
  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

// foo被调用多少次
console.log( data.count ); // 4

这里是使用了一种更熟悉的技术——词法作用域

在 JavaScript 中词法作用域规则:

  1. 函数允许访问函数外的数据.
  2. 整个代码结构中只有函数可以限定作用域.
  3. 作用规则首先使用提升规则分析
  4. 如果当前作用规则中有名字了, 就不考虑外面的名字

还有一种办法就是强制this指向foo函数对象,把for循环中调用foo的写法改为foo.call(foo,i);

Y同学一脸困惑~~

this指向误解二

第二种常见的误解是this指向函数的作用域,这个问题,在某种情况下是正确的,在一些情况却是错误的。(叹气~~)

在这里需要明确的是this在任何情况下都不指向函数的词法作用域。在JavaScript内部,作用域确实和对象类似,可见的标识符都是它的属性,但是作用域“对象”无法通过JavaScript代码访问,它存在于JavaScript引擎内部。

思考下面的代码:

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

在这段代码里,this试图跨域边界使用this隐式引用函数的词法作用域,但是没有成功,

每当想要把this和词法作用域的查找混合使用时,一定要提醒自己,这是无法实现的。

this到底是什么?

经过一些例子的思考后,结合我们的知识,我们应该更清楚,this是在运行时绑定的,并不是在编写时候绑定,它的上下文取决于函数调用时的各种条件。this的绑定和函数声明的位置没有任何关系,只取决于函数的调用方法。

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

接下来会讲解如何寻找函数的调用位置,从而判断函数在执行过程中会如何绑定this

Y同学:迷惑+1 迷惑+1 迷惑+1 迷惑+1