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).
Related
I'm learning javascript and want to define 2 classes inside an object. The second class is derived from the first class.
It looks like:
let foo = {
bar: (function () {
let bar_msg = '';
class bar {
constructor(msg) {
bar_msg = msg;
}
}
return bar;
}()),
baz: (function () {
let baz_msg = '';
class baz extends foo.bar {
constructor(msg) {
super();
baz_msg = msg;
}
}
return baz;
}())
};
Error message:
Uncaught ReferenceError: foo is not defined
Then, I've tried to split the classes to multiple parts:
let foo = {};
foo.bar = (function () {
let bar_msg = '';
class bar {
constructor(msg) {
bar_msg = msg;
}
}
return bar;
}());
foo.baz = (function () {
let baz_msg = '';
class baz extends foo.bar {
constructor(msg) {
super();
baz_msg = msg;
}
msg() {
return baz_msg;
}
}
return baz;
}());
let b = new foo.baz('hi!');
console.log(b.msg());
It works.
So, my question is: Why do I get the difference? I cannot show some google search results because I have no idea about the keywords.
An object is 'completely' defined when you declared it and assign it a value like so:
var foo = 'bar';
You may declare a variable but not assign it to any value:
var foo;
and when you try to access it, it will give you undefined. You may also assign a value to an undeclared variable:
foo = 'bar';
And the javascript will automatically create a global variable foo. If you try to access a variable that has never been declared or implicitly declared (e.g foo = 'bar'). It will throw an error;
console.log(iDoNotExist); // will throw an error!
In your first code, the class baz, could not locate foo in its own scope, so it will go to the global scope. But since it's also not available in the global scope and it has neither been declared explicitly nor implicitly, it will throw an error.
In the second code, you have explicitly declared foo and assign it to an object and added a property bar right after it. Any code written after foo is declared would be able to access it, including when you assign the property bar to foo, in which a class called baz tries to extend foo.bar. If the piece of code that tries to extend foo or foo.bar is written after the definition of foo. It will throw an error. See snippet below for example:
class baz extends foo {
constructor(msg) {
super();
baz_msg = msg;
}
msg() {
return baz_msg;
}
}
let foo = {};
let b = new foo.baz('hi!');
In summary, you get the difference because the accessibility of foo. In the first variable you try to access it before it is both neither explicitly nor implicitly defined, resulting in an error. In the second variable, you've declared and assigned it to a value and try to access it after, which is perfectly legal.
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"
The question is, what will the following alert:
function bar() {
return foo;
foo = 10;
function foo() {}
var foo = '11';
}
alert(typeof bar());
and the answer is, function.
My questions:
Isn't bar() being replaced by its return value? If no, why?
Isn't foo = 10; being hoisted to the top? (hoisting)
You can look at it this way:
function bar() {
var foo = function() {};
return foo; // function ends here
foo = 10;
foo = '11';
}
The other two assignment statements aren't happening.
Only declarations are hoisted in JavaScript.
For function declarations, that includes their entire statement body (which is empty in the case of foo). However, with vars, the assignments aren't considered part of the declaration and will remain where the statement was placed. (2)
To the engine, bar() appears to be:
function bar() {
// hoisted
function foo() {}
var foo; // no-op, `foo` was already declared by `function foo() {}`
// remaining statements
return foo;
// unreachable code following a `return`
foo = 10;
foo = '11'; // separated from `var foo;`
}
The resulting typeof being function is referring to the type of function foo() {}, a reference to which is what bar() returns. (1)
alert(bar().toString()); // "function foo() {}"
Can someone explain why if I add a property to foo, somehow bar also inherits that property when I do this:
var foo = bar = {};
foo.hi = 'hi';
console.log(bar); //Object {hi: "hi"}
How does this work? I'm setting properties on foo, not bar. I realized I passed the object to bar and then bar to foo, but I must be missing something here.
Integer assignment works differently and more sense (to me anyhow):
var foo = bar = 5;
foo = 4;
console.log(bar); // 5
Objects are passed by reference in JavaScript. Strings and number literals are not. In your code foo === bar, is the same object. You can simply declare the variables separately:
// Two different object instances
var foo = {};
var baz = {};
by doing foo = bar = {};, foo and bar are pointers to same object. so when you do:
foo.hi = 'hi';
It sets bar.hi also, because foo and bar point to the same object. To make it different, you should do:
var foo = {}, bar = {};
foo.hi = 'hi';
var foo = function () { this.bar = 1; }
>> foo.bar
undefined
How do I access the property of a function?
You syntax is wrong:
function foo() { this.bar = 1; }
var a = new foo();
a.bar; // 1
That is a definition. You need to instantiate it.
var foo = function () { this.bar = 1; }
>> new foo().bar
Another option:
var foo = function () { this.bar = 10; return this; } ();
console.log(foo.bar);
Read about self executing functions here:
What is the purpose of a self executing function in javascript?
The problem here is that you've only defined foo and not actually executed it. Hence the line this.bar = 1 hasn't even run yet and there is no way for bar to be defined.
The next problem is that when you run foo it will need a context which this will be defined in. For example
var x = {}
foo.apply(x);
x.bar === 1 // true
Or alternatively you could run foo as a constructor and access bar on the result
var x = new foo();
x.bar === 1 // true