The Binding of “this”

时光毁灭记忆、已成空白 提交于 2021-01-29 03:16:19

问题


I'm learning how the value of "this" binds in JavaScript. In the following example, is it correct to say that "this" binds to the getFriends() method instead of the "details" object, which is the reason this.name = "" instead of this.name = "Joe"?

const details = {
    name: 'Joe',
    friends: [ 'Bob', 'Alex' ],
    getFriends: function() {
        this.friends.forEach( function( friend ) {
            console.log( this.name + " is friends with " + friend );
        } );
    }
};

details.getFriends();

// Output:
// is friends with Bob
// is friends with Alex

As I understand from my studies, "this" does not bind one level up in the parent scope, right? That's one benefit of using arrow functions, which bind "this" to the parent scope.


回答1:


Quoting from w3schools

In a method, this refers to the owner object. Alone, this refers to the global object. In a function, this refers to the global object. In a function, in strict mode, this is undefined. In an event, this refers to the element that received the event. Methods like call(), and apply() can refer this to any object.

https://www.w3schools.com/js/js_this.asp




回答2:


No when you run the code this way this is pointing to the global window object. You can console.log the value of this. To test it, you can also place a key of my_name on the window (don't use name, since that's used by the window). Now when you run the code, you'll see the global:

const details = {
    my_name: 'Joe',
    friends: [ 'Bob', 'Alex' ],
    getFriends: function() {
        this.friends.forEach( function( friend ) {
            console.log( this.my_name + " is friends with " + friend );
        } );
    }
};
window.my_name = "What?"
details.getFriends();

FYI: forEach takes a second value that you can use to specify what this will be in the callback. So this works for example:

const details = {
    my_name: 'Joe',
    friends: [ 'Bob', 'Alex' ],
    getFriends: function() {
        this.friends.forEach( function( friend ) {
            console.log( this.my_name + " is friends with " + friend );
        }, this ); //<-- pass this into the forEach
    }
};

details.getFriends();

Of course, you can always use an arrow function too.




回答3:


The keyword this refers to the current execution context.

By default this in a function is set to the global context which is always the window object in a browser:

function foo() {
  return this;
}

console.assert(foo() === window);

However when called as a constructor, the new operator sets this to an object created from the function prototype. That context is unique to each instance.

function foo() {
  return this;
}

console.assert(new foo() !== window);
console.assert(new foo() !== new foo());

When an object has a method, the this in that method is that object by default:

const question = {
  ask: function () {
    return this;
  }
};

console.assert(question.ask() === question);

Now here is why the current execution context matters.

If you take that method outside of its object then that method context will default to the global context:

const question = {
  ask: function () {
    return this;
  }
};

const ask = question.ask;

console.assert(ask() === window);

To solve that problem you can use either bind, call or apply:

const question = {
  ask: function () {
    return this;
  }
};

const ask = question.ask;

console.assert(ask.bind(question)() === question);
console.assert(ask.call(question) === question);
console.assert(ask.apply(question) === question);

You must have heard of arrow functions which bind this to the context that was available at the time the function was defined.

Previously we had to save this in a variable (usually called that) to refer to the right context. (Or use bind.)

function foo() {
  const that = this;
  // By the time the function executes, the execution context
  // will be different even though we invoked the function
  // as a constructor.
  setTimeout(function () {
    console.assert(that !== this);
    console.assert(this === window);
  }, 100);
}

new foo();

That technique became deprecated with arrow functions:

function foo() {
  setTimeout(() => {
    console.assert(this !== window);
  }, 100);
}

new foo();

However remember that functions are lexically scoped, so this will not give you the expected result:

const question = {
  ask: () => {
    return this;
  }
};

console.assert(question.ask() === window);

Why? At the time the arrow function was defined, the only context available in the lexical scope was the global context.



来源:https://stackoverflow.com/questions/56157472/the-binding-of-this

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!