What scope is 'this' of a node.js object when it gets called at the top-level?

前端 未结 3 1902
太阳男子
太阳男子 2021-01-27 06:03

I read \"Why and how to bind methods in your React component classes?\" and grabbed the basic idea of how different is this by the scopes it gets called.

I

相关标签:
3条回答
  • 2021-01-27 06:22

    there's a function print

    when you call it like this

    some.print();
    

    that effectively gets translated to this

    print.call(some, ...args)
    

    When you just pull it out

    const going = some.print
    

    You've just gotten a reference to the standalone function so calling it with

    going()
    

    Is the same as calling

    print();
    

    It's the . between some and print that is magically passing some as this to print. going() has no period so no this is passed.

    Note that you could assign going to an object and use the period operator to do the magic "pass the thing on the left as this" operation

    function print() {
      console.log(this);
    }
    
    const someObj = {
      foo: print,
    };
    
    someObj.foo();

    all the class keyword does is help assign print to Something's prototype

    class Something {
      print() { console.log(this); }
    }
    

    is the same as

    function Something() {}  // the constructor
    Something.prototype.print = function() { console.log(this} );
    

    which is also effectively the same as this

    function print() { console.log(this); }
    function Something() {}  // the constructor
    Something.prototype.print = print;
    

    Saikat showed using bind. Bind effectively makes a new function that wraps your old function. Example

    const going = print.bind(foo);
    

    Is nearly the same as

    function createAFuncitonThatPassesAFixedThis(fn, objectToUseAsThis) {
      return function(...args) {
        return fn.call(objectToUseAsThis, ...args);
      }
    }
    
    const going = createAFunctionThatPassesAFixedThis(print, foo);
    going();  // calls print with 'foo' as this
    

    Some of the other answers have appear to have a fundamental mis-understanding of this. this is not the "owner" nor is this the current object. this is just effectively another variable inside a function. It gets set automatically if you use the . operator with a function call. so a . b() sets this to a. if the dot operator did not exist you could still set this by using call or apply as in somefuntion.call(valueForThis, ...args) or somefunction.apply(valueForThis, [...args]);

    function print() {
      console.log(this.name);
    }
    
    print.call({name: "test"});  // prints test
    
    const foo = {
      name: "foo",
      bar: print,
    };
    
    foo.bar();  // prints foo
    
    function Something(name) {
      this.name = name;
    }
    Something.prototype.someFunc = print;
    
    const s = new Something("something");
    s.someFunc();  // prints something
    
    s.foobar = print;
    s.foobar();   // prints something

    Also note that ES6 added the => arrow operator which binds this to whatever it was when the function is created. In other words

    const foo = () => { console.log(this); }
    

    Is the same as

    const foo = function() { console.log(this); }.bind(this);
    

    it should also be clear that both functions made with bind and arrow functions you can not change this using call or apply since effectively they made a wrapper that always sets this to what it was when the wrapper was made just like createAFuncitonThatPassesAFixedThis did above.

    Let's add some comments to show what I mean

    function createAFuncitonThatPassesAFixedThis(fn, objectToUseAsThis) {
    
      // return a function that calls fn with objectToUsAsThis as this
      return function(...args) {
    
        // when we arrive here from the "going.call" line below
        // `this` will be "someObject"
        // but on the next line we're passing "objectToUseAsThis" to fn as this
        // so "someObject" is effectively ignored.
    
        return fn.call(objectToUseAsThis, ...args);
      }
    }
    
    const going = createAFunctionThatPassesAFixedThis(print, foo);
    going.call(someObject);  // pass some object as `this`
    

    One more thing. It's very common to use bind to make a function for an event listener or callback. It's very common in React. Example

    class Component extends React.Component {
      constructor(props) {
        super(props);
        this.handleClick = this.handleClick.bind(this);
      }
      handleClick() {
        // 'this' will be the current component since we bound 'this' above
        console.log("was clicked");
      }
      render() {
        return (<button onClick={this.handleClick}>click me</button>);
      }
    }
    

    This would be handled in at least 3 common ways.

    1. Using bind in the constructor (see above)

    2. Using bind in render

        render() {
          return (<button onClick={this.handleClick.bind(this)}>click me</button>);
        }
      
    3. Using arrow functions in render

        render() {
          return (<button onClick={() => { this.handleClick(); }}>click me</button>);
        }
      

    Above I showed what bind and => actually do with createAFuncitonThatPassesAFixedThis. They create a new function. So that should make it clear that if use style 2 or 3 above when every single time render is called a new function is created. If you use style 1 then a new function is only created in the constructor. It's a style issue as to whether or not that's important. Creating more functions means more garbage which means a possibly slower webpage but in most cases unless you have a crazy complicated webpage that is rendering constantly it probably doesn't matter which of the 3 ways you do it.

    0 讨论(0)
  • 2021-01-27 06:24

    you should correct this line to

    const going = some.print.bind(some);
    

    Otherwise going is getting a different scope and its not an member of some object so this is undefined

    NOTE

    this refers to the current class object. And going is not a member of any class so this is undefined

    0 讨论(0)
  • 2021-01-27 06:40
    const going = some.print;
    going(); // 'this' is undefined.
    

    Here, in the first line, you're storing a reference to the print function (property) of the some object in the variable going.

    Note, you're just storing the reference to that particular property (print), not along with the owner (some).

    this refers to the 'owner' always.

    So when you execute going, the owner (this) is unknown (or call it undefined).

    0 讨论(0)
提交回复
热议问题