0

function F () {
    var color = "red";
    this.fun = function f () {
            console.log(color);
    };
};

var i = new F(); 

i.fun();

I'm a bit confused to what new F() / new F is.

Here's what I know about the new keyword:
i) It creates {} either in constructor function or somehow related to constructor function.
ii) this in constructor function references this empty object and
iii) makes constructor function return this object unless you specify constructor function to return something else instead. So my first thought is that in the code above, new F() returns { fun: [Function: f] }.

When you console.log(new F()); what comes is slightly different: F { fun: [Function: f] }

It look like an object, however this behaviour makes it apparent it's more complicated:

i.fun() returns "red". A method of this object is able to access variables inside the constructor function that made it. If it were purely a basic object it wouldn't be able to do that.

Right now, my only idea is that new F() is an object that is able to see the scope of the constructor function that made it.

Disclaimer: I am aware of what closures are and I know how function f could see color in F. I thought what was going on was a copy of function f was being added as a value in a brand new object which is assigned to i. Therefore, how does function f, in this brand new object, see the color variable in a completely separate function, F?

For example:

function F (v) {
    var v = v;
    var color = "red";
    this.fun = function f () {
            console.log(color);
    };
};

var i = {fun: function f() {console.log(color)}};

i.fun() //returns 'color' is not defined.
tonitone110
  • 139
  • 6
  • 1
    When a function is defined it captures a *closure* over the variables in its scope. When the `function f() { ... }` is defined during the execution of `new F();` it captures the locally scoped variable `color` in its closure. This doesn't really have that much to do with it being a class or object or whatever. – Klaycon Jun 30 '20 at 19:36
  • 1
    Does this answer your question? [How do JavaScript closures work?](https://stackoverflow.com/questions/111102/how-do-javascript-closures-work) – Klaycon Jun 30 '20 at 19:36
  • You still don't get this? – Barmar Jun 30 '20 at 19:39
  • This is the exact same code as in your last two questions. – Barmar Jun 30 '20 at 19:40
  • @Klaycon We tried using that as the duplicate for his first question https://stackoverflow.com/questions/62646319/how-is-an-object-created-from-a-constructor-function-able-to-access-variable – Barmar Jun 30 '20 at 19:41
  • @Klaycon In the last paragraph of my question is an understanding of what I thought would be going on. Is there any way you can explain what an instantiation of a constructor really is? – tonitone110 Jun 30 '20 at 19:45
  • @tonitone110 See my answer, where I try to give a simple overview of what an instantiation of a constructor is, and explain what happens during the execution of the function that causes `f` to see `color` everywhere. – Klaycon Jun 30 '20 at 19:57

4 Answers4

1

There's another step when new F() is used:

iv) It sets the prototype of the object to an object like {contructor: F}.

This is why the object is logged as F { fun: [Function f] } instead of just {fun: [Function f] }. Objects with a prototype other than the standard Object prototype are shown with the name of the constructor function as a prefix.

As far as access to the variable color is concerned, it's no different from any other nested function, is a closure that captures variables from the containing environment. It's no different from writing an ordinary function like this:

function F() {
  var color = 'red';
  let fun = function f() {
    console.log(color);
  };
  return {
    fun: fun
  }
}
let i = F()
i.fun()

Using new has no effect on the way variables are captured in a closure.

When you assign this.fun = function f ... it just stores the closure in the fun property. JavaScript never makes copies of objects, functions, etc. unless you do so explicitly -- assignments always assign references to the object.

You can even copy the closure to another object, it will still retain its access to the original environment. Since the function doesn't refererence this, it doesn't care how it's called.

var foo = {};
foo.fun2 = i.fun;
foo.fun2() // will log "red"
Barmar
  • 741,623
  • 53
  • 500
  • 612
  • Thank-you for your response I hope I'm not too confusing as I try and relay what's going on in that good example you give. I think my my understanding of primitive data-types and referential data-types was missing. So... `fun` is a reference value-type. The `fun` value in the object `F` returns isn't a copy of 'fun'. It refers to the same `fun` that was first defined in `F`. Now we're saying `i` points to the same object that `F` returns (which includes, as a value, fun which in turn refers to the 'let fun...` seen in `F`)? – tonitone110 Jul 01 '20 at 13:16
  • Yes. In JavcaScript, all values are effectively references. – Barmar Jul 01 '20 at 14:12
1

When you call new F() the following steps are performed (from MDN):

  1. Creates a blank, plain JavaScript object;
  2. Links (sets the constructor of) this object to another object;
  3. Passes the newly created object from Step 1 as the this context;
  4. Returns this if the function doesn't return an object.

So when executing the code var i = new F(); you have the following steps:

  1. Create a new object. {} The prototype of this new object is set to F.prototype.
  2. Set the constructor of this object to the function F. This changes the display to F {}
  3. Bind the function F to the object and execute F. That means this within the body of F() refers to the object F {} we are carrying from Step 2.
  4. The new keyword causes the expression to return that object if the return type of F() is undefined (which it is, as there's no value returned).

During step 3, F is executed, with this as an empty object F {}.
The first line var color = "red"; is executed. It defines and assigns a variable, nothing special.
The second line is executed. this.fun = function f() { ... }; The function must be evaluated first before the assignment.
During the evaluation of the function, a closure is created around all variables in scope. A specific reference to this instance's declaration of the variable color is captured in scope. The function f, even when referenced outside of F, carries this closure with it. Anywhere it is executed, it carries the same closure which was captured during evaluation of this line. This closure has the variable color, so no matter where it's passed around and executed, it still knows what the variable color is. This is how closures work.

Hopefully that last paragraph clears up why this works the way it does for you.

To specifically address your last example:

var i = {fun: function f() {console.log(color)}};

The function f() in this context was not evaluated with color in its scope. The closure captured during the evaluation of this line of code does not include color, because color is a local variable only defined in the scope of a current execution of the function F().

Klaycon
  • 10,599
  • 18
  • 35
  • Thanks for your response. I think the gap in my knowledge is primitive-types and reference-types. Here's what I think is going on now. `i` literally refers to the object that `F` returns because object is a reference-type. It isn't a copy of it, like I thought. In turn, one of the values in this object that `F` returns - `fun` - isn't a copy of `let fun...` - it literally refers to `let fun...` because `let...fun` is function which is an object. So `i.fun()` points back to this `let fun...` that was originally declared in `F`. This original `let fun...` has access to `var color`. – tonitone110 Jul 01 '20 at 13:28
  • @tonitone110 The only thing I'd still point out in that assessment is that the declaration of `var color` is unique to that instance of `F`, and similarly the closure that `i.fun` contains has a reference to that unique `var color`. If you were to change `var color` from within `i.fun` it would only affect that instance, making a new one would make that new one print the original value. – Klaycon Jul 01 '20 at 13:56
  • Okay I'll try and bear that in mind although I admit I'm not confident in knowing why. It's because we assigned all this stuff (the object `F` returns which has `function f` as one of its values) to a new variable called `i`, right? So `i` almost makes a copy of `F`? But this is confusing because `F` is an object so `i` should just refer to `F`, not make a copy of it. – tonitone110 Jul 01 '20 at 14:56
  • "`i` should just refer to `F`, not make a copy of it" - `F` is not an object, it's a function. Using `new` on `F` makes an invisible new object which has the prototype and constructor of `F`, then executes `F` (letting it assign any properties with `this`), and returns a reference to this new object to `i`. Because you've just executed the function `F`, all the code within was newly executed, including declaring variables and stuff like that. – Klaycon Jul 01 '20 at 15:00
  • when you say new invisible object has prototype and constructor of `F` do you mean by that? You don't mean that `F` is a value in this new object, do you? – tonitone110 Jul 01 '20 at 15:10
  • @tonitone110 a reference to the function `F` is stored in `i.constructor` – Klaycon Jul 01 '20 at 15:11
  • Okay. So a copy/reference of `F` is stored `i.constructor` and that's why it doesn't change anything to the original `F` so when you assign an instance of `F` to a new variable, that variable gets the original `F`. I'd like to learn more about constructors and prototypes. Do you recommend a book? MDN's 'reference' section I find too difficult to read right now for anything other than maybe finding out what different methods on objects, arrays, strings etc. do. – tonitone110 Jul 01 '20 at 15:26
  • @tonitone110 Sorry, I haven't read any books so I wouldn't know what to recommend. – Klaycon Jul 01 '20 at 15:33
0

Javascript has constructor functions, you can do:

class F {
    constructor() { this.color = "red"; }

    fun() {
        console.log(this.color);
    }
};

Then you have to call that with new F();

When not calling a constructor specifically you can call any function with new, which has the effect of making the this function inside the function apply to the function itself. You can call it with apply and set this to whatever you want.

For that reason a common convention in JS was to name functions that expected to be called with new with CapitalCase, and those that didn't with camelCase. So you could just call fooBar() however you wanted, but would expect bugs if you had new FooBar() without the new.

Therefore, how does function f, in this brand new object, see the color variable in a completely separate function, F?

It doesn't, f is inside F and sees the var color declared across that scope. Functions inherit the scope of anything they're declared inside. f can see the rest of F, but F can't see the closure inside f.

Keith
  • 150,284
  • 78
  • 298
  • 434
0

You are able to access color (value: red) beacuse of closure as you mentioned. Since color variable was available at the time of function f / fun declaration. It saves it value as a closure variable.

If you want separate copies of color variable as well. Define it on this like following:

function F (color) {
    this.color = color;
    this.fun = function f () {
            console.log(this.color);
    };
};

var i1 = new F("green"); 
var i2 = new F("red"); 

i1.fun(); // green
i2.fun(); // red

In your second code snippet you are getting color as undefined because there is no color variable declared in same scope as function f (inside i object) or globally.