美文网首页
[javascript] this 的绑定机制

[javascript] this 的绑定机制

作者: 小黄人get徐先生 | 来源:发表于2020-01-13 11:47 被阅读0次

为什么要学习 this?

  1. 阅读源码需要;很多 js 库里面都有关于 this 以及 this 绑定的用法,所以我们需要深入学习了解 this 帮助我们高效阅读源码。
  2. 提高编程能力;this 提供了一种更优雅的方式来隐式“传递”一个对象的引用,我们可以使用 this 编写出更加高级优雅的代码。

关于 this 的误解?

  1. 指向自身
    从字面意思上理解确实是说得通的。在我刚刚接触 javascript this 的时候,我也以为它是指向于自身,这个主要是受到以前 python 编程经验的影响,觉得 this 指向实例本身。事实上,javascript 通过 new 实例化操作后的实例也确实指向于实例本身,不过 javascript 里面的 this 的情况更为多样和多变。

  2. 指向作用域
    this 指向作用域,有些情况下是正确的,但是在其他情况下却是错误的。

function foo() {
  const a = 2;
  this.bar();
}

function bar() {
  console.log(this.a);
}

foo();

打印出来的结果是 undefined,这个我们想象的不一样;我们以为应该是 2。所以 this 并不是指向作用域的。

this 到底是什么?

this 是在运行时进行绑定的,并不是编写时绑定,它的上下文取决于函数调用时的各种条件。this 的绑定和函数声明的位置没有任何关系,只取决于函数的调用方式(调用位置)

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

调用位置

在正式开始 this 学习之前,我们先通过下面这个例子学习下调用位置。

function baz() {
  // 当前调用栈是:baz,因此,当前调用位置是全局作用域
  console.log("baz");
  bar();
}

function bar() {
  // 当前调用栈是 baz -> bar,因此,当前调用位置在 baz 中
  console.log("bar");
  foo();
}

function foo() {
  // 当前调用栈是 baz -> bar -> foo,因此,当前调用位置在 bar 中
  console.log("foo");
}

baz();

this 的绑定规则

  1. 默认绑定(独立函数调用)
    可以把这条规则看做是无法应用其他规则的默认规则。
    如上面的调用位置的例子,如果我们在每个函数里面打印 this,我们会发现 this 都执行 Window 对象。(严格模式下 thisundefined
function baz() {
  console.log(this, "baz");
  bar();
}

function bar() {
  console.log(this, "bar");
  foo();
}

function foo() {
  console.log(this, "foo");
}

baz();
  1. 隐式调用(调用位置是否有上下文对象,或者说是否被某个对象拥有或包围,不过这种说法可能会造成一些误导)
function foo() {
  console.log(this.a);
}

const obj = {
  a: 2,
  foo: foo
};

obj.foo(); // 2

对象属性引用链只有最顶层或者最后一层会影响调用位置。

function foo() {
  console.log(this.a);
}

const obj2 = {
  a: 42,
  foo: foo
};

const obj1 = {
  a: 2,
  obj2: obj2
};

obj1.obj2.foo(); // 42
隐式丢失
function foo() {
  console.log(this.a);
}

const obj = {
  a: 2,
  foo: foo
};

const bar = obj.foo;

const a = 'test';

bar(); // "test";

虽然 barobj.foo 的一个引用,但实际上,它引用的是 foo 函数本身,因此此时 bar() 其实是一个不带任何修饰的函数调用,因此应用了默认绑定。

function foo() {
  console.log(this.a);
}

function doFoo(fn) {
  // 这里存在一个隐性赋值,fn = obj.foo
  fn();
}

const obj = {
  a: 2,
  foo: foo
};

const a = "test";

doFoo(obj.foo); // "test"

参数传递其实就是一种隐式赋值,这是我们需要注意的,这样的问题开发中如果不太清楚,很容易迷惑。

  1. 显示绑定
    直接指定 this 的绑定对象,有三种方法:bindcallapply
function foo() {
  console.log(this.a);
}

const obj = {
  a: 2
};

foo.call(obj); // 2

通过 foo.call(...),我们可以在调用 foo 时强制把 this 绑定到 obj 上。

但是显示绑定也不能解决绑定丢失的问题,通过借助 bind 方法我们可以实现硬绑定。

  1. new 绑定
function foo(a) {
  this.a = a;
}

const bar = new foo(2);
console.log(bar.a); // 2

使用 new 来调用 foo(...) 时,我们会构造一个新对象并把它绑定到 foo(...) 调用的 this 上。

绑定优先级

new 绑定 > 显示绑定 > 隐式绑定 > 默认绑定

例外:关于箭头函数的 this

箭头函数是一种无法使用我们上面四种规则的特殊函数类型。

function foo() {
  return (a) => {
    console.log(this.a);
  }
}

const obj1 = {
  a: 2
};

const obj2 = {
  a: 3
};

const bar = foo.call(obj1);
bar.call(obj2); // 2,不是 3

foo() 内部创建的箭头函数会捕获调用时 foo()this。由于 foo()this 绑定到了 obj1barthis 也会绑定到 obj1,箭头函数的绑定无法被修改。(new 也不行)。

箭头函数常用于回调函数中,例如事件处理或定时器:

function foo() {
  setTimeout(() => { console.log(this.a) }, 100);
}

const obj = { a: 2 };

foo.call(obj); // 2

总结:

  1. 由 new 调用?绑定到新创建的对象。
  2. 由 call 或者 apply(或者 bind)调用?绑定到指定的对象。
  3. 由上下文对象调用?绑定到那个上下文对象。
  4. 默认:在严格模式下绑定到 undefined,否则绑定到全局对象。

参考:《你不知道的 Javascript(上卷)》

相关文章

  • [javascript] this 的绑定机制

    为什么要学习 this? 阅读源码需要;很多 js 库里面都有关于 this 以及 this 绑定的用法,所以我们...

  • JavaScript的事件机制

    在JavaScript的学习中,我们经常会遇到JavaScript的事件机制,例如,事件绑定、事件监听、事件委托(...

  • JavaScript this

    对于那些没有花时间学习 this 绑定机制如何工作的 JavaScript 开发者来说,this 绑定一直是困惑的...

  • jQuery事件机制

    jQuery的事件机制,指的是:jQuery对JavaScript操作DOM事件的封装,包括了:事件绑定、事件解绑...

  • JavaScript绑定事件的三种方式

    @(javascript)[JavaScript事件绑定] JavaScript绑定事件的三种方式 使用内联 使用...

  • js事件深入学习

    JavaScript的事件机制包含,事件绑定、事件监听、事件委托(事件代理)等 js中的事件流 ① .冒泡:当下级...

  • AWTK javascript绑定原理

    AWTK javascript绑定原理 一、基本架构 Javascript的绑定方法和lua的绑定方法有些不同,主...

  • 实例解析javascript中的this

    this关键字是Javascript中最复杂的机制之一。对this的主要理解,即知道在this的绑定对象。不管是在...

  • 你不知道的js中关于this绑定机制的解析[看完还不懂算我输]

    言 最近正在看《你不知道的JavaScript》,里面关于this绑定机制的部分讲的特别好,很清晰,这部分对我们j...

  • Angular.js问题总结

    1.angular 的数据绑定采用什么机制?详述原理 脏检查机制。 双向数据绑定是 AngularJS 的核心机制...

网友评论

      本文标题:[javascript] this 的绑定机制

      本文链接:https://www.haomeiwen.com/subject/wgqyactx.html