Variable defined with a '.' in the name - javascript

Today I noticed something weird while using webpack and the DefinePlugin.
Lets say my webpack.config.js contains the following:
new webpack.DefinePlugin({
'foo.bar': JSON.stringify('hello world'),
'baz': JSON.stringify({bar: 'I am in baz.bar'}),
})
And my global-environment.ts:
declare var foo: object
declare var baz: object
Now, I do this on my main script file:
console.log(foo.bar) // hello world
console.log(foo['bar']) // hello world
console.log(baz.bar) // I am in baz.bar
console.log(baz['bar']) // I am in baz.bar
And it works fine, but now the fun begins:
console.log(foo) // ReferenceError: foo is not defined
console.log(baz) // { bar: 15 }
Why does it think that foo is not defined but I can indeed access bar through . and []?
My initial suspicion was that it was something similar to when one uses an object as key for another object and it coerces to a string:
const a = {}
const b = {}
const b[a] = 15 // {'[object Object]': 15}
So foo.bar would be equivalent to something like globals['foo.bar'], and then it would make sense that foo alone be undefined and foo[bar] too. Except it isn't, and as mentioned above you can use foo.bar and foo['bar'] normally just as if foo was a regular object.
What is happening here?

Related

Weird behavior with variable scope and 'require' in NodeJS

I have 2 files:
testrequire.js
let a = {};
function foo() {
a = 'test';
};
module.exports.foo = foo;
module.exports.a = a;
test.js
let a = require('./testrequire');
a.foo();
console.log(a);
When I run test.js, this is the result:
{ foo: [Function: foo], a: {} }
But I expect it to be like this:
{ foo: [Fuction: foo], a: 'test' }
However, when I change testrequire.js like this:
let a = {};
function foo() {
a.b = 'test';
};
module.exports.foo = foo;
module.exports.a = a;
The result is:
{ foo: [Function: foo], a: { b: 'test' } }
And it is perfectly like what I expected.
The question here is: Why function foo() can modify a's properties while it cannot modify a?
P/S: I did try var instead of let and the result is still the same. So it is definitely not ES6 let fault.
It's a pointer thing. It's the same in C/C++, Java etc. We've gotten so used to closures that we've sort of expect regular pointers to work the same. But pointers/references are simple indirections.
Let's walk through your code:
let a = {};
Create an object ({}) and point the variable a to that object.
function foo() {
a = 'test';
};
Declare a function foo() that overwrites the value of a with a string. Now, if you remember your C/assembly then you'd remember that the value of a pointer is the address of the thing it points to. So the original value of a is not {} but the address to that object. When you overwrite a with a string that object still exist and can be garbage collected unless something else points to it.
module.exports.foo = foo;
module.exports.a = a;
Export two properties, 1. foo which points to a function and 2. a which points to the same object that a is pointing to. Remember, just like in C/Java this does not mean that module.exports.a points to a but that it points to {}. Now you have two variables pointing to the same object {}.
Now, when you do:
a.foo();
All you're doing is changing the enclosed variable a to point to a string instead of the original object. You haven't done anything to a.a at all. It's still pointing to {}.
Workarounds
There are two ways to get what you want. First, the OO way. Don't create a closure for a, make it a regular object property:
function foo() {
this.a = 'test';
};
module.exports.foo = foo;
module.exports.a = {};
This will work as expected because modules in node.js are proper singletons so they can be treated as regular objects.
The second way to do this to use a getter to get the enclosed a. Remember that closures only work with functions, not objects. So just assigning the variable to a property like you did results in a regular pointer operation not a closure. The workaround is this:
let a = {};
function foo() {
a = 'test';
};
function getA() {
return a; // this works because we've created a closure
}
module.exports.foo = foo;
module.exports.getA = getA;
Now you can do:
a.foo();
a.getA(); // should return 'test'
foo can modify the variable a to point to something else.
But this has no effect on the object exported. When the require is done, the calling module receives whatever a pointed to at the time. After that, it does not care about what happens to (the variable) a.
In your second example, you are not assigning a to a new object, but you are modifying the existing object (by adding a new field). That will of course be visible to anyone who got hold of that object before.
This is (very roughly) analogous to
function callerDoesNotSeeThis(a){ a = 1 }
function callerSeesThis(a){ a.foo = 1 }

Overriden toString() not called when object inialized with object literal

I have the following problem: I have a class Foo which overrides the default implementation of toString().
class Foo {
bar: string;
toString(): string {
return this.bar;
}
}
When I initialize a fresh variable with an (typed) object literal and call toString() on this new object the standard Javascript implementation is called. So that "[object Object]" is printed out into the console.
let foo : Foo = {
bar: "baz"
}
console.log(foo.toString()) // does not invoke Foo.toString()
However, when I create the object with the new operator everything works like a charm:
let foo: Foo = new Foo();
foo.bar = "baz"
console.log(foo.toString()); // prints out "baz"
What am I doing wrong? I'd like to avoid the second alternative because in reality the Foo object has many (nested) attributes. So that it would lead to very verbose object initializations.
Your first example:
let foo : Foo = {
bar: "baz"
}
You're creating an object literal and claiming it has the same interface as Foo. It's not an actual instance of Foo so it uses the default implementation of toString().
In your second example:
let foo: Foo = new Foo();
You're creating an actual instance of Foo which has your override of toString() implemented.

Why can't the functions in my CoffeeScript class 'see' other?

My foo.coffee looks like this:
class window.Foo
bar: () ->
baz()
baz: () ->
"baz"
In the Google Chrome Console, why then do I get the following?
f = new Foo
▶ Foo {}
f.baz
"baz"
f.bar
▶ Uncaught ReferenceError: baz is not defined(…)
You need to understand how the class abstraction works. Foo is a function with its own scope, so you might think you are defining inner functions that exist in the same scope. But you aren't. This is (more or less) the code that actually gets produced:
var Foo = function Foo() {};
Foo.prototype.bar = function() {
return baz();
};
Foo.prototype.baz = function() {
return "baz";
};
So bar only exists on Foo's prototype. Because of the way the new operator works in javascript, each instance of Foo gets a pointer to that function. You can reference these prototype functions with this which in coffeescript is abbreviated to #.
class Foo
bar: () ->
#baz()
baz: () ->
'baz'
You could alternatively (although it defeats the point of using classes) say:
class Foo
bar: () ->
Foo.prototype.baz()
baz: () ->
'baz'
you're missing the # sign - reference to this/self
class window.Foo
bar: () ->
#baz()
baz: () ->
"baz"

Define a class that new a function in CoffeeScript

I want to use the 'class' syntax to create a class, and when it news an instance, the instance can be directly used as a Function.
class Foo
constructor: (#bar) ->
baz: ->
console.log 'baz'
...
f = new Foo 'bar'
f() # runs () -> console.log #bar
f.baz() # 'baz'
Here is a JavaScript solution but I cannot reproduce it in CoffeeScript using class syntax.
I don't think you can write a Coffeescript class which compiles to that Javascript (or something close). The Coffeescript class insists on 2 things:
It ends the class body with return AwesomeObject;
If I put a return bar in the class body, it objects with error: Class bodies cannot contain pure statements.
The linked Javascript model is:
var AwesomeObject = (function() {
var AwesomeObject = function() {...};
...
return function() {
var o = new AwesomeObject();
...};
})();
It defines an AwesomeObject constructor internally, but returns a different function. This is clearer if the internal name is changed to AwesomeObject1. It functions the same, but there is no way of accessing AwesomeObject1 directly.
Also AwesomeObject() and new AwesomeObject() return the same thing.
{ [Function]
whatstuff: 'really awesome',
doStuff: [Function] }
The compiled Coffeescript (for class AwesomeObject...) instead is:
AwesomeObject = (function() {
function AwesomeObject() {...}
...
return AwesomeObject;
})();
P.S.
https://github.com/jashkenas/coffee-script/issues/861
Coffeescript issue discussion on new Foo() versus Foo() syntax. Consensus seems to be that while new-less calls are allowed in Javascript for objects like Date, it isn't encouraged for user defined classes. This is interesting, though not really relevant to the question here.
How about something like this:
class Foo
constructor: (#bar) ->
return => console.log #bar
f = new Foo 'bar'
f()
This is what you're looking for:
class Foo
constructor: (#bar) ->
f = -> console.log bar
for v, k of #
f[v] = k
return f
baz: ->
console.log 'baz'
f = new Foo 'bar'
f() # runs () -> console.log #bar
f.baz() # 'baz'
Notice that this solution does not return a callable object which inherits from Foo.prototype (which is impossible), but that it does return a Function object in which some properties are mixed in.
A better pattern would be not to return a callable function object, but to return a standard object instance that has a call or execute method.
Here's an variation on Bergi's answer:
class Foo
constructor: (#bar) ->
foo = () =>
console.log #bar
foo.baz = ->
console.log 'baz'
return foo
f = new Foo 'bar'
f()
f.baz()
This may just be using class as a wrapper, much as do() does. f is { [Function] baz: [Function] }. Also Foo 'bar' (without the new), produces the same thing.

JavaScript scope: referencing parent object member from child member's closure

Newbie to JavaScript here.
How do I reference member foo from within member foobar, given that foobar's in a closure?
var priv = {
foo: "bar",
foobar: (function() {
return this.foo === "bar";
})()
};
The code above fails. In it, this.foo is undefined. If I change this.foo to priv.foo, it's still undefined. How do I reference priv.foo from within the foobar closure?
It's impossible to read any properties of an object in its defination during its initialization since prev will be undefined at that time.
When you're trying to call a clojure inside it, it refers to undefined this or priv.
Probably you wanted to write:
foobar: (function() {
return this.foo === "bar";
})
without () in the end. And then you could call it as priv.foobar();
If you still need to call it, you could define foobar after foo:
var priv = {
foo: "bar"
};
priv.foobar = (function() {
return priv.foo === "bar";
})()
The problem is that you aren't defining a closure - I don't think that there is any way to access foo from your function as priv is not yet initialised.
What exactly are you trying to do? The following is equivalent to what I understand your sample is trying to do, but my guess is that I'm not understanding the problem:
// Set elsewhere
var foo = "bar";
var priv = {
foo: foo ,
foobar: foo == "bar"
};

Categories

Resources