博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
JavaScript中this终极理解(2)
阅读量:7191 次
发布时间:2019-06-29

本文共 3083 字,大约阅读时间需要 10 分钟。

在上一篇我们了解过每个函数的this是在调用的时候绑定的,完全却决于函数的调用位置(也就是函数的调用方法)。

1. 调用位置

在理解this的绑定过程之前,首先要理解调用位置:调用位置就是函数在代码中被调用的位置,而不是声明的位置。

找到函数的调用位置最重要的是要分析调用栈(就是为了到达当前执行位置所调用的所有函数)。我们关心的调用位置就在当前正在执行的函数的前一个调用中。

function baz() {    //当前调用栈是:baz    //因此,当前调用位置是全局作用域    console.log("baz");    bar(); //<--bar的调用位置}function bar() {    //当前调用栈是baz->bar    //因此,当前调用位置在baz中    console.log("bar")    foo(); }function foo() {    //当前调用栈是baz->bar->foo    //因此,当前调用位置在bar中    console.log("foo")}baz() //<---baz得调用位置

2. 绑定规则

那么调用位置如何决定this得绑定对象呢。首先,我们要找到调用位置,然后按照下面四条规则进行应用。首先我们先了解下这几条规则:

(1)默认绑定

首先介绍最常用得函数调用类型:独立函数调用。可以将这条规则看作是无法应用其他规则得默认规则。

function foo() {    console.log(this.a)}var a = 2;foo(); //2

上面得代码中,在全局环境中声明了一个变量a,那么a就是全局对象得一个同名属性。当调用foo()时,this.a被解析成了全局变量a。因为函数调用得时候应用了this得默认绑定,因此this指向全局对象。

在代码中,foo()是直接使用不带任何修饰的函数引用进行调用,因此只能使用默认绑定

如果使用严格模式,那么全局对象将无法使用默认绑定,因此this会绑定到undefined。

(2)隐式绑定

另一条需要考虑的规则是调用位置是否有上下文对象,或则说是否被某个对象拥有或者包含。

function foo() {    console.log(this.a)}var obj = {    a: 2,    foo: foo}obj.foo(); //2

首先需要注意的是foo()的声明方式,以及之后是如何被当作引用属性添加到obj中的。但是无论是直接在obj定义还是先定义再添加为引用属性,这个函数严格来说都不属于obj对象。

调用位置会使用obj上下文来引用函数,因此你可以说函数被调用时,obj对象"拥有"或者"包含"它。

当foo()调用时,它的落脚点确实指向obj对象。当函数引用有上下文对象时,隐式绑定规则会把函数调用中的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绑定到全局对象或者undefined上,这要取决于是否式严格模式。

function foo() {    console.log(this.a)}var obj = {    a:2,    foo: foo}var bar = obj.foo; //函数别名var a = 'oops, globas'; //a式全局对象的属性bar(); //'oops, globas'

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

再看下面一组代码:

function foo() {    console.log(this.a)}function doFoo(fn) {    fn();}var obj = {    a:2,    foo:foo}var a = 'oops,global'doFoo(obj.foo); //'oops, global'

参数传递其实就是一种隐式赋值,因此我们传入函数时也会被隐式赋值,所以结果和之前一样。

(3)显示绑定

我们可以使用函数的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的绑定对象,这个原始值会被转换成对象形式,也就是(new String(..)),这通常被称为"装箱"。

function foo() {    console.log(this.a)}var obj = {    a:2}var bar = function() {    foo.call(obj)}bar();//2setTimeout(bar, 10); //2bar.call(window); //2

如上,我们不管怎么调用bar,它总会手动在obj上调用foo,这种绑定时一种显示的强制绑定,因此我们称为为硬绑定

硬绑定的典型应用场景就是创建一个包裹函数,传入所有的参数并返回接收到的所有值:

function foo(something) {    console.log(this.a, something)    return this.a + something}//简单的辅助绑定函数function bind(fn, obj) {    return function() {        return fn.apply(obj, arguments)    }}var obj = {    a: 2};var bar = bind(foo, obj)var b = bar(3) //2, 3console.log(b) //5

bind(...)会返回一个硬编码的新函数,它会把参数设置为this的上下文并调用原始函数。

(4) new绑定

在JavaScript中,构造函数只是一些使用new操作符时被调用的函数。他们并不会属于某个类,也不会实例化一个类。实际上他们甚至都不能说时一种特殊的函数类型。只是被new操作符调用的普通函数。

使用new来调用函数时会执行以下操作:

1. 创建一个全新的对象2. 这个新对象会被执行[[原型]]连接3. 这个新对象会绑定到函数调用的this4. 如果函数没有返回其他对象,那么new表达式中的函数调用会自动返回这个新对象。

转载地址:http://mzxkm.baihongyu.com/

你可能感兴趣的文章
[学习笔记]阶和原根
查看>>
js事件委托
查看>>
计算机硬件
查看>>
gattAttribute_t 含义 中文解释
查看>>
jquery 选择器汇总
查看>>
Nodejs 学习资料
查看>>
设计模式(三) 抽象工厂模式
查看>>
置换群的快速幂运算
查看>>
post7
查看>>
Spring.net 学习IOC------准备
查看>>
zend studio xdebug配置详解
查看>>
pydoc用法
查看>>
静态变量、全局变量和局部变量
查看>>
内容提供者------数据库
查看>>
Spark之Pipeline处理模式
查看>>
Errors running builder 'Integrated External Tool Builder' on project xxx
查看>>
SpringMVC学习笔记(三)
查看>>
数据结构之线性表的查找
查看>>
Google论文之三----MapReduce
查看>>
SpringMVC的拦截器不起作用原因
查看>>