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

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"

Related

Interdependent includes in node.js result in a empty object

I have two files that declare a class, both of them with static functions:
foo.js:
const bar = require('./bar');
console.log('bar in foo: ');
console.log(bar);
class foo {
static _(test) {
return 'foo_' + test + bar._(test);
}
static bar() {
return 'bar';
}
}
module.exports = foo;
bar.js:
const foo = require('./foo');
console.log('foo in bar:');
console.log(foo);
class bar {
static _(test) {
return foo.bar(test);
}
}
module.exports = bar;
In VS Code, code completion work in both files to get the other file's static method.
But when I try to execute it:
const foo = require('./foo');
foo._('test');
I can see that the recursive require don't work as I thought it would:
foo in bar:
{}
bar in foo:
[Function: bar]
bar.js:8
return foo.bar(test);
^
TypeError: foo.bar is not a function
I know class in JS are syntaxic sugar around functions, but I am still surprised that the interpreter lose static members.
Can anybody explain the working of require in that case? And why it result in a empty object?

Variable defined with a '.' in the name

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?

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.

Why is bar undefined in the following JavaScript?

I am playing around with CoffeeScript and the 'class' keyword, ended up with the following:
var Foo, foobar;
Foo = (function() {
function Foo() {}
Foo.bar = 7;
return Foo;
})();
foobar = new Foo;
alert(foobar.bar);
after typing in:
class Foo
#bar = 7
foobar = new Foo
alert foobar.bar
I kind of expected bar to retain that value but the alert pops up as undefined
bar is a property of Foo (the function), not instances it creates.
If you want bar on instances, you have two choices:
Set it within the constructor:
class Foo
constructor: () ->
#bar = 7
foobar = new Foo
alert foobar.bar
Which translates to this JavaScript:
var Foo, foobar;
Foo = (function() {
function Foo() {
this.bar = 7;
}
return Foo;
})();
foobar = new Foo;
alert(foobar.bar);
Or set it on the prototype:
class Foo
bar: 7
foobar = new Foo
alert foobar.bar
which translates to:
var Foo, foobar;
Foo = (function() {
function Foo() {}
Foo.prototype.bar = 7;
return Foo;
})();
foobar = new Foo;
alert(foobar.bar);
Try this:
class Foo
constructor: ->
#bar = 7
foobar = new Foo()
alert foobar.bar
The problem is that when you're at the class-level # is referring to Foo. So in your example Foo.bar == 7, but it's not setting anything on instances (which you can do in the constructor).

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