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

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'
}

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.

Is adding another method to a class no longer allowed in JavaScript?

So I was playing around in typescriptlang.org/play, writing a class of Plane with a method of pilot(): void {} and I pasted the JS code inside my Chrome console and played around with that for a minute.
Then I wanted to put into practice the concept of being able to add another method to class Plane {}. So this is what I had on the TypeScript side:
class Plane {
color: string = 'red’;
pilot(): void {
console.log(‘swoosh’);
}
takeoff(): void {
console.log(‘ready for takeoff’);
}
}
This is JS version:
class Plane {
constructor() {
this.color = 'red';
}
pilot() {
console.log(‘swoosh’);
}
takeoff() {
console.log(‘ready for takeoff’);
}
}
When I pasted that into Chrome console I got Uncaught SyntaxError: Identifier 'Plane' has already been declared.
Okay, so how do I add a new method then? I should be able to easily attach as many methods to prototype as I want. Is it because the term prototype does not exist in my JS code?
class functions like const or let in JS-land: it can't be re-declared. When I'm pasting code in the console that uses those terms, I generally refresh the page each time.
But happily, new releases of Chrome are letting you re-declare let and const within the console. I don't know if this will ever extend to class.
Note that you can, indeed, add a line like Plane.prototype.foo = function() {} after Plane has been declared, and this will work as you'd expect.
It seems like you are pasting the JavaScript code into a console that already has your Plane class defined.
If you already pasted the class definition once you cannot redeclare it by pasting an edited version again, you need to refresh your page or just open a console in an empty tab to get a fresh environment.
If though you want to experiment with adding a method to an existing class you can do so like this:
// You start by defining the original class (or picking one of the defined ones like Date)
class Plane {
// Your class definition
getName() { return 'I am a plane'; }
}
// Then somewhere later in your code (or console)
Plane.prototype.sayHello = function() {
return 'Hello ' + this.getName();
}
// And finally
const plane = new Plane();
plane.sayHello(); // Will return Hello I am a plane

Why is 'this' undefined in the debugger but printable in a console.log?

When setting a breakpoint on the console.log statement, why would this be undefined in the debugger but the statement print with no issues? Am I missing something in regards to scope here?
export class OptionsSidebarComponent {
...
public filters: Object = new Object();
...
public generateFilters() {
for (let property in this.terms) {
if (this.terms.hasOwnProperty(property) && this.terms[property] !== null) {
if (!this.filters.hasOwnProperty(this.productGroup.id)){
this.filters[this.productGroup.id] = new Filters();
}
this.terms[property].forEach((term) => {
console.log(this.filters);
});
}
}
}
}
With typescript While debugging, keep in mind that transpilers can rename variables. Using the original name in the console without sourcemaps that include renaming will show you undefined even if the original value isn't. Make sure you use the transpiled name in watches or console commands.
When you're referencing this with your console.log statement inside its own class, you're using it in its relevant scope. Depending on the framework you are using, different terms are used to reference your class... AngularJS used $scope- Angular 2+ uses this to reference the current class (or current scope/context).
When you use this in your debugger you're using it outside of its scope. It no longer references the class you intend it to.
Each one of your components in Angular uses this to reference itself. Here's an article to explain in further detail: https://angular-2-training-book.rangle.io/handout/features/refresher_on_this.html
If we simplify it to basic javascript and look at your class as just a function a good example would be this:
function example(){
var anything = 10;
console.log(anything); //output: 10
}
console.log(anything); //undefined! we're out of scope :)
So looking at it from this perspective, this is nothing magical. It's just provided to us by Angular to make our references to other things inside our components easier.

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

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.

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.

Categories

Resources