资讯专栏INFORMATION COLUMN

关于this的全面解析(上)

caige / 2639人阅读

摘要:关于的全面解析下页面链接的调用位置调用位置就是函数在代码中被调用的位置而不是声明的位置,寻找调用位置就是寻找函数被调用的位置,最重要的是分析调用栈就是为了到达当前执行位置所调用的所有函数。因此,调用函数时被绑定到这个对象上,所以和是一样的。

关于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); //<-this指向全局作用域
    }
    var a = 2;
    foo(); //<-foo调用位置

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

function foz() {
        "use strict";
        console.log(this.a); //<--严格模式下不能将全局对象用于绑定

    }
    foz(); //TypeError

2 隐藏绑定

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

function foa() {
        console.log(this.a);
    }
    var foaObj = {
        a: "Hello",
        foa: foa //<--foa函数调用位置
    }
    foaObj.foa();

foa函数在严格意义上来说不属于foaObj对象。然而,调用位置会使用foaObj上下文来引用函数,因此可以判断为函数调用时,foaObj对象包含并引用它。

当函数引用有上下文对象时,隐式绑定规则会把函数调用中的this绑定到这个上下文对象。因此,调用foa函数时this被绑定到foaObj这个对象上,所以this.a 和 foaObj.a 是一样的。

然而,有一个常见的this绑定问题就是被隐式绑定的函数会丢失绑定对象,也就是说它会应用默认绑定,从而把this绑定到全局对象或undefined上。

function fob() {
        console.log(this.a);
    }
    var fobObj = {
        a: "Hello",
        fob: fob
    }
    var focObj = fobObj.fob;
    var a = 1; //a是全局对象的属性
    focObj();

focObj引用实际上是fob函数,所以this绑定的是全局对象中的a。

3 显式绑定

就像我们刚才看到的那样,在分析隐式绑定时,我们必须在一个对象内部包含一个指向函数的属性,并通过这个属性间接引用函数,从而把this间接绑定到这个对象上。

JavaScript提供的绝大多数函数以及你自己创建的所有函数都可以使用call(…) 和 apply (…) 方法。

这两个方法的第一参数是一个对象,是给this准备的,接着在调用函数时将其绑定到this。因为你可以直接指定this的绑定对象,因此我们称之为显式绑定。

function fod() {
        console.log(this.a);
    }
    var fodObj = {
        a: 2
    }
    fod.call(fodObj); //2

通过fod.call(…)方法,可以强制把this绑定到fodObj这个对象上。

然而,显示绑定仍然无法解决之前提出的丢失绑定问题。

但是显示绑定的一个变种可以解决这个问题。

 function foh() {
        console.log(this.a);
    }
    var fohObj = {
        a: 2
    }
    var baa = function() {
        foh.call(fohObj);
    }
    baa(); //2
    setTimeout(baa, 100); //2
    baa.call(window); //2

我们创建了一个baa函数,并在它的内部手动调用了foh.call(fohObj),因此强制把foh的this绑定到了fohObj上。无论之后如何调用函数baa,它总会手动在fohObj上调用foh。这种绑定是一种显示的强制绑定,因此我们称之为硬绑定。

硬绑定的典型应用场景就是创建一个包裹函数,负责接收参数并返回值。

function foi(something) {
        console.log(this.a, something);
        return this.a + something;
    }
    var foiObj = {
        a: 2
    }
    var bae = function() {
        return foi.apply(foiObj, arguments);
    }
    var b = bae(3);
    console.log(b);

另一种使用方法是创建一个可以重复使用的辅助函数。

function fol(something) {
        console.log(this.a, something);
        return this.a + something;
    }

    function bind(fn, obj) {
        return function() {
            return fn.apply(obj, arguments);
        }
    }
    var folObj = {
        a: 3
    }
    var bac = bind(fol, folObj);
    var c = bac(4);
    console.log(c);

4 new绑定

使用new来调用函数,或者说发生构造函数调用时,会自动执行下面的操作。

1) 创建(或者说构造)一个全新的对象。
2) 这个新对象会被执行Prototype连接。
3) 这个新对象会绑定到函数调用的this。
4) 如果函数没有返回其他对象,那么new表达式中的函数调用会自动返回这个新对象。

function abc(a) {
        this.a = a;
    }
    var x = new abc(10);
    console.log(x.a);

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/79663.html

相关文章

  • 关于this全面解析(下)

    摘要:关于的全棉解析上的文章地址判断函数是否在中调用绑定如果是的话绑定的是新创建的对象。显而易见,这种方式可能会导致许多难以分析和追踪的。默认在严格模式下绑定到,否则绑定到全局对象。 关于this的全棉解析(上)的文章地址 判断this 函数是否在new中调用(new绑定)?如果是的话this绑定的是新创建的对象。 bar = new foo() 函数是否通过call、apply(显式绑定...

    philadelphia 评论0 收藏0
  • this全面解析(一)

    摘要:调用栈就是为了到达当前执行位置所调用的所有函数。由于无法控制回调函数的执行方式,因此就没有办法控制调用位置得到期望的绑定,下一节我们会介绍如何通过固定来修复这个问题。 在《你不知道的this》中我们排除了对于this的错误理解,并且明白了每个函数的this是在调用时绑定的,完全取决于函数的调用位置。在本节中我们主要介绍一下几个主要内容: 什么是调用位置 绑定规则 this词法 调用...

    darry 评论0 收藏0
  • this全面解析(二)

    摘要:在传统的面向类的语言中,构造函数是类中的一些特殊方法,使用初始化类是会调用类中的构造函数。 在上一节中我们详细介绍了this的两种绑定方式,默认绑定和隐式绑定,在这一节我们继续介绍this的另外两种绑定方式显示绑定和new绑定。那么,我们要解决的问题当然就是上一节中我们提到的:this丢失! 显式绑定 在隐式绑定中,我们必须在一个对象的内部包含一个指向函数的属性,并通过这个属性间接引用...

    iflove 评论0 收藏0
  • Java杂记17—String全面解析

    摘要:所以也就是说在没有的基础上,执行代码会在串池中创建一个,也会在堆内存中再出来一个。不可变性的优点安全性字符串不可变安全性的考虑处于两个方面,数据安全和线程安全。 摘要: String基本特性,String源码,为什么String不可变? 前言 基于字符串String在java中的地位,关于String的常识性知识就不多做介绍了,我们先来看一段代码 public class Test {...

    jeffrey_up 评论0 收藏0
  • 剖析非同质化代币ERC721-全面解析ERC721标准

    摘要:本文就来剖析下什么是是什么在创建代币一篇,我们讲到过代币,和一样,同样是一个代币标准,官方简要解释是,简写为,多翻译为非同质代币。返回合约代币符号,尽管是可选,但强烈建议实现,即便是返回空字符串。 本文首发于深入浅出区块链社区原文链接:剖析非同质化代币ERC721-全面解析ERC721标准原文已更新,请读者前往原文阅读 什么是ERC-721?现在我们看到的各种加密猫猫狗狗都是基于ERC...

    Sike 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<