JS中this的绑定规则
栏目: JavaScript · 发布时间: 5年前
内容简介:我们明白每个函数的 this 是在调用 时被绑定的,完全取决于函数的调用位置(也就是函数的调用方法)。下面我们来看看到底什么是调用栈和调用位置:首先要介绍的是最常用的函数调用类型:独立函数调用。可以把这条规则看作是无法应用 其他规则时的默认规则。
我们明白每个函数的 this 是在调用 时被绑定的,完全取决于函数的调用位置(也就是函数的调用方法)。 在理解 this 的绑定过程之前,首先要理解调用位置:调用位置就是函数在代码中被调用的 位置(而不是声明的位置)。 最重要的是要分析调用栈(就是为了到达当前执行位置所调用的所有函数)。我们关心的 调用位置就在当前正在执行的函数的前一个调用中。
调用位置
下面我们来看看到底什么是调用栈和调用位置:
function baz() { // 当前调用栈是:baz // 因此,当前调用位置是全局作用域 console.log( "baz" ); bar(); // <-- bar 的调用位置 } function bar() { // 当前调用栈是 baz -> bar // 因此,当前调用位置在 baz 中 console.log( "bar" ); foo(); // <-- foo 的调用位置 } function foo() { // 当前调用栈是 baz -> bar -> foo // 因此,当前调用位置在 bar 中 console.log( "foo" ); } baz(); // <-- baz 的调用位置 复制代码
绑定规则 (注:皆不考虑严格模式)
1. 默认绑定
首先要介绍的是最常用的函数调用类型:独立函数调用。可以把这条规则看作是无法应用 其他规则时的默认规则。
思考一下下面的代码:
function foo() { console.log( this.a ); } var a = 2; foo(); // 2 复制代码
我们可以看到当调用 foo() 时,this.a 被解析成了全局变量 a。为什么?因为在本 例中,函数调用时应用了 this 的默认绑定,因此 this 指向 全局对象 。
2. 隐式绑定
另一条需要考虑的规则是调用位置 是否有上下文对象,或者说是否被某个对象拥有或者包 含 ,不过这种说法可能会造成一些误导。
思考下面的代码:
function foo() { console.log( this.a ); } var obj = { a: 2, foo: foo }; obj.foo(); // 2 复制代码
当函数引 用( 注:foo是引用函数,既指向function foo(){···}这个函数,而foo()才是调用函数 )有上下文对象时,隐式绑定规则会把函数调用中的 this 绑定到这个上下文对象。因为调 用 foo() 时 this 被绑定到 obj,因此 this.a 和 obj.a 是一样的。
对象属性引用链中只有最顶层或者说最后一层会影响调用位置。举例来说:
function foo() { console.log( this.a ); } var obj2 = { a: 42, foo: foo }; var obj1 = { a: 2, obj2: obj2 }; obj1.obj2.foo(); // 42 复制代码
隐式丢失 (如果到这有点晕可以先跳过这个)
这里我们还要谈到隐式丢失这个问题。一个最常见的 this 绑定问题就是被隐式绑定的函数会丢失绑定对象,也就是说它会应用默认绑定,从而把 this 绑定到全局对象。
思考下面的代码:
function foo() { console.log( this.a ); } var obj = { a: 2, foo: foo }; var bar = obj.foo; // 函数别名! var a = "oops, global"; // a 是全局对象的属性 bar(); // "oops, global" 复制代码
虽然 bar 是 obj.foo 的一个引用,但是实际上,它引用的是 foo 函数本身( 注:var bar = obj.foo;相当于给这个函数又取了一个名字,所以bar();=foo(); ),因此此时的 bar() 其实是一个不带任何修饰的函数调用,因此应用了默认绑定。
3. 显式绑定
JavaScript 提供的绝大多数函数以及你自 己创建的所有函数都可以使用 call(..) 和 apply(..) 方法 。 这两个方法是如何工作的呢? 它们的第一个参数是一个对象,它们会把这个对象绑定到 this,接着在调用函数时指定这个 this 。因为你可以直接指定 this 的绑定对象,因此我 们称之为显式绑定。
思考下面的代码:
function foo() { console.log( this.a ); } var obj = { a:2 }; foo.call( obj ); // 2 复制代码
通过 foo.call(..),我们可以在调用 foo 时强制把它的 this 绑定到 obj 上。 ( 注:从 this 绑定的角度来说,call(..) 和 apply(..)还有bind(...) 是一样的,它们的区别体现 在其他的参数上,但是现在我们不用考虑这些 。)
4. new绑定
在 JavaScript 中,构造函数只是一些 使用 new 操作符时被调用的函数。它们并不会属于某个类,也不会实例化一个类。实际上, 它们甚至都不能说是一种特殊的函数类型,它们 只是被 new 操作符调用的普通函数而已 。
使用 new 来调用函数,或者说发生构造函数调用时,会自动执行下面的操作。(这里我们可以不用管第二步)
- 创建(或者说构造)一个全新的对象。
- 这个新对象会被执行 [[ 原型 ]] 连接。
- 这个新对象会绑定到函数调用的 this。
- 如果函数没有返回其他对象,那么 new 表达式中的函数调用会自动返回这个新对象。
思考下面的代码:
function foo(a) { this.a = a; } var bar = new foo(2); console.log( bar.a ); // 2 复制代码
使用 new 来调用 foo(..) 时,我们 会构造一个新对象并把它绑定到 foo(..) 调用中的 this 上 。new 是最后一种可以影响函数调用时 this 绑定行为的方法,我们称之为 new 绑定。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 神经规则引擎:让符号规则学会变通
- Golang Echo数据绑定中time.Time类型绑定失败
- ???? 图解 == 操作符规则和不同类型间转换规则
- 如何在Symfony的表单中添加一个未绑定字段,否则绑定到一个实体?
- Wireshark 【OSI二层】抓包过滤规则和显示过滤规则实例
- js双向绑定
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Design systems
Not all design systems are equally effective. Some can generate coherent user experiences, others produce confusing patchwork designs. Some inspire teams to contribute to them, others are neglected. S......一起来看看 《Design systems》 这本书的介绍吧!
HTML 压缩/解压工具
在线压缩/解压 HTML 代码
RGB转16进制工具
RGB HEX 互转工具