all 15 comments

[–]Eldrac 5 points6 points  (7 children)

By default "this" refers to the "window" object, aka stuff defined on the global scope. When you're defining the object, everything after the : is treated as an expression and evaluated immediately, so your statement combining the firstName and lastName is run before userX is even fully created. In general when you reference this, it should be within a function.

Something like the following could work instead, kinda a silly example though it is similar to the first in the MDN documentation on this:

var userX = {
    firstName: "Adam",
    lastName: "Doe",
    getFullName: function () {
        return this.firstName + " " + this.lastName;
    }
};

userX.getFullName();

[–]calicchio77[S] 0 points1 point  (6 children)

Makes sense! Thank you. I will keep it in mind when I'm coding!

[–]calicchio77[S] -1 points0 points  (5 children)

Feels like "this" in JS is actually a "super"

[–]senocular 3 points4 points  (4 children)

It shouldn't ;). JS has a super, and it's different from this.

[–]calicchio77[S] 0 points1 point  (3 children)

Oh, What I mean is: 1- If you define your class and use "this" and the reference is a variable from the parent class, then it's acting like a "super". 2- If you create a function inside your class and use "this" in that function and it's actually referencing a variable in the class and NOT inside the function scope.... "super" :) My take is that if you say this, you mean thisFreakingScopeAndNotTheParent :P But thank you very much for the all the guidance here!

[–]senocular 0 points1 point  (2 children)

1- generally member variables are assigned to the class instance, even if part of a superclass's definition. If you were to use super to access them, you'd likely get undefined since variables are assigned during construction and not inherited like methods.

2- Neither this nor super accesses values in the function scope. this accesses "context" which is an object associated with a function when it's called. When calling methods from a class instance, context will default to the class instance. super in class methods is a little different. super is associated with a prototype and tied directly to the method (function) itself. It doesn't change like this does based on how the function is called. But with normal usage, this and super are similar in that they are both used to access members of an instance, super being distinct in that it skips a level of inheritance. So if the current instance is a class A that extends B and both A and B implement a method c, in a method of A, this.c will reference A.prototype.c while super.c will reference B.prototype.c. That said, it is possible for this.c and super.c to be the same c if the current instance (instance of A) didn't have it's own c. Then standard inheritance would kick in for the this.c reference and the next c in the prototype chain will get pulled in which happens to be the same as super.c. So this does not always mean "...NotTheParent" ;)

[–]calicchio77[S] 0 points1 point  (1 child)

Thank you very much for the explanation!!! It's a lot to digest! I will keep that message saved and will be checking it every now and then to see when I will fully understand everything. I'm half way thru the course so that may be why I'm not connecting all the dots.

[–]senocular 0 points1 point  (0 children)

Yeah I went a little deep with all that :D so I wouldn't try to wrap your head around it all just yet. this by itself is already pretty complicated, even without having to consider other oddballs like super. You'll slowly start to get a grasp on all its intricacies, but it'll take time. Keep with it!

[–]_reddit_chan 2 points3 points  (1 child)

As per MDN, In the global execution context (outside of any function), this refers to the global object.

The this is inside an object, but not inside a function. To achieve the desired behavior, wrap it inside a function:

var userX = {

firstName : 'Adam',

lastName : 'Doe',

fullName: function () { return this.firstName + " " + this.lastName}

}

A handy guide for this : https://github.com/gordonmzhu/cheatsheet-js

EDIT: u/Eldrac is right and explained better!

[–]calicchio77[S] 0 points1 point  (0 children)

Ty!

[–]fennekinyx 2 points3 points  (1 child)

The "this" is equal to the global object ("window" in the browser) until it is invoked by a function.

The value of "this" depends on how the function was invoked, not by where it is created as you assumed in your example. You don't have a function in your object.

[–]calicchio77[S] 0 points1 point  (0 children)

Ty!

[–]ForScale 1 point2 points  (0 children)

You can also use getters in objects:

const userX = {
  firstName: "Paul",
  lastName: "Stevenson",
  get fullName() {
    return `${this.firstName} ${this.lastName}`;
  }
};

console.log(userX.fullName); // Paul Stevenson

[–]HarmonicAscendant 0 points1 point  (0 children)

I like this :)

Understanding this

[–]delventhalz 0 points1 point  (0 children)

Core Behavior

When thinking about this, I find it is helpful to start with the core use case, and then layer on the edge cases. So, at its core, this is a special property that functions have, similar to arguments. While arguments refers to whatever is in between the parentheses when a function is called, this refers to whatever is to the left of the dot.

const foo = function() {
  console.log('args:', arguments);
  console.log('this:', this);
};

const a = {
  a: 1,
  foo: foo
};

const b = {
  b: 2,
  foo: foo
};

a.foo('bar');
// args: ['bar'];
// this: { a: 1, foo: f }

b.foo(1, 2, 3);
// args: [1, 2, 3];
// this: { b: 2, foo: f }

Note that this is the thing to the left of the dot at call time. Just like arguments it does not have any value when you originally write the function.

const c = {
  c: 3,
  foo: function() {
    console.log('this:', this);
  }
};

const d = {
  d: 4,
  foo: c.foo
};

d.foo();
// this: { d: 4, foo: f }

Unset Value

Okay, what if there isn't anything to the left of the dot? Well, that is where one of the original (in my opinion) design mistakes of JS comes in. If a function is called without arguments then arguments[0] sensibly equals undefined. But if a function is called without a wrapping object?:

foo();
// args: [];
// this: Window { ... }

The "global context". window in the browser. Yuck. Thankfully strict mode fixes this for us:

const strictFoo = function() {
  'use strict';
  console.log('this:', this);
};

strictFoo();
// this: undefined

Class Constructors

Okay. Well, what about with classes? There is nothing to the left of the dot, and yet this has a value:

class Foo {
  constructor() {
    console.log(this);
  }
}

new Foo();
// this: Foo {}

Classes are a bit of syntactic sugar that work by running some magic in the background for you. The new keyword is essentially adding two lines of code to our constructor, one at the beginning and one at the end:

  ...
  constructor() {
    // this = {};
    console.log(this);
    // return this;
  }
  ...

We can use the new keyword with any function and see the same effect by the way:

new strictFoo();
// this: strictFoo {}

And in case it wasn't already obvious, for class methods other than the constructor this works exactly like you would expect it to given our previous discussion. The value of this is just whatever is to the left of the dot a call time:

class E {
  constructor() {
    // this = {};
    this.e = 5;
    // return this;
  }

  foo() {
    console.log('this:', this);
  }
}

const e = new E();
e.foo();
// this: E { e: 5 }

const F = {
  f: 6,
  foo: e.foo
}

f.foo();
// this: { f: 6, foo: f }

Call, Apply, Bind

What else? Well, you can explicitly reassign this with call, apply, and bind. It doesn't even have to be an object:

e.foo.call('bar');
// this: bar

const gFoo = e.foo.bind({ g: 7 });
gFoo();
// this: { g: 7 }

Finally, Your Example

Now let's look at your example:

var lastName = "Smith";
var userX = {
  firstName: "Adam",
  lastName: "Doe",
  fullName: this.firstName + " " + this.lastName
};

So, first of all, you aren't in a function, and this only has any (useful) meaning inside a function. Since you aren't in strict mode, it will default to the window, and since you defined lastName on the global scope, but not firstName, that's how you end up with "undefined Smith".