Javascript class method vs prototype property - javascript

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.

Related

How to create a Pair class that's equal in Set when each entry equals?

class Pair {
constructor(x, y) {
this.x = x
this.y = y
}
// TODO ?
}
const set = new Set()
set.add(new Pair(1, 2))
How to make follow statement to return true?
set.has(new Pair(1, 2)) // -> false
(We can override equals and hashCode of Pair class in Java)
The references will only be equal if they point to the same value in memory - new will (by default) create a new object, which cannot be === to a separate one. I suppose one option would be to return an existing x,y Pair instance if it's been constructed earlier:
const pairs = {};
function getMakePair(x, y) {
const str = x + ',' + y;
if (pairs[str]) {
return pairs[str];
}
const pair = new Pair(x, y);
pairs[str] = pair;
return pair;
}
class Pair {
constructor(x, y) {
this.x = x
this.y = y
}
}
const set = new Set()
set.add(getMakePair(1, 2))
console.log(set.has(getMakePair(1, 2)))
Or, if you wanted all action to be in the constructor, you can have the constructor explicitly create / return the instance, though it's a bit less clear IMO, and it's kind of odd to see:
class Pair {
constructor(x, y) {
const str = x + ',' + y;
if (Pair.pairs[str]) {
return Pair.pairs[str];
}
const instance = Object.create(Pair.prototype);
instance.x = x
instance.y = y
Pair.pairs[str] = instance;
return instance;
}
}
Pair.pairs = {};
const set = new Set()
set.add(new Pair(1, 2))
console.log(set.has(new Pair(1, 2)))
Of course, if you use a method like this to cache instances, you shouldn't mutate the .x / .y property of the instance, else the caching mechanism will harder to reason about. If you want that sort of mutable caching functionality, you might consider using .find on an array of instances instead.
Overriding the equality check is infeasible right now: https://stackoverflow.com/a/29759699/7803502
Instead, you can extend the Set class to create your own PairSet.
class Pair {
constructor(x, y) {
this._x = x
this._y = y
}
toString() {
return [
this._x,
this._y,
].join(',')
}
}
class PairSet extends Set {
add(pair) {
return super.add(pair.toString())
}
has(pair) {
return super.has(pair.toString())
}
delete(pair) {
return super.delete(pair.toString())
}
}
(function test() {
const set = new PairSet()
set.add(new Pair(1, 2))
console.log(set.has(new Pair(1, 2))) // true
console.log(set.has(new Pair(2, 1))) // false
})()

Handle nested properties of Object in JS

Hi I'm writing a module in NodeJS in a OOP style.
I have multiples simples objects that contains primitive data and multiple complex objects that contain other objects.
const Simple = function Simple() {
this.x = 0;
this.y = 0;
}
Simple.prototype.getArea = function() {
return this.x * this.y;
}
const Complex = function Complex() {
this.ownProp = 0;
this.nestedProp = new Simple();
this.otherNestedProp = new otherSimple();
}
Complex.prototype.set = function(key, value) {
this[key] = value;
}
Complex.prototype.otherSet = function(value) {
Object.assign(this, value);
}
My problem is that users who will use my API can break things by doing this:
let simple = new Simple();
simple.getArea(); // 0
let complex = new Complex();
complex.nestedProp.getArea(); // 0
complex.set('nestedProp', {x: 5, y: 6});
complex.nestedProp.getArea(); // THROW <----
let complex = new Complex();
complex.nestedProp.getArea(); // 0
complex.set({nestedProp: {x: 5, y: 6});
complex.nestedProp.getArea(); // THROW <----
Is there a lodash function to only assign values of such nested Object.
Or is there a good way to manage this kind of problems?
Note: I could check for instanceof but I have a lot of modules, and I don't want to manage each specific case.
It seems you think passing something like {x: 1, y:2} to Complex.set will magically make x and y end inside of Simple. I think you are confused about how Javascript works, no offense meant.
Here's an implementation that would make things work roughly the way you seem to want.
const Simple = function Simple() {
this.x = 0;
this.y = 0;
}
Simple.prototype.getArea = function() {
return this.x * this.y;
}
Simple.prototype.set = function (x, y) {
this.x = x;
this.y = y;
}
const Complex = function Complex() {
this.nestedProp = new Simple();
}
Complex.prototype.set = function(props) {
this.nestedProp.set(props.x, props.y);
}
let complex = new Complex();
complex.nestedProp.getArea(); // 0
complex.set({x: 5, y: 6});
complex.nestedProp.getArea(); // 30
The properties x and y are passed explicitly from Complex to Simple until they end where they should. You can either pass x and y as separate parameters (see Simple's set) or as properties of an object (see Complex's set).
But if you thought x and y would make it all the way to the end by themselves you need to study basic OOP before writing code; again, no offense meant.

How to scope a variable to a class in 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.

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

multiple constructor in javascript

I have a question: I was wondering if it is possible to simulate the
multiple constructors, like in Java (yes, I know that the languages are
completely different)?
Let's say that I have a class called "Point" which would have two
values "x" and "y".
Now, let's say if it were the Java version, I would want two
constructors: one that accept two numbers, the other accepts a string:
public class Point {
private int x;
private int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public Point(String coord) {
this.x = coord.charAt(0);
this.y = coord.charAt(1);
}
//...
}
//In JavaScript, so far I have
Point = function() {
var x;
var y;
//...
}
Is it possible to have two declarations for the Point.prototype.init?
Is it even possible to have multiple constructors in JavaScript?
You can do this in javascript by testing the number of arguments, or the type of the arguments.
In this case, you can do it by testing the number of arguments:
function Point(/* x,y | coord */) {
if (arguments.length == 2) {
var x = arguments[0];
var y = arguments[1];
// do something with x and y
} else {
var coord = arguments[0];
// do something with coord
}
}
Yes, you can, although not as your expecting. As Javascript is weakly typed, no-one cares or checks what type the arguments that you provide are.
Java requires two different constructors because it is strongly typed and the argument types have to match the method signature, however this isn't the case with JavaScript.
function Point(arg1, arg2) {
if (typeof arg1 === "number" && typeof arg2 === "number") {
// blah
} else if (typeof arg1 === "string" && arguments.length == 1) {
// blah
} else {
throw new Error("Invalid arguments");
}
};
This is inspired from iOS.
class Point {
constructor() {
this.x = 0; // default value
this.y = 0; // default value
}
static initWithCoor(coor) {
let point = new Point();
point.x = coor.x;
point.y = coor.y;
return point;
}
static initWithXY(x,y) {
let point = new Point();
point.x = x;
point.y = y;
return point;
}
}
Just like that, you could have as many initializers as you want without writing lots of if-else.
let p1 = Point.initWithCoor({ x:10, y:20 });
let p2 = Point.initWithXY(10, 20);
Just make one constructor wrap another:
function Point(x,y) {
//make the point and do what the constructor is designed to do
}
function PointStr(str) {
var xp = arguments[0];
var yp = arguments[1];
return new Point(xp, yp);
}

Categories

Resources