目录

1. 默认绑定:
2. 隐式绑定:
3. 显示绑定
4. new 绑定
5. 优先级
6. 规则之外

this 关键字是函数运行时自动生成的一个内部对象,只能在函数内部使用,总指向调用它的对象。

根据不同的使用场合,this 有不同的值,主要分为下面几种情况:

1. 默认绑定:

什么情况下使用默认绑定呢?独立函数调用。

  • 独立的函数调用我们可以理解成函数没有被绑定到某个对象上进行调用;

案例一:普通函数调用

  • 该函数直接被调用,并没有进行任何的对象关联;
  • 这种独立的函数调用会使用默认绑定,通常默认绑定时,函数中的 this 指向全局对象(window);
js
function foo() { console.log(this); // window } foo();

案例二:函数调用链(一个函数又调用另外一个函数)

  • 所有的函数调用都没有被绑定到某个对象上;
js
// 2.案例二: function test1() { console.log(this); // window test2(); } function test2() { console.log(this); // window test3() } function test3() { console.log(this); // window } test1();

案例三:将函数作为参数,传入到另一个函数中

js
function foo(func) { func() } function bar() { console.log(this); // window } foo(bar);

我们对案例进行一些修改,考虑一下打印结果是否会发生变化:

  • 这里的结果依然是 window,为什么呢?
  • 原因非常简单,在真正函数调用的位置,并没有进行任何的对象绑定,只是一个独立函数的调用;
js
function foo(func) { func() } var obj = { name: "why", bar: function() { console.log(this); // window } } foo(obj.bar);

2. 隐式绑定:

另外一种比较常见的调用方式是通过某个对象进行调用的:

  • 也就是它的调用位置中,是通过某个对象发起的函数调用。

案例一:通过对象调用函数

  • foo 的调用位置是 obj.foo() 方式进行调用的
  • 那么 foo 调用时 this 会隐式的被绑定到 obj 对象上
js
function foo() { console.log(this); // obj对象 } var obj = { name: "why", foo: foo } obj.foo();

案例二:案例一的变化

  • 我们通过 obj2 又引用了 obj1 对象,再通过 obj1 对象调用 foo 函数;
  • 那么 foo 调用的位置上其实还是 obj1 被绑定了 this
js
function foo() { console.log(this); // obj1 对象 } var obj1 = { name: "obj1", foo: foo } var obj2 = { name: "obj2", obj1: obj1 } obj2.obj1.foo();

案例三:隐式丢失

结果最终是 window,为什么是 window 呢?

  • 因为 foo 最终被调用的位置是 bar ,而 bar 在进行调用时没有绑定任何的对象,也就没有形成隐式绑定;
  • 相当于是一种默认绑定;
js
function foo() { console.log(this); } var obj1 = { name: "obj1", foo: foo } // 讲obj1的foo赋值给bar var bar = obj1.foo; bar();

3. 显示绑定

隐式绑定有一个前提条件:

  • 必须在调用的 对象内部 有一个对函数的引用(比如一个属性);
  • 如果没有这样的引用,在进行调用时,会报找不到该函数的错误;
  • 正是通过这个引用,间接的将this绑定到了这个对象上;

如果我们不希望在 对象内部 包含这个函数的引用,同时又希望在这个对象上进行强制调用,该怎么做呢?

  • JavaScript 所有的函数都可以使用 callapply 方法(这个和 Prototype 有关)。
    • 它们两个的区别这里不再展开;
    • 其实非常简单,第一个参数是相同的,后面的参数,apply 为数组,call 为参数列表;
  • 这两个函数的第一个参数都要求是一个对象,这个对象的作用是什么呢?就是给 this 准备的。
  • 在调用这个函数时,会将 this 绑定到这个传入的对象上。

因为上面的过程,我们明确的绑定了 this 指向的对象,所以称之为 显示绑定

案例一callapply

通过 call 或者 apply 绑定 this 对象

  • 显示绑定后,this 就会明确的指向绑定的对象
js
function foo() { console.log(this); } foo.call(window); // window foo.call({name: "why"}); // {name: "why"} foo.call(123); // Number对象,存放 123

案例二bind 函数

如果我们希望一个函数总是显示的绑定到一个对象上,我们可以使用 bind 函数:

function foo() { console.log(this); } var obj = { name: "why" } var bar = foo.bind(obj); bar(); // obj对象 bar(); // obj对象 bar(); // obj对象

案例三:内置函数

有些时候,我们会调用一些 JavaScript 的内置函数,或者一些第三方库中的内置函数。

  • 这些内置函数会要求我们传入另外一个函数;
  • 我们自己并不会显示的调用这些函数,而且 JavaScript 内部或者第三方库内部会帮助我们执行;
  • 这些函数中的 this 又是如何绑定的呢?
js
// 1. setTimeout中会传入一个函数,这个函数中的this通常是window setTimeout(function() { console.log(this); // window }, 1000); // 2. forEach map filter 等高阶函数 this 通常指向 window对象,但我们可以通过第二个参数改变 var names = ["abc", "cba", "nba"]; names.forEach(function(item) { console.log(this); // 三次window }); var names = ["abc", "cba", "nba"]; var obj = {name: "why"}; names.forEach(function(item) { console.log(this); // 三次obj对象 }, obj);

4. new 绑定

JavaScript 中的函数可以当做一个类的构造函数来使用,也就是使用 new 关键字。

使用 new 关键字来调用函数时,会执行如下的操作:

  1. 创建一个全新的对象;
  2. 这个新对象会被执行 Prototype 连接;
  3. 这个新对象会绑定到函数调用的 thisthis 的绑定在这个步骤完成)
  4. 如果函数没有返回其他对象,表达式会返回这个新对象;
js
// 创建Person function Person(name) { console.log(this); // Person {} this.name = name; // Person {name: "why"} } var p = new Person("why"); console.log(p);

5. 优先级

new绑定 > 显示绑定(bind) > 隐式绑定 > 默认绑定

PS: new 绑定后可以使用 bind 但是 bind 不会生效。 new 绑定后使用 callapply 会报错。

6. 规则之外

  • bind 绑定一个 null 或者 undefined 无效
  • 间接函数引用
js
function foo() { console.log(this); } var obj1 = { name: "obj1", foo: foo }; var obj2 = { name: "obj2" } obj1.foo(); // obj1对象 // 赋值(obj2.foo = obj1.foo)的结果是foo函数 // foo函数被直接调用,那么是默认绑定; (obj2.foo = obj1.foo)(); // window
  • ES6 箭头函数:箭头函数不使用 this 的四种标准规则(也就是不绑定 this ),而是根据外层作用域来决定 this
如果对你有用的话,可以打赏哦
打赏
ali pay
wechat pay

本文作者:叶继伟

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!