This的几种常见使用场景

在此之前,先了解一下执行上下文:

理解执行上下文

JavaScript中代码是分段执行的,控制器转到每段可执行代码时,就会进入一个执行上下文,执行上下文可以理解为当前代码的执行环境,执行环境有三种情况:

  • 全局环境: JavaScript代码执行会首先进入该环境。
  • 函数环境:当函数被调用执行时,会进入该函数的执行环境。
  • eval()(不建议使用,可以忽略)

我们只需要考虑全局执行环境和函数执行环境,JavaScript引擎会通过栈的方式来处理执行环境,栈底永远都是全局执行环境,栈顶是当前正在执行的函数执行环境。

注意:在函数中,遇到return就会直接结束可执行代码的执行,将当前上下文弹出栈。

在执行上下文的创建阶段,会执行以下操作:

  • 生成变量对象
  • 确定this指向
  • 建立作用域链

这里我们先暂时只讨论this的指向问题。

关于this的指向,请牢记准则:

this的指向是在函数调用的时候确定的,函数定义时无法确定。

1
2
3
4
5
6
7
8
9
10
11
12
var name = 'global';
var a = {
name: 'A',
fn: function () {
console.log(this.name);
}
};

a.fn(); // A
a.fn.call({name: 'B'}); // B
var fn1 = a.fn;
fn1(); // global

除此之外,在函数的执行过程中,this一旦被确定,就不可更改了。

知其然还要知其所以然,所以我们先分析一下this的指向。

作为构造函数执行

1
2
3
4
5
6
7
function Foo (name) {
// this = {};
this.name = name;
// return this;
}

const f = new Foo('xiaoming');

new 一个对象的过程:

  • 创建一个新对象
  • this指向这个对象
  • 执行代码,即对this赋值
  • 返回this

作为对象属性执行

1
2
3
4
5
6
7
8
const obj = {
name: 'A',
printName: function () {
console.log(this.name);
}
}

obj.printName();

作为普通函数执行

1
2
3
4
5
function foo () {
console.log(this);
}
// 这里相当于window.foo(),所以 this === window
foo();

call

1
2
3
4
5
6
function foo (name) {
console.log(name);
console.log(this);
}

foo.call({a: 10}, 'xiaoming'); // this === {a: 10}

apply

1
2
3
4
5
6
function foo (name, age) {
console.log(name, age);
console.log(this);
}

foo.apply({a: 10}, ['xiaoming', 20]);

bind

1
2
3
4
5
6
7
// 注意:这里不能使用函数声明式写法,只能用函数表达式写法
var foo = function (name, age) {
console.log(name, age);
console.log(this);
}.bind({a: 10})

foo('xiaoming', 20);