【问题标题】:`this` binding and call-site of `this``this` 绑定和 `this` 的调用点
【发布时间】:2020-01-15 16:28:55
【问题描述】:

函数是否可以访问其调用站点中的所有信息?

我认为也许一个函数可以访问其调用站点范围,否则我可能会弄错,所以我希望得到反馈和解释。

function bar() {
    // call-stack is: `bar`
    // so, our call-site is in the global scope
    let a = “heyo”;
    console.log( “bar” );
    foo(); // ← call-site for `foo`
}

function foo() {
    // call-stack is: `bar` -> `foo`
    // so, our call-site is in `bar`
    console.log( “foo “ + this.a );
}
bar(); // ← call-site for `bar`

这里,this.a 返回 undefined,但如果它的调用站点是声明 a 的位置,它不应该访问变量 a 吗?

【问题讨论】:

    标签: javascript object binding this


    【解决方案1】:

    foo() 中的 this 指的是调用它的上下文,在 sn-p 中它是在没有定义变量 a 的全局上下文中调用的,所以你得到 @987654324 @。

    您还使用let 声明了a,它创建了一个块范围的变量。如果您在函数外部使用var 声明a,它将在全局范围内创建一个变量。

    在我的 sn-p 中,我创建了一个具有两个属性 afoo 方法的对象。现在,如果您在obj 的上下文中调用obj.foo() 方法,this 将指向obj 对象,因为我在foo() 的全局调用中使用var 声明了a,将打印来自全局范围的a 的值:

    var a = "from global";
    
    function bar() {
       //lexical context is global
       //let defines a variable in the enclosing scope
       let a = "heyo";
       console.log( "bar" );
       //foo is called within the global context, so this points to global object.
       foo(); 
       let obj = {a : "from obj", foo};
       // or foo.bind(obj); which sets the this context
       //now foo is called in the context of object "obj" so this points to obj
       obj.foo(); 
    }
    function foo() {
       console.log( "foo " + this.a );
    }
    bar(); 

    【讨论】:

    • 很棒的解释!非常感谢。所以使用 let 意味着它不能被块范围之外的任何东西访问,对吗?
    • @JoeSpinelli 是的,这是正确的,它的范围仅限于封闭块。 const 也是如此。
    • 但是如果 let 是在全局范围内声明的,为什么它不起作用呢?因为 this 是指全局上下文,而 let 是在全局范围内声明的?
    • @Joe Spinelli 如果您使用 var 在全局范围内声明变量,那么它将被添加为全局对象的属性。但是使用 let 或 const 做同样的事情并不会使它成为全局对象的属性。因此,如果您执行了let a = ...,当this 指向全局上下文时,将无法从this.a 访问。
    • 明白了!谢谢
    【解决方案2】:

    首先,提出的问题根本不包含this,这很有趣,因为它是所提供问题的一个神奇例外。

    JavaScript 是词法范围的。也就是说,函数可以访问它们定义的范围,而不是访问当前堆栈任意位置的值。

    const rememberX = (x) => () => x;
    // a function that takes a value and returns a function
    // when the inner function is called, it returns the value
    
    let x = 5;
    let x42 = rememberX(42);
    let x10 = rememberX(10);
    x = 20;
    
    x42(); // 42
    x10(); // 10
    

    词法作用域提供了闭包的概念,这就是上述工作的原因。这使得它不像传统环境中的其他语言,而更像是一种函数式编程语言。

    有趣的是,this 是打破这种模式的唯一值。它非常晚绑定,并在执行时确定。

    class Person {
      constructor (name) {
        this.type = "person";
        this.name = name;
      }
    
      sayHi () {
        console.log(`${this.name}, the ${this.type}, says “Hi”.`);
      }
    }
    
    
    const bob = new Person("Bob");
    bob.sayHi(); // "Bob, the person, says “Hi”."
    
    const spot = {
      type: "dog",
      name: "Spot",
      sayHi: bob.sayHi
    };
    
    spot.sayHi(); // "Spot, the dog, says “Hi”."
    
    bob.sayHi.apply({
      type: "ape",
      name: "Grape"
    }); // "Grape, the ape, says “Hi”."
    // apply is a method that all functions have,
    // which sets `this` for this invocation
    
    
    const bind = function (method, overriddenThis) {
      return function (...args) {
        return method.apply(overriddenThis, args);
      };
    };
    // bind also exists as a method on functions,
    // to prevent `this` from being overwritten, using closure
    
    const sayHi = bind(bob.sayHi, bob);
    sayHi.apply(spot); // "Bob, the person, says “Hi”."
    

    因此,您的基于堆栈的调用无法按预期工作是有原因的,为什么this 与您的预期不同,以及如何处理它。

    【讨论】:

    • 谢谢!很好的解释:)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-12-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-01-26
    • 2021-05-07
    相关资源
    最近更新 更多