this 是什么

438 阅读4分钟

this 原理

首先,要知道的是 this 是指函数当前的运行环境(上下文)。

我们都知道 this 是函数运行时所在的环境,但我们不知道是函数的运行环境到底是怎么决定的。

var obj = {
   fn:function(){
       console.log(this.title); 
   },
   title:"Hello"
}

var fn = obj.fn;
var title = "Hi";

obj.fn() //"Hello"
fn() //"Hi"

上面的例子中,obj.fnfn调用的都是同一个函数,但结果却不一样,这就是我们上面说的 this 是根据运行时所在环境不同导致的,对于 obj.fn 来说,fn 是运行来 obj 环境的,所以this 指向 obj,对于 fn 来说是运行在全局的,因此 this 指向的是全局,也就是 window

那为什么 obj.fn() 就是在 obj 环境运行的,而一旦 var fn = obj.fn 就变成全局环境了呢?下面让我们一点点揭开 this 的神秘面纱。

1. 内存的数据结构

javascript 之所有有 this,是和内存结构有关系的。

var obj = {fn:5}

以上代码,将一个对象赋值给变量 obj,在 javascript 的 引擎中会先在内存中,生成一个对象 { fn:5},然后在将这个对象的地址赋给变量 obj

就是说,变量 obj 只是存了一个地址,如果要进行取读操作 obj.fn,那引擎是先从 obj 拿到内存地址,然后在从该地址读出原始对象,然后返回它的 fn 属性。

原始的对象以类似于字典结构的状态保存,每一个属性名都对应一个属性描述对象。拿上面的那个例子的 fn 属性来看

注意!fn 属性的值是保存在属性描述对象的 value 属性里面

2. 函数

属性值不是一个函数的时候很清晰,那如果属性的值是一个函数呢?

var obj = { fn: function(){}};

当对象的属性值是函数的时候,javascript 引擎会单独把函数保存在内存中,然后再将函数的地址赋值给 fn 属性的属性描述对象的 value 属性上。

由于函数是单独的一个值,所以它可以在不同的环境 ( 上下文 ) 中运行。

var fn = function(){ 
    console.log(this.age);
};

var obj = {
    fn: fn,
    age: 5
};
var age = 10;

//单独运行时
fn(); //10

//在obj中运行时
obj.fn(); // 5

3. 环境变量

由于 javascript 允许函数体内,引用当前环境的其他变量,而变量是有当前的运行环境提供的,那么由于函数是可以在不同的运行环境中执行,那就需要一种机制,一种能够在函数体内部获得当前运行环境。so,这时 this 出现了,它的目的就是在函数体内部,指向函数当前的运行环境。

function fn (){
    console.log(age); //由运行环境提供
}

var age= 10";

fn(); //10

以上代码中的 age 就由该函数运行时所在的环境提供。

再看,下面的例子

function fn(){
    console.log(this.xx);
}

var xx = 10;

var obj = {
    fn: fn,
    xx: 100
}

//单独运行时
fn()//10

//在obj中运行时
obj.fn()//100
  • 以上代码中,this.xx 指向运行时所在的环境的 xx。
  • fn() 单独运行时,this.xx 指向全局环境的 xx。
  • obj.fn() 运行时,则 this.xx 指向的是 obj 环境的 xx。

现在,我们回到文章中最开始提出的问题,obj.fn() 是通过 obj 找到的 fn,所以就是在 obj 环境执行,一旦 var fn = obj.fn,则 变量 fn 就直接指向了函数本身,所以 fn() 就变成了全局环境执行。

注意!不要忘记,当对象里的属性名的值是一个函数的时候,这时,javascript 引擎就报函数单独保存在内存中,然后把函数地址给了 属性名的属性描述对象的value上。

this 使用

this 是 javascript 中的一个关键字。
它是在运行时,在函数体内自动生成的一个对象,因此只能在函数体内使用

下面让我们直截了当的来看下 this 的几种使用方式

1. 函数调用

函数的通常用法,这种属于全局调用,因此这时 this 就代表者全局对象

var x = 1;

function fn(){
    console.log(x); // 变量 x,这时是全局的变量 x
}
fn(); //1

2. 匿名函数的使用

匿名函数执行时,this 代表者全局

var x = 1;
var fn = function(){
    console.log(this.x);
}
fn(); // 1

//常用闭包式匿名函数

(function(){
    console.log(this.x)
})() //1

3. 作为对象调用

当函数作为对象调用的时候,此时的 this 是这个上级对象

function fn(){
    console.log(this.x); //this 指向 obj
}
var obj = {
    x:1,
    m:fn
}    
obj.m(); // 1 

4. 构造函数使用

当通过构造函数,生成一个新对象时,此时 this 是指这个新对象

function Fn(){
    this.x = 1; 
}
var x = 2; 
var obj = new Fn();
obj.x // 1

5. apply / call 时调用

apply/call 是函数的一个方法,主要就是用来改成函数调用的,第一个参数就是要改变的对象,因此,this 就是指这第一个参数,如果第一个参数无,那 this 就代表者全局

var x = 1;
function fn(){
    console.log(this.x);
}
var obj = {
    x: 2,
    m:fn
}

obj.m.apply(); // 1

更多详情请移步 阮一峰老师博文