Javascript console.log does not show derived class name - inheritance - classes - javascript

I'm playing with ECMAScript6 classes.
I still don't understand why the following code :
"use strict";
class A {}
class B extends A {}
let b = new B();
console.log(b);
Displays :
A { }
Instead of :
B { }
Live Example:
(function () {
"use strict";
class A {}
class B extends A {
foo() {
}
}
let b = new B();
console.log(b);
})();
Open the console. Works only on very up-to-date browsers (such as Chrome 43+).
How can I have the expected logical output B { } on console.log ?
May I need to specify my class name to be "B" ? Is there such an option to pass, or an attribute or a function to define ?
T.J. Crowder got it : It is a referenced bug on Chrome.
Everybody, can you star this bug to increase its priority ?
https://code.google.com/p/chromium/issues/detail?id=510688

You haven't said what browser you're using, but I figure it has to be Chrome, given the style of the output you showed and that it runs at all. (If I run that in IE11, I get [object Object] {} instead. If I run it in Firefox, I get an error — because Firefox doesn't support class yet.)
I can't think of any reason other than a bug in Chrome. Support for class is very new to Chrome, it could easily be that the devtools just aren't quite handling it correctly yet. I didn't find a bug report on http://crbug.com in a quick search, you might want to file one. But you did find it.
It really should be showing B with your code, not A. It does with the equivalent old-fashioned way to do it:
(function() {
"use strict";
function A() {}
function B() {
A.call(this);
}
B.prototype = Object.create(A.prototype);
B.prototype.constructor = B;
var b = new B();
console.log(b);
})();
Open the console.

Related

How to Call Global Variables From Constructors

I have a class that looks like this:
class MyClass {
constructor() {
this.field = 42;
}
method() {
return "Hello world!";
}
}
I want to define an instance of it as a global variable, so I registered it:
global.MyObject= new MyClass();
Which means this will now work:
console.log(MyObject.field); // 42
console.log(MyObject.method()); // Hello world!
However, whenever I call that same code from another class it suddenly becomes a lot more random:
class OtherClass {
constructor() {
console.log(MyObject.field); // undefined
console.log(MyObject.method()); // TypeError: MyObject.method is not a function
}
}
Sometimes this works, sometimes it doesn't. I am not able to find out when it works and when it doesn't, only that it isn't totally random. Called from the same place, the constructor either works or not. Which does not help to isolate the problem.
It seems that when the class's constructor is called from a module that does not have a direct require('my-class') it will not work - even when OtherClass has that requironment? Even when every other module has that require? Which does not make any sense.
I tried it with two modules A and B. If A defines global variables, B can access them just fine even without a require - as long as someone else has it.
So... I'm stumped at how this works. Might be a silly JavaScript gimmick, I'm not a native JavaScript developer.
My setup looks like this:
src/
ActualCode (uses functionality that will create OtherClass)
test/
mock/
MyClass
OtherClass (require('MyClass'))
ActualCodeTest (require('ActualCode'), require('OtherClass')
The constructor in OtherClass will be able to access MyClass when called from MyClass and OtherClass, but not from ActualCode or ActualCodeTest. Even if called in sequence (so the global variable definitively exists).
Which means changing parameters and adding a require is out of the question, since the ActualCode should never know test code.
The problem seems to be with the Mocha framework. The global variable cannot be accessed inside the "it":
console.log(MyObject.field); // 42
it('test', () => {
console.log(MyObject.field); // undefined
}
If I remove the surrounding code it will (obviously) work.
How do I call global variables from a class's constructor?
This sample code works as expected when you have it all in a single file:
$ cat sample.js
class MyClass {
constructor() {
this.field = 42;
}
method() {
return "Hello world!";
}
}
global.MyObject = new MyClass();
class OtherClass {
constructor(props) {
console.log(MyObject.field);
console.log(MyObject.method());
}
}
new OtherClass();
$ node sample
42
Hello world!
I suspect the confusion comes in the sequence those lines are run when you're loading the modules. Node's module-loading code will run every line in the sequence they are encountered.
The problem is that Mocha can't access global variables in its tests. I could not find the
relevant bug for this behavior, but at least a person with the same problem.
Since the global variables are only used in the tests, I get to have an easy solution:
global.MyObject= new MyClass();
module.exports.MyObject = MyObject;
And this in the test:
before(() => {
global.MyObject = require('./mock/my-class').MyObject;
});
It's probably a good idea to add a TODO so you can revisit this issue every now and then in case the Mocha crew fixes it.

Google Chrome Snippets: Identifier '...' has already been declared

I'm using Google Chrome's Snippets (inside Dev Tools) for some JS development and testing.
When declaring ES6 classes, the console throws an
Uncaught SyntaxError: Identifier 'Foo' has already been declared at...
after the first time it was run.
class Foo {
constructor(val) {
this.bar = val;
}
}
var f = new Foo('myVal');
console.log(f.bar); // prints 'myVal'
I did some research and found out that wrapping the code in a block scope with {} would help in avoiding this problem.
But when I do this, although the code runs without error, Chrome doesn't recognize any subsequent edits that I may do to my code.
So, if I changed the above code to the following:
{
class Foo {
constructor(val) {
this.bar = val;
}
}
}
var f = new Foo('myVal');
console.log(f.bar); // prints 'myVal'
So far everything works fine.
Now, I realize that there is a problem with my class and I change the code to the following:
{
class Foo {
constructor(val) {
this.bar = 'MyHardcodedVal'; // Here is the changed code
}
}
}
var f = new Foo('myVal');
console.log(f.bar); // STILL prints 'myVal'
As you can see, the edits I made to my class are not taking effect.
It appears that Google Chrome has put my code in a sandbox that is immune from my editing.
A way to look behind the scene and see what Google Chrome is doing is to introduce an intentional mistake into the code and then click on the source of the mistake that Chrome shows in the console. There you will see that the code for the class is still the old one and not changed at all, while the code that has been outside of the block scope is up to date.
I could always close the tab I am working in and open it again, but that isn't practical.
What am I doing wrong?
Is there a sane way to use Snippets for such tests?
Hope that makes sense!
Based off the comments, it sounds like the solution is to wrap the entire snippet in braces.
{
class Foo {
constructor(val) {
this.bar = 'MyHardcodedVal'; // Here is the changed code
}
}
var f = new Foo('myVal');
console.log(f.bar); // STILL prints 'myVal'
}

Evaluation of nested function in JS

I'm new to JS, I've seen the code with exact the same logic (except my newbie errors) :
function foo(a){
var t = 1;
function(b){
console.log(a+b+(++t));
}
}
bar = foo(5);
bar(6);
bar(6);
what is output of first and second call of bar function going to be ?
H,
Seems like too many problems with your code.
maybe you meant to do something like this:
function foo(a){
var t = 1;
return function(b){
console.log(a+b+(++t));
}
}
var new1 = foo(5);
new1(6);// will output 13
new1(6);// will output 14
anyway,
you can try it on online REPL to test the code yourself.
ONLINE JS REPL
or any modern browser dev tools (F12 on chrome -> console for example)

What is the Chrome console displaying with log()?

I think I may have found a bug with Google Chrome (16.0.912.75 m, the latest stable at the time of this writing).
var FakeFancy = function () {};
console.log(new FakeFancy());
var holder = {
assignTo : function (func) {
holder.constructor = func;
}
};
holder.assignTo(function () { this.type = 'anonymous' });
var globalConstructor = function () { this.type = 'global' }
console.log(new holder.constructor());
If you run that block in Firefox, it shows both listed as "Object" with the second having type = local, pretty good. But if you run it in Chrome, it shows
> FakeFancy
> globalConstructor.type
If you expand the trees, the contents are correct. But I can't figure out what Chrome is listing as the first line for each object logged. Since I'm not manipulating the prototypes, these should be plain old objects that aren't inheriting from anywhere.
At first, I thought it was WebKit related, but I tried in the latest Safari for Windows (5.1.2 7534.52.7) and both show up as "Object".
I suspect that it's attempting to do some guesswork about where the constructor was called from. Is the anonymous constructor's indirection messing it up?
The first line is a result of
console.log(new FakeFancy());
The WebKit console generally tries to do "constructor name inference" to let you know what type of object it's outputting. My guess is that the more recent version included with Chrome (as opposed to Safari 5.1) can do inference for constructor declarations like
var FakeFancy = function () {};
and not just ones like
function FakeFancy() {}
so that's why you're seeing the disparity.

Annotate Singleton objects in JavaScript for the Google Closure Compiler, or "dangerous use of the global this object" warning

I'm working with the Google Closure Compiler in ADVANCED_OPTIMIZATIONS compilation level and have started to annotate my constructors because I get all kinds of warnings:
WARNING - dangerous use of the global this object
For my 'constructor' type functions I'll annotate them like this:
/**
* Foo is my constructor
* #constructor
*/
Foo = function() {
this.member = {};
}
/**
* does something
* #this {Foo}
*/
Foo.prototype.doSomething = function() {
...
}
That seems to work fine, however what if I have a 'singleton' object that isn't constructed with var myFoo = new Foo();
I couldn't find in the documentation how to annotate this type of object because its type is just object right?
Bar = {
member: null,
init: function() {
this.member = {};
}
};
The preferred way of creating singletons in Closure is like this:
/** #constructor */
var Bar = function() { };
goog.addSingletonGetter(Bar);
Bar.prototype.member = null;
Bar.prototype.init = function() {
this.member = {};
};
This allows for lazy instantiation of the singleton. Use it like this:
var bar1 = Bar.getInstance();
var bar2 = Bar.getInstance();
bar1.init();
console.log(bar2.member);
Keep in mind that this doesn't prevent people from using the constructor to create instances of Bar.
This is exactly the type of potential bug that "dangerous use of this" warns you against. In your example, the Closure Compiler may try to "flatten" your code to:
Bar$member = null;
Bar$init = function() { this.member = {}; };
NOTE: The Closure Compiler currently will not flatten a namespace that is declared as a global object (i.e. without the "var" keyword in front), so your code may still work now. However, there is no telling that it won't do that in a future version and your code will suddenly break without warning.
Of course, then "Bar$member" and "Bar$init" will be renamed to "a" and "b" respectively. This is called "namespace flattening" or "collapsing of properties".
You can immediately see that your code no longer works correctly. Before compilation, if you write:
Bar.init();
this will refer to Bar. However, after compilation it becomes:
Bar$init();
this will no longer refer to Bar. Instead it refers to the global object.
This is way the compiler is trying to warn you that using "this" in such a way is "dangerous", because "this" may be changed to refer to the "global" object. That's the true meaning of the warning.
In short, DO NOT DO THIS. This type of coding style creates bugs that are very difficult to track down.
Modify your code this way:
var Bar = { // Closure Compiler treats globals and properties on global differently
member: null,
init: function() { Bar.member = {}; }
};
or use a closure:
var Bar = (function() {
var member = null;
return {
init: function() { member = {}; }
};
})();
When using the Closure Compiler in Advanced Mode, do not try to get rid of warnings by annotating them away. Warnings are there for a reason -- they try to warn you about something.

Categories

Resources