How to scope a variable to a class in javascript - javascript

I am trying to create a class using the class foo {} syntax, and want a private variable inside of it. Right now, I have something like this:
'use strict';
class foo {
constructor(x1,y1) {
var x = x1;
var y = y1;
}
get x() {
return x;
}
get y() {
return y;
}
}
Whenever I try to access foo.x and foo.y, I get a x is not found error. However, when I tried putting the variable declarations outside of the constructor, it stopped working. I'm also using static methods, so I can't abandon the class syntax without some work. How can I fix this? How can I declare a variable inside of the object, that is global and can't be found outside?

Those getters are declared on the prototype, where they cannot access the private (scoped) variables in the constructor. You'll need to use Object.defineProperty for creating instance-specific getters:
class Foo {
constructor(x, y) {
Object.defineProperties(this, {
x: {get() { return x; }, configurable: true},
y: {get() { return y; }, configurable: true}
});
}
}
This is no different than in ES5. Btw, given you don't assign to the variables, you might as well make them non-writable properties.

Well people are assuming you want to use getters and setters. If that's not the case, this will suffice:
'use strict';
class foo {
constructor(x1,y1) {
this.x = x1;
this.y = y1;
}
}
var f = new foo(10, 20);
console.log(f.x)
console.log(f.y)
JSFiddle: https://jsfiddle.net/gLxnqfrv/

Taken from es6-features this is the correct syntax for a class and it's getters and setters.
class Rectangle {
constructor (width, height) {
this._width = width
this._height = height
}
set width (width) { this._width = width }
get width () { return this._width }
set height (height) { this._height = height }
get height () { return this._height }
get area () { return this._width * this._height }
}
var r = new Rectangle(50, 20)
r.area === 1000
Important to note that there is no requirement to damage the prototype in ES6.

Related

Implementing Circle constructor in JavaScript, without ES6 classes

Eric Faust typed up a Circle constructor function in the following article about ES6 classes: https://hacks.mozilla.org/2015/07/es6-in-depth-classes/
I was wondering:
Why does he use defineProperty? Can't we just implement the behavior straight into the constructor. For example: Circle.draw = function draw() {..}
Why use get / set instead of just having the state in normal properties: Circle.circleCount?
Which properties should be implemented directly on new instance objects, via this in constructor, vs on Constructor.prototype (given how both make the properties available to new instances)?
Eric's code:
function Circle(radius) {
this.radius = radius;
Circle.circlesMade++;
}
Circle.draw = function draw(circle, canvas) { /* Canvas drawing code */ }
Object.defineProperty(Circle, "circlesMade", {
get: function() {
return !this._count ? 0 : this._count;
},
set: function(val) {
this._count = val;
}
});
Circle.prototype = {
area: function area() {
return Math.pow(this.radius, 2) * Math.PI;
}
};
Object.defineProperty(Circle.prototype, "radius", {
get: function() {
return this._radius;
},
set: function(radius) {
if (!Number.isInteger(radius))
throw new Error("Circle radius must be an integer.");
this._radius = radius;
}
});
let c1 = new Circle(10);
console.log(c1.area());
console.log(Circle.circlesMade);
My version:
function Circle(_radius) {
this.radius = _radius;
Circle.draw = function draw(circle, canvas) {
/* Canvas drawing code */
};
!Circle.circleCount ?
(Circle.circleCount = 1) //First construction
:
(Circle.circleCount = Circle.circleCount + 1);
this.area = function area() {
return Math.pow(this.radius, 2) * Math.PI;
};
}
let c1 = new Circle(10);
console.log(Circle.circleCount);
console.log(c1.area());
let c2 = new Circle(20);
console.log(Circle.circleCount);
console.log(c2.area());
I prefer using the constructor's prototype object for functions that will be shared with instances rather than defining a new function on each instance. This can save memory because you only have one instance of the function rather than a new copy for each instance you create.
You can also define circleCount on the prototype since all instances need the same number. You just need to be a little careful changing it to make sure you don't create a shadowed property on each instance. Then each instance can directly provide the count via the prototype chain.
Doing that complicates the function, but simplifies the rest of you code:
function Circle(_radius) {
this.radius = _radius;
// creating an instance increments the count for everyone
Circle.prototype.circleCount++ // not this.circleCount++ which will create a new property on instance
}
Circle.prototype.draw = function draw(circle, canvas) {
/* Canvas drawing code */
};
Circle.prototype.area = function() {
return Math.pow(this.radius, 2) * Math.PI;
}
Circle.prototype.circleCount = 0
let c1 = new Circle(10);
console.log(c1.circleCount);
console.log(c1.area());
let c2 = new Circle(20);
console.log(c2.circleCount);
console.log(c2.area());
Also, regarding the question about Object.defineProperty. It looks like he's using that so he can set getter and setter functions rather than just returning the property. You could do that with area with something like:
Object.defineProperty(Circle.prototype, "area", {
get: function() {
return Math.pow(this.radius, 2) * Math.PI;
}
});
Which would allow you to access area as if ti were a property on each instance:
c2.area // instead of a function c2.area()
You could set area as a property directly, but then if you change the radius you need to also change the area. I guess which is best depends on whether radius will ever change.

Javascript class method vs prototype property

for-in example image
http://jsbin.com/mosedibera/edit?js,console
function Point(x, y) {
this.x = x
this.y = y
}
Point.prototype.point = function() {
console.log('point')
}
var point = new Point(1, 2)
for (var x in point) {
console.log(x) // x y point
}
console.log(point)
class ClassPoint {
constructor(x, y) {
this.x = x
this.y = y
}
point() {
console.log('class point')
}
}
var classPoint = new ClassPoint(1, 2)
for (var y in classPoint) {
console.log(y) // x y (without point)
}
console.log(classPoint)
As the image show, I am curious the difference between ES2015 class and prototype.
The result of for-in is different. (Although use Object.hasOwnProperty() can avoid the issue)
Does anyone know the reason made the for-in result?
Thank you very much!
The difference in for-in is because class syntax automatically makes the methods non-enumerable, whereas when you do:
Point.prototype.point = function() {
console.log('point')
}
...that creates an enumerable property on the object used as the prototype of objects created by that constructor function.
You could do the same thing class does via Object.defineProperty:
Object.defineProperty(Point.prototype, "point", {
value: function() {
console.log('point')
},
writable: true
});
That creates a non-enumerable property instead of an enumerable one (because we haven't included enumerable: true and the default is false; we also haven't included configurable: true and so the property isn't configurable, which is also what class does with methods).
Live example:
function Point(x, y) {
this.x = x
this.y = y
}
Object.defineProperty(Point.prototype, "point", {
value: function() {
console.log('point')
},
writable: true,
configurable: true
});
var point = new Point(1, 2)
for (var x in point) {
console.log(x) // x y point
}
console.log(point)
class ClassPoint {
constructor(x, y) {
this.x = x
this.y = y
}
point() {
console.log('class point')
}
}
var classPoint = new ClassPoint(1, 2)
for (var y in classPoint) {
console.log(y) // x y (without point)
}
console.log(classPoint)
There are other minor differences related to super, but that's not related to your for-in question, and as you're not using super in that code, they don't come into it anyway.

ES6 classes - Wrong constructor and setters behavior

I need to use setters insight ES6 classes to call some method automatically when some properties of a instance are changed.
I wrote this:
class Some {
constructor () {
this.xPos = 0;
this.yPos = 0;
}
set xPos (value) {
console.log(`xPos ${this.xPos}`);
}
set yPos (value) {
console.log(`yPos ${this.yPos}`);
}
}
let some = new Some();
But the console outputs:
xPos undefined
yPos undefined
You don't have getters for xPos and yPos, so why you get undefined.
This this.xPos = 0; calls setter for xPos, but when you want to write the value, it is going to find a variable or a getter for it, but you don't have any of them. In you case you need to work with value, or write a getter for it.
In the example I am working through getters and setters. In setter I set the value for properties and read throw getter. The getter returns the propertie's value.
class Some {
constructor () {
this.xPos = 0;
this.yPos = 0;
}
set xPos (value) {
this.xPosProp = value;
console.log(`xPos ${this.xPos}`);
}
set yPos (value) {
this.yPosProp = value;
console.log(`yPos ${this.yPos}`);
}
get xPos () {
return this.xPosProp;
}
get yPos () {
return this.yPosProp;
}
}
let some = new Some();

ES6 Setting Properties

I've looked into it, and it seems as though ES6 doesn't have the ability to set properties of a class, and return that class?
class MyClass {
constructor() {
this.x = 0;
this.y = 0;
}
update(value) {
// logic
this.y = value;
return value;
}
}
var x = new MyClass();
console.log(x.update(1));
With the above, x will keep y as 0, even though setting y to 1. console.log will put out 1, but y is never actually updated. Calling x.y will result in 0.
I've also attempted returning the class, yet that doesn't work either.
class MyClass {
constructor() {
this.x = 0;
this.y = 0;
}
update(value) {
// logic
this.y = value;
return this;
}
}
var x = new MyClass();
x = x.update(1);
Using console.log(x) afterwards would once again result in y being 0, and not 1.
I'm aware of set and get, but then I wouldn't be able to perform any logic within update() or return anything.
Is this intended, or am I completely doing it wrong?
I would like to note that I'm using NodeJS.
I am doing something such as:
class.js ->
module.exports = /*class MyClass{}*/ (the above MyClass code)
app.js ->
let MyClass = require('class');
let x = new MyClass();
x.update(1);
console.log(x) (this returns the same value as x before calling update())
Calling x.y will result in 0
No it does. This suggests that your // logic is flawed. If there is no extra logic, the x.y property does end up as 1.
It works!
var x =new MyClass();
console.log(x.update(1)); //1
console.log(x.y); //1

JavaScript override methods

Let's say you have the below code:
function A() {
function modify() {
x = 300;
y = 400;
}
var c = new C();
}
function B() {
function modify(){
x = 3000;
y = 4000;
}
var c = new C();
}
C = function () {
var x = 10;
var y = 20;
function modify() {
x = 30;
y = 40;
};
modify();
alert("The sum is: " + (x+y));
}
Now the question is, if there is any way in which I can override the method modify from C with the methods that are in A and B. In Java you would use the super-keyword, but how can you achieve something like this in JavaScript?
Edit: It's now six years since the original answer was written and a lot has changed!
If you're using a newer version of JavaScript, possibly compiled with a tool like Babel, you can use real classes.
If you're using the class-like component constructors provided by Angular or React, you'll want to look in the docs for that framework.
If you're using ES5 and making "fake" classes by hand using prototypes, the answer below is still as right as it ever was.
JavaScript inheritance looks a bit different from Java. Here is how the native JavaScript object system looks:
// Create a class
function Vehicle(color){
this.color = color;
}
// Add an instance method
Vehicle.prototype.go = function(){
return "Underway in " + this.color;
}
// Add a second class
function Car(color){
this.color = color;
}
// And declare it is a subclass of the first
Car.prototype = new Vehicle();
// Override the instance method
Car.prototype.go = function(){
return Vehicle.prototype.go.call(this) + " car"
}
// Create some instances and see the overridden behavior.
var v = new Vehicle("blue");
v.go() // "Underway in blue"
var c = new Car("red");
c.go() // "Underway in red car"
Unfortunately this is a bit ugly and it does not include a very nice way to "super": you have to manually specify which parent classes' method you want to call. As a result, there are a variety of tools to make creating classes nicer. Try looking at Prototype.js, Backbone.js, or a similar library that includes a nicer syntax for doing OOP in js.
Since this is a top hit on Google, I'd like to give an updated answer.
Using ES6 classes makes inheritance and method overriding a lot easier:
'use strict';
class A {
speak() {
console.log("I'm A");
}
}
class B extends A {
speak() {
super.speak();
console.log("I'm B");
}
}
var a = new A();
a.speak();
// Output:
// I'm A
var b = new B();
b.speak();
// Output:
// I'm A
// I'm B
The super keyword refers to the parent class when used in the inheriting class. Also, all methods on the parent class are bound to the instance of the child, so you don't have to write super.method.apply(this);.
As for compatibility: the ES6 compatibility table shows only the most recent versions of the major players support classes (mostly). V8 browsers have had them since January of this year (Chrome and Opera), and Firefox, using the SpiderMonkey JS engine, will see classes next month with their official Firefox 45 release. On the mobile side, Android still does not support this feature, while iOS 9, release five months ago, has partial support.
Fortunately, there is Babel, a JS library for re-compiling Harmony code into ES5 code. Classes, and a lot of other cool features in ES6 can make your Javascript code a lot more readable and maintainable.
Once should avoid emulating classical OO and use prototypical OO instead. A nice utility library for prototypical OO is traits.
Rather then overwriting methods and setting up inheritance chains (one should always favour object composition over object inheritance) you should be bundling re-usable functions into traits and creating objects with those.
Live Example
var modifyA = {
modify: function() {
this.x = 300;
this.y = 400;
}
};
var modifyB = {
modify: function() {
this.x = 3000;
this.y = 4000;
}
};
C = function(trait) {
var o = Object.create(Object.prototype, Trait(trait));
o.modify();
console.log("sum : " + (o.x + o.y));
return o;
}
//C(modifyA);
C(modifyB);
modify() in your example is a private function, that won't be accessible from anywhere but within your A, B or C definition. You would need to declare it as
this.modify = function(){}
C has no reference to its parents, unless you pass it to C. If C is set up to inherit from A or B, it will inherit its public methods (not its private functions like you have modify() defined). Once C inherits methods from its parent, you can override the inherited methods.
the method modify() that you called in the last is called in global context
if you want to override modify() you first have to inherit A or B.
Maybe you're trying to do this:
In this case C inherits A
function A() {
this.modify = function() {
alert("in A");
}
}
function B() {
this.modify = function() {
alert("in B");
}
}
C = function() {
this.modify = function() {
alert("in C");
};
C.prototype.modify(); // you can call this method where you need to call modify of the parent class
}
C.prototype = new A();
Not unless you make all variables "public", i.e. make them members of the Function either directly or through the prototype property.
var C = function( ) {
this.x = 10 , this.y = 20 ;
this.modify = function( ) {
this.x = 30 , this.y = 40 ;
console.log("(!) C >> " + (this.x + this.y) ) ;
} ;
} ;
var A = function( ) {
this.modify = function( ) {
this.x = 300 , this.y = 400 ;
console.log("(!) A >> " + (this.x + this.y) ) ;
} ;
} ;
A.prototype = new C ;
var B = function( ) {
this.modify = function( ) {
this.x = 3000 , this.y = 4000 ;
console.log("(!) B >> " + (this.x + this.y) ) ;
} ;
} ;
new C( ).modify( ) ;
new A( ).modify( ) ;
new B( ).modify( ) ;
test it here
You will notice a few changes.
Most importantly the call to the supposed "super-classes" constructor is now implicit within this line:
<name>.prototype = new C ;
Both A and B will now have individually modifiable members x and y which would not be the case if we would have written ... = C instead.
Then, x, y and modify are all "public" members so that assigning a different Function to them
<name>.prototype.modify = function( ) { /* ... */ }
will "override" the original Function by that name.
Lastly, the call to modify cannot be done in the Function declaration because the implicit call to the "super-class" would then be executed again when we set the supposed "super-class" to the prototype property of the supposed "sub-classes".
But well, this is more or less how you would do this kind of thing in JavaScript.
HTH,
FK
function A() {
var c = new C();
c.modify = function(){
c.x = 123;
c.y = 333;
}
c.sum();
}
function B() {
var c = new C();
c.modify = function(){
c.x = 999;
c.y = 333;
}
c.sum();
}
C = function () {
this.x = 10;
this.y = 20;
this.modify = function() {
this.x = 30;
this.y = 40;
};
this.sum = function(){
this.modify();
console.log("The sum is: " + (this.x+this.y));
}
}
A();
B();

Categories

Resources