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

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.

Related

What different between assigning property on Object and Object.prototype?

What different between assigning property on Object and Object.prototype?
for example
Object.test =function(){};
and
Object.prototype.test =function(){}
The first gives Object a static method that can be invoked directly from the class, without an instance. For example:
Object.test =function(){
console.log('Object test running');
};
Object.test();
Assigning a function to the prototype, on the other hand, allows for instances to run the method:
Object.prototype.test = function() {
console.log('test running on object ', this);
};
// don't use the object constructor, this is just an example:
const obj = new Object();
obj.test();
It might make a bit more sense if you didn't use the built-in Object, which everything inherits from:
function Foo() {}
Foo.checkIfFoo = function(arg) {
return arg instanceof Foo;
};
const f = new Foo();
console.log(Foo.checkIfFoo(f));
Here, foo.checkIfFoo is a helper function on Foo that checks if a passed object is an instance of Foo or not - no instance is required to run checkIfFoo. Functions on the prototype, on the other hand, require an instance to run:
function Foo() {
this.info = 'A Foo instance';
}
Foo.prototype.checkInfo = function() {
console.log(this.info);
};
const f = new Foo();
f.checkInfo();
Note that in ES6+, you can put a function directly on the class with the static keyword:
// roughly equivalent to the snippet with checkIfFoo above
class Foo {
static checkIfFoo(arg) {
return arg instanceof Foo;
}
}
const f = new Foo();
console.log(Foo.checkIfFoo(f));
Whereas a standard method lacks the static keyword:
// roughly equivalent to the snippet with checkInfo above
class Foo {
constructor() {
this.info = 'A Foo instance';
}
checkInfo() {
console.log(this.info);
}
}
const f = new Foo();
f.checkInfo();

Using `super()` when extending `Object`

I'm creating a class that extends Object in JavaScript and expect super() to initialise the keys/values when constructing a new instance of this class.
class ExtObject extends Object {
constructor(...args) {
super(...args);
}
}
const obj = new Object({foo:'bar'});
console.log(obj); // { foo: 'bar' }
const ext = new ExtObject({foo:'bar'});
console.log(ext); // ExtObject {}
console.log(ext.foo); // undefined
Why isn't foo defined as 'bar' on ext in this example?
EDIT
Explanation: Using `super()` when extending `Object`
Solution: Using `super()` when extending `Object`
Nobody has actually explained why it doesn't work. If we look at the latest spec, the Object function is defined as follows:
19.1.1.1 Object ( [ value ] )
When Object function is called with optional argument value, the following steps are taken:
If NewTarget is neither undefined nor the active function, then
Return ? OrdinaryCreateFromConstructor(NewTarget, "%ObjectPrototype%").
If value is null, undefined or not supplied, return ObjectCreate(%ObjectPrototype%).
Return ! ToObject(value).
The first step is the important one here: NewTarget refers to the function that new was called upon. So if you do new Object, it will be Object. If you call new ExtObject it will ExtObject.
Because ExtObject is not Object ("nor the active function"), the condition matches and OrdinaryCreateFromConstructor is evaluated and its result returned. As you can see, nothing is done with the value passed to the function.
value is only used if neither 1. nor 2. are fulfilled. And if value is an object, it is simply returned as is, no new object is created. So, new Object(objectValue) is actually the same as Object(objectValue):
var foo = {bar: 42};
console.log(new Object(foo) === foo);
console.log(Object(foo) === foo);
In other words: Object does not copy the properties of the passed in object, it simply returns the object as is. So extending Object wouldn't copy the properties either.
You are missing the Object.assign
class ExtObject extends Object {
constructor(...args) {
super(...args);
Object.assign(this, ...args);
}
}
const obj = new Object({foo:'bar'});
console.log(obj); // { foo: 'bar' }
const ext = new ExtObject({foo:'bar'});
console.log(ext); // { foo: 'bar' }
console.log(ext.foo); // bar
This answer works only when using the Babel transpiler.
Because Object's constructor returns value. See spec
15.2.2.1 new Object ( [ value ] )
When the Object constructor is called with no arguments or with one argument value, the following steps are taken:
...
8. Returns obj.
class ExtObject extends Object {
constructor(...args) {
return super(...args);
}
}
const obj = new Object({foo:'bar'});
console.log(obj); // { foo: 'bar' }
const ext = new ExtObject({foo:'bar'});
console.log(ext); // { foo: 'bar' }
console.log(ext.foo); // bar

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?

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

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.

Categories

Resources