Define a class that new a function in CoffeeScript - javascript

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.

Related

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?

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"

What does Function.prototype.toMethod() do?

I noticed that the Function.prototype has a toMethod() method in experimental JavaScript, but what does that actually do? And how do I use it?
Update: the toMethod method was experimental only and did not make it into the standard. The home object is essentially static now, the only way to manipulate super is to change the [[prototype]]:
var base = {…}; // as below
var obj = Object.setPrototypeOf({
foo() { // needs to use method definition syntax
super.foo();
}
}, base);
obj.foo();
It's very similar to the bind method of function objects. However, instead of creating a new function with a bound this value, it creates a new function with a bound [[HomeObject]], which is the reference that is used for super calls:
[[HomeObject]] (Object): If the function uses super, this is the object whose [[GetPrototypeOf]] provides the object where super property lookups begin.
Consider this example (not using any class syntax):
var base = {
foo: function() {
console.log("base foo called on", this);
}
};
base.foo(); // base foo called on base
var obj = Object.create(base);
obj.foo(); // base foo called on obj
obj.foo = function() {
super.foo();
};
obj.foo(); // ReferenceError: this method has no home
obj.bar = obj.foo.toMethod(obj);
obj.bar(); // base foo called on obj
obj.baz = function() {
super();
};
obj.baz(); // ReferenceError: this constructor has no parent class
Reflect.setPrototypeOf(obj.baz, base.foo);
obj.baz(); // base foo called on obj
My understanding is that .toMethod is like cloning a function. Consider the example in the source I posted,
class P { }
class C extends P {
foo() {
console.log("f");
super();
}
}
P.prototype.foo=C.prototype.foo;
(new C).foo();
Here you reference a subclass method .foo in the superclass, so when you call .foo, it will reference P's .foo which is C's .foo and you have just created a loop.
It seems like to solve this issue, you can use .toMethod which "clones" the function and give it a different super/"home" that you specifed:
P.prototype.foo = C.prototype.foo.toMethod(P.prototype);
now calling (new C).foo() would not go on forever.

Does ECMAscript offer any ways of constructing functions?

As far as I know, constructors are unable to produce functions: they can assign properties to this, and offer an immediate prototype reference for further properties that are generic and thus not instance-specific. But it's impossible to assign anything to this directly. Even if it were, the logical outcome would be to replace the instance with the assignment, along with its prototype chain.
From what I have read of ES6 Classes, they amount to syntax sugar for grouping constructor declaration and prototype instantiation in a single statement.
My practical interest lies in the value of the instanceof operator in asserting that X conforms to the higher order description of Y without any duck-typing. In particular, duck-typing is undesirable because it relies on some kind of definition of Y which is external to Y itself.
Edit
I'm interested in functions which are instances of other functions
In ECMAScript 6 you should be able to call Object.setPrototypeOf on a function, but it isn't advised and although in JavaScript a Function is an Object too you may end up with unexpected behaviour
function foo() {}
function bar() {}
Object.setPrototypeOf(bar, foo.prototype);
bar instanceof foo; // true
bar.constructor === foo; // true
I'm not entirely sure what you're asking, but hopefully these code examples will help you
Returning an Object from a function invoked with new
function Foo() {
// a constructor
}
function Bar() {
// another constructor
return new Foo();
}
var b = new Bar();
b instanceof Bar; // false
b instanceof Foo; // true
Using new Function
function Fizz() {
return new Function('return "Buzz";');
}
var b = Fizz();
b(); // "Buzz"
Invoking a function with a different this by using call, apply or bind
function hello() {
return this;
}
hello(); // window, null or error depending on environment
hello.call({'foo': 'bar'}); // {'foo': 'bar'}
hello.apply({'foo': 'bar'}); // {'foo': 'bar'}
var b = hello.bind({'fizz': 'buzz'});
b(); // {'fizz': 'buzz'}
Extending a constructor
function Foo() {
this.foo = 'foo';
}
Foo.prototype = {'fizz': 'buzz'};
function Bar() {
Foo.call(this);
this.bar = 'bar';
}
// and link in inheritance
Bar.prototype = Object.create(Foo.prototype);
var b = new Bar();
b.bar; // "bar"
b.foo; // "foo"
b.fizz; // "buzz"
b instanceof Bar; // true
b instanceof Foo; // true
// but
Bar instanceof Foo; // false
Constructors can construct functions. If your constructor returns an object, the object returned by the constructor function becomes the result of the whole new expression.
See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/new
As functions are objects, you can return them just as well from your constructor as shown here:
function Shout(text) {
return function () {
alert(text);
};
}
shout1 = new Shout('hola');
shout2 = new Shout('eeee');
shout1(); // hola
shout2(); // eeee

Categories

Resources