关于this

316 阅读3分钟

最近在拜读《你不知道的js》,而此篇是对于《你不知道的js》中this部分的笔记整理,希望能有效的梳理,并且巩固关于this的知识点

一、this是一种怎样的机制

1、this是在运行时进行绑定的,并非在编写时绑定,它的上下文取决于函数调用时的各种条件,它指向什么完全取决于函数的调用位置;

2、this的绑定与函数申明位置无关系,只取决于函数的调用方式;

3、当函数被调用时,会创建一个活动记录,即执行上下文。这包括函数在哪被调用、函数调用方式、传入的参数信息等。this则为其记录的一个属性,会在函数执行的过程中用到;

二、this的作用

首先看一段代码:

function identify() {
    return this.name.toUpperCase();
}
function speak() {
    var greeting = "Hello, I'm " + identify.call(this);
    console.log(greeting);
}
var me = {
    name: "Mike"
};
var you = {
  name: "Jenny"  
};
identify.call(me); // Mike
identify.call(you); // Jenny

speak.call(me); // Hello, I'm Mike
speak.call(you); // Hello, I'm Jenny

这段代码可在不同的上下文对象中重复使用identify(),speak();

若不使用this,可将代码变换如下:

function identify(con) {
    return con.name.toUpperCase();
}
function speak(con) {
    var greeting = "Hello, I'm " + identify.call(con);
    console.log(greeting);
}
var me = {
    name: "Mike"
};
var you = {
  name: "Jenny"  
};
identify(me); // Mike
speak(you); // Hello, I'm Jenny

this可以以一种更优雅的方式来传递上下文对象,使代码变得简洁且易于复用

三、对于this的误解

误解一、this指向函数自身(仅从字面上理解this的含义)

先来看一段代码:

function foo(num) {
    console.log("foo: " + num);
    // 记录foo被调用的次数
    this.count++;
}
foo.count = 0;
var i;
for(i = 0; i < 0; i++) {
    if(i > 5) {
        foo(i);
    }
}
// foo(6);
// foo(7);
// foo(8);
// foo(9);

// foo被调用了几次?
console.log(foo.count); // 0

为什么此处的foo.count值为0呢?

执行foo.count时,的确向函数对象foo添加一个属性count,但函数内部代码的this并非指向foo,而this.count是创建了一个全局变量,所以this指向了window

此时this.count的值为NaN

若要实现foo.count = 4 ,则将代码变化如下:

方法一、

function foo(num) {
    console.log("foo: " + num);
    // 记录foo被调用的次数
    data.count++;
}
var data = {
    count: 0
};
var i;
for(i = 0; i < 0; i++) {
    if(i > 5) {
        foo(i);
    }
}
// foo(6);
// foo(7);
// foo(8);
// foo(9);

// foo被调用了几次?
console.log(data.count); // 4

此写法虽然达到了理想的状态,但却忽略了this

方法二、

function foo(num) {
    console.log("foo: " + num);
    // 记录foo被调用的次数
    foo.count++;
}
foo.count = 0;
var i;
for(i = 0; i < 0; i++) {
    if(i > 5) {
        foo(i);
    }
}
// foo(6);
// foo(7);
// foo(8);
// foo(9);

// foo被调用了几次?
console.log(data.count); // 4

这种写法同样回避了this的问题

方法三、

function foo(num) {
    console.log("foo: " + num);
    // 记录foo被调用的次数
    this.count++;
}
foo.count = 0;
var i;
for(i = 0; i < 0; i++) {
    if(i > 5) {
        // 使用call(...)可以确保this指向函数对象foo本身
        foo.call(foo, i);
    }
}
// foo(6);
// foo(7);
// foo(8);
// foo(9);

// foo被调用了几次?
console.log(data.count); // 4

此方法使用call,改变了上下文对象,强制将this指向foo函数对象

总结:通过以上例子说明,仅仅从字面意思来解释this是不准确的,而是要通过关注执行上下文,以及函数的调用位置来判断this的指向

误解二、this指向函数的作用域

这个问题在某些情况下是正确的,但也有错误的情况,例:

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

这段代码试图使用this联通foo()、bar()的词法作用域,从而让bar()可以访问foo()作用域中的变量。但this不可能在词法作用域中查到什么。因为this指向全局,而a属于foo作用域中的变量,所以无法查到

总结:this在任何情况下都不指向函数的词法作用域

词法作用域:定义在词法阶段的作用域,即由你在写代码时将变量和块作用域写在哪里来决定