Problem: I am really new to object oriented programming. Have been done a lot of reading lately about objects and I am honestly confused about how to access properties of a sub object from within the main object.
If that is not clear I will shown you what I mean:
JSFiddle: http://jsfiddle.net/rn3btqp0/3/
Example:
// Pseudo code
obj1 = {
xy: {
x: 'foo',
y: this.x + 'bar'
}
}
// Working
obj2 = {
xy: (function() {
x = 'foo',
y = console.log(this.x + 'bar')
})()
}
// need to access stuff like this
obj1 = {
xy: {
x: 'foo',
y: this.x + 'bar'
}
z: xy.y
}
Question: As you can see, Im utterly confused with the proceedings of what I believe is called inheritance (correct me im mistaken). What exactly is a valid way of freely accessing properties of objects within an object?
If anything in my question seems unclear or not well structured, please do let me know so that I can make appropriate adjustments.
First, none of this is inheritance. This is more rightly called "composition", where one object is composed of other nested objects.
Looking at your code...
Example 1
// Pseudo code
obj1 = {
xy: {
x: 'foo',
y: this.x + 'bar'
}
}
This example is what people usually want to do, but you simply can't. Using object literal initializers, there's simply no way to refer to the object being created; you need to wait until after it's created. Specifically, this has no meaning that will pertain to the object being created.
You need to do this:
// Pseudo code
obj1 = {
xy: {
x: 'foo'
}
}
obj1.xy.y = obj1.xy.x + 'bar'
You could give y some default value if you wish, but none that relies on accessing obj1 or obj1.xy.
Example 2
// Working
obj2 = {
xy: (function() {
x = 'foo',
y = console.log(this.x + 'bar')
})()
}
With this one, it doesn't actually work. The x = and and y = are creating global variables. There's nothing being added to obj2.xy; it is undefined.
Example 3
// need to access stuff like this
obj1 = {
xy: {
x: 'foo',
y: this.x + 'bar'
}
z: xy.y
}
Just like Example 1, this can't be done. The this.x + 'bar' has been explained above, but the z: xy.y won't work even if y did have a useful value. This is because obj.xy doesn't yet exist. There's simply no implicit way to access objects being created while they're being created.
This works but usually not best
There are some tricks that can be done, like using anonymous constructors, but the syntax gets a good bit more verbose.
I'll show it here, but I don't think this is often the best way.
var obj1 = new function() {
this.xy = new function() {
this.x = 'foo',
this.y = this.x + 'bar'
}
this.z = this.xy.y
}
Now any object being created is actually using a temporary anonymous function as a constructor. This is because both functions are invoked using new. As such, the value of this in the functions will be the object being created, so we can refer to it. The functions implicitly return the new object, since that's how JS works when invoking a function using new.
Traditional approach
You're really better of just creating the objects individually. It's a little more verbose, but much clearer.
var obj1 = {
xy: {
x: 'foo'
}
}
obj1.xy.y = obj1.xy.x + 'bar'
obj1.z = obj1.xy.y
Using named constructors
If you like the previous one with the functions, then make actual constructor functions and invoke them.
function MyObj() {
this.x = 'foo'
this.y = this.x + 'bar'
}
function MyOuterObj() {
this.xy = new MyObj()
this.z = this.xy.y
}
var obj1 = new MyOuterObj()
Related
function Graph() {
this.vertices = [];
this.edges = [];
Graph.prototype = {
x : 0,
y : 0
};
};
console.log(Graph.prototype);
var g = new Graph();
console.log(g.x);
console.log(g.y);
Inside the constructor Graph, which is a toy exmaple, I try to assign an object to its prototype.
function Graph() {
this.vertices = [];
this.edges = [];
Graph.prototype = {
x : 0,
y : 0
};
}
When I look at Graph.prototype using:
console.log(Graph.prototype);
I find that Graph.prototype is still the default prototype. I didn't make any change to it.
Why can't I specify the constructor's prototype inside that constructor? Can someone tell me the real reason behind it?
Thanks a lot!
Background
I assume that you're calling console.log(Graph.prototype); before running the Graph constructor.
Remember that JavaScript's formal type-system for object values uses mutable prototypes as a way of implementing object-inheritance, and those prototype object references can also be swapped-out entirely and redefined at runtime even after object values using that prototype already exist, which makes it much harder to reason about a JavaScript program's type-safety (for this reason TypeScript still cannot represent every valid JavaScript program, as of early 2020).
For example, consider:
// 1. Declare `Foo` constructor.
function Foo() {
this.bar = 123;
}
// 2. Extend the prototype:
Foo.prototype.baz = 456;
// 3. Create a new instance of Foo:
const foo1 = new Foo();
console.log( "foo1.baz == %o", foo1.baz ); // "456"
// 4. Change the prototype:
delete Foo.prototype.baz;
Foo.prototype.qux = "abc";
// 5. Create a second new instance of Foo:
const foo2 = new Foo();
console.log( "foo2.qux == %o", foo2.qux ); // "abc"
// 6. Because Foo's prototype is changed, `foo1.baz` is no-longer valid:
console.log( "foo1.baz == %o", foo1.baz ); // "undefined"
So the formal-type of foo1 (i.e. the set of properties of foo1) is entirely up-in-the-air - which is why it's a good idea to never redefine a prototype in JavaScript before any objects using that prototype are created.
The Answer
With that background out of the way...
"Why can't I specify the constructor's prototype inside that constructor? Can someone tell me the real reason behind it?"
You technically can, it just won't work the way you want it to:
The Foo.prototype (or Graph.prototype in your case) would only be set when and only when the first new Foo() call is made.
The Foo.prototype object would be reupdated on every Constructor call.
This would be a bad thing: I'm unsure if JavaScript engines would treat the same lexical object-literal as the same instance of an object or would create a new object from the object-literal - either way, it makes your program much harder to reason about.
If you want to succinctly define the prototype in the same "place" in your project's codebase then just define it immediately after the Constructor function definition, this also means that Graph.prototype will be updated as-expected without needing to actually create any new Graph objects.
So this is what you're currently doing:
function Graph() {
this.vertices = [];
this.edges = [];
Graph.prototype = {
x: 0,
y: 0
};
}
console.log( Graph.prototype ); // "{}" or "{constructor: f}"
But if you update the prototype immediately after defining the constructor it will work as-intended:
function Graph() {
this.vertices = [];
this.edges = [];
}
Graph.prototype = {
x: 0,
y: 0
};
console.log( Graph.prototype ); // "{x: 0, y: 0}"
Regarding this.constructor, this.constructor.prototype, and Graph.prototype.
Inside a constructor function:
this refers to the newly created object-instance.
this.constructor refers to the constructor function (function TypeName()).
this.prototype is undefined as prototype is only defined on (constructor) functions.
TypeName.prototype:
By default this is an object with this definition:
{ constructor: f } // where `f` is `function TypeName`.
If TypeName.prototype is redefined anywhere (even inside the constructor) then it will be equal to that redefinition but only after the code that performs the redefinition actually executes (so not when the JavaScript code is merely loaded and parsed).
this.constructor.prototype also refers to TypeName.prototype (i.e. TypeName.prototype === this.constructor.prototype):
BTW, it breaks JSON:
Note that you probably don't want to do this anyway because object properties inherited from a prototype are not considered "own" properties and so are excluded from JSON.stringify output, e.g.:
var g = new Graph();
var json = JSON.stringify( g );
console.log( json ); // "{"vertices":[],"edges":[]}"
Fortunately there's a workaround you can use for serialization (but implementing a prototype-aware JSON.parse is an exercise for the reader):
function JSON_stringify_for_prototypes( obj ) {
const flat = Object.create( obj );
for( const key in flat ) { // `for( x in y )` includes non-"own" properties.
flat[key] = flat[key]; // This looks silly, but this actually causes each property to be marked as "own" in `flat`.
}
return JSON.stringify( flat );
}
You need to update Graph.prototype instead of replacing it with a new object, and you need to console.log(Graph.prototype) after calling the constructor rather than before:
function Graph() {
this.vertices = [];
this.edges = [];
Graph.prototype.x = 0;
Graph.prototype.y = 0;
};
var g = new Graph();
console.log(Graph.prototype);
console.log(g.x);
console.log(g.y);
But as others have said, because the prototype is shared by all instances of Graph, you probably don't actually want to do this. Without anymore context, it just seems like you want two more properties x and y for each instance:
function Graph() {
this.vertices = [];
this.edges = [];
this.x = 0;
this.y = 0;
};
var g = new Graph();
console.log(g);
console.log(g.x);
console.log(g.y);
It seems to me like you are really just trying to create a method that sets static properties. Here's how you would go about attempting to replicate this in JavaScript:
function Graph(){
this.vertices = []; this.edges = [];
this.staticXY = (x = 0, y = 0)=>{
const p = this.constructor.prototype;
p.x = x; p.y = y;
return this;
}
if(this.x === undefined && this.y === undefined)this.staticXY();
}
const graph1 = new Graph;
graph1.staticXY(2, 8); graph1.x = 3; graph1.y = 1;
console.log(graph1.x, graph1.y); // static remains anyways
const graph2 = new Graph;
console.log(graph2.x, graph2.y);
This question already has answers here:
How does the "this" keyword work, and when should it be used?
(22 answers)
Closed 3 years ago.
Initial question
The below code should return 1,3 since x: 1 is just present above return statement - however, it's returning 3,1. Why is this? Will it be same result when we use let and/or const.
It would be great if you guys explain, so that I can improve my fundamentals. Below is my code:
var x = 3;
var foo = {
x: 2,
baz: {
x: 1,
bar: function() {
console.log("this--->", this);
return this.x;
}
}
}
var go = foo.baz.bar;
alert(go());
alert(foo.baz.bar());
Update 1:
In the first case bar is considered as a closure which has access to the variables outside and prints 3:
bar: function() {
console.log("this--->", this);
return this.x;
}
The 'this' keyword will mean two different objects for the two function calls. You may see it by adding 'console.log(this);' before the return statement. Try using an arrow function instead!
When you assign foo.baz.bar to a var go, after the assignment go just holds a reference to the inner function. If it is then called from the outer scope then x = 3. In effect, the this within a function depends on the scope that the function is called from.
In the second case, when you call bar(), it is in the scope of the object it's called from within (i.e. foo.baz ), so this time the this within function bar() refers to baz, giving and so x = 1.
More on scope and this in javascript here
Here you can read up on Function.prototype.bind. This method should be able to help you out with your issue.
Check the code example below:
var x = 3;
const foo = {
x: 2,
baz: {
x: 1,
bar: function() {
return this.x;
}
}
}
//You're assigning a function definition to a variable, but this will not maintain scope:
const fn = foo.baz.bar;
//When called like this, bar() can be thought of as a method of foo.baz
console.log(foo.baz.bar()); //so we expect x = 1;
//You must properly bind your function reference to the desired "this" object:
const globalBound = fn; //which would be equivalent of: fn.bind(this);
const fooBound = fn.bind(foo);
const bazBound = fn.bind(foo.baz);
console.log(globalBound()); //x = 3
console.log(fooBound()); //x = 2
console.log(bazBound()); //x = 1
x = 'Now globalBound() will return a string';
foo.x = 'and so will fooBound(), but bazBound() will remain unchanged';
console.log(globalBound());
console.log(fooBound());
console.log(bazBound());
I encourage you to change var x = 3 to let x = 3 and check out your result.
I would like to change a property of an object, inside an object. But, when I did that, other object property that created using the same prototype also changed.
The code is as follows:
var a = {
x: { y: 'foo' }
}
var b = Object.create(a)
var c = Object.create(a)
console.log(a.x.y) // 'foo'
console.log(b.x.y) // 'foo'
console.log(c.x.y) // 'foo'
b.x.y = 'bar'
var d = Object.create(a)
console.log(a.x.y) // 'bar'
console.log(b.x.y) // 'bar'
console.log(c.x.y) // 'bar'
console.log(d.x.y) // 'bar'
I think the problem is because all objects referring the same x, therefore changing y from any object reflected in all objects. Can anyone explain what really happened here, perhaps with reference and suggestion for a workaround?
x is an object, that is why it is referenced by a pointer and not by the value like the string is.
Try the following as a workaround:
b.x = { y: 'bar' } // instead of b.x.y = 'bar'
this creates a new object x which will be different from others
Try this:
var a = function() {
this.x = { y: 'foo '};
}
var b = new a();
var c = new a();
b.x.y = 'bar';
You will just reference the same object (reference pointer to a place in memory), and modify the object that each object reference to. What you probably want to do is create a new object that are isolated.
Object.create creates a new object and sets its prototype to the first parameter passed in. In your case that's an instance of an object (a), but you use that same instace as the prototype for b & c. So... when accessing members of the prototype of b, you really accessing members of a (via prototypical inheritance). Modifying that applies to all inheriting objects
In order to achieve the inheritance you tried, while using Object.create AND separating all instances, do this:
function a() {
this.x = { y: 'foo' }
}
var b = Object.create(new a())
var c = Object.create(new a())
//console.log(a.x.y) // a is not an object, it's a function, or a "type"
console.log(b.x.y) // 'foo'
console.log(c.x.y) // 'foo'
b.x.y = 'bar'
var d = Object.create(new a())
//console.log(a.x.y) // a is not an object, it's a function, or a "type"
console.log(b.x.y) // 'bar'
console.log(c.x.y) // 'foo'
console.log(d.x.y) // 'foo'
This question already has answers here:
this.constructor.prototype -- can't wholly overwrite, but can write individual props?
(2 answers)
Closed 8 years ago.
I'm working with a fairly simple Point2D data structure I built to be inheritable for say a Point3D in the future and I've followed all the guides and similar questions I can find, but none seem help with my issue.
I've defined Point2D as follows:
function Point2D (px, py)
{
this.x = px;
this.y = py;
Point2D.prototype =
{
constructor: Point2D,
move:function (mx, my)
{
this.x = mx;
this.y = my;
},
translate:function (dx, dy)
{
this.x += dx;
this.y += dy;
}
};
};
I instantiate the object as follows:
var p2d1 = new Point2D(2,3);
Then I call one of the methods as follows:
p2d1.move(1,2);
And the result is:
TypeError: Object #<Point2D> has no method 'move'
I have not idea why my methods don't resolve.
I've messed around with it for a good while and found that I can declare Point2D methods this way and they will work.
Point2D.prototype.move = function () {};
Can anyone explain why they first style of replacing the entire prototype does not work, but adding functions to the existing prototype does work?
When you call new Point() the first time, Point.prototype is still an "empty" prototype. I.e. the instance that is created doesn't inherit any methods.
You change (replace) the prototype after the instance was already created. JavaScript has assign by value, not assign by reference. Quick example:
var a = 5;
var b = {c: a};
a = 10;
b.c is still 5, since assigning to a doesn't change what b.c refers to.
Point2D.prototype.move = function () {};
works because you are not replacing Point2D.prototype, you are simply mutating the existing object.
Overall, assignments to *.prototype should take place outside the constructor:
function Point2D (px, py) {
this.x = px;
this.y = py;
};
Point2D.prototype = { };
I am not sure, but defining the prototype inside the declaration of the "class" is unusual and to me, hard to define exactly how things would be resolved. When doing manual inheritence, I tend to follow more these patterns:
function Foo() {
this.bar = ...
}
Foo.prototype.baz = function() { ... }
OR
function Foo() { ... }
function Bar() { ... }
Foo.prototype = new Bar();
OR
Foo.prototype = {
blah: ...
}
Also I wouldn't usually create a "constructor" property manually, as this is a side effect of setting the prototype, but I know some popular libraries do this. In the middle example above, Foo.prototype.constructor == Bar.
If you really want to warp your brain create a second instance of Point2D and watch it have the move method available and working!
So here is what is happening.
define Point2D class
create instance of Point2D class
create initialization object
create execution context object per new keyword usage
attach prototype to execution context (at this point just Object)
run constructor method
assign value of x
assign value of y
assign new prototype value to Point2D class
what you want to do is to move the prototype setting out to the same scope as the class definition.
JavaScript has lexical scoping which means that non-local variables accessed from within a function are resolved to variables present in the parents' scope of that function when it was defined. This is in contrast to dynamic scoping in which non-local variables accessed from within a function are resolved to variables present in the calling scope of that function when it is called.
x=1
function g () { echo $x ; x=2 ; }
function f () { local x=3 ; g ; }
f # does this print 1, or 3?
echo $x # does this print 1, or 2?
The above program prints 1 and then 2 in a lexically scoped language, and it prints 3 and then 1 in a dynamically scoped language. Since JavaScript is lexically scoped it will print 1 and then 2 as demonstrated below:
var print = x => console.log(x);
var x = 1;
function g() {
print(x);
x = 2;
}
function f() {
var x = 3;
g();
}
f(); // prints 1
print(x); // prints 2
Although JavaScript doesn't support dynamic scoping we can implement it using eval as follows:
var print = x => console.log(x);
var x = 1;
function g() {
print(x);
x = 2;
}
function f() {
// create a new local copy of `g` bound to the current scope
// explicitly assign it to a variable since functions can be unnamed
// place this code in the beginning of the function - manual hoisting
var g_ = eval("(" + String(g) + ")");
var x = 3;
g_();
}
f(); // prints 3
print(x); // prints 1
I would like to know if there exists another possible way to achieve the same result without resorting to eval.
Edit: This is what I'm trying to implement without using eval:
var print = x => console.log(x);
function Class(clazz) {
return function () {
var constructor;
var Constructor = eval("(" + String(clazz) + ")");
Constructor.apply(this, arguments);
constructor.apply(this, arguments);
};
}
var Rectangle = new Class(function () {
var width, height;
constructor = function (w, h) {
width = w;
height = h;
};
this.area = function () {
return width * height;
};
});
var rectangle = new Rectangle(2, 3);
print(rectangle.area());
I know that it's not a very good example but the general idea is to use dynamic scoping to create closures. I think this pattern has a lot of potential.
To add a note on this topic:
In JavaScript whenever you make use of:
function declaration statement or function definition expression then local variables will have Lexical Scoping.
Function constructor then local variables will refer to the global scope (top-level code)
this is the only built-in object in JavaScript that has a dynamic
scoping and is set through the execution (or invocation) context.
So to answer to your question, In JS the this is already dynamically scoped feature of the language and you even don't need to emulate another one.
Attribute lookup falls through the prototype chain, which matches quite well to dynamic scopes. Just pass your own environment of dynamically-scoped variables to use around instead of using Javascript's lexical scoping.
// Polyfill for older browsers. Newer ones already have Object.create.
if (!Object.create) {
// You don't need to understand this, but
Object.create = function(proto) {
// this constructor does nothing,
function cons() {}
// and we assign it a prototype,
cons.prototype = proto;
// so that the new object has the given proto without any side-effects.
return new cons();
};
}
// Define a new class
function dyn() {}
// with a method which returns a copy-on-write clone of the object.
dyn.prototype.cow = function() {
// An empty object is created with this object as its prototype. Javascript
// will follow the prototype chain to read an attribute, but set new values
// on the new object.
return Object.create(this);
}
// Given an environment, read x then write to it.
function g(env) {
console.log(env.x);
env.x = 2;
}
// Given an environment, write x then call f with a clone.
function f(env) {
env.x = 3;
g(env.cow());
}
// Create a new environment.
var env = new dyn();
// env -> {__proto__: dyn.prototype}
// Set a value in it.
env.x = 1;
// env -> {x: 1} // Still has dyn.prototype, but it's long so I'll leave it out.
f(env.cow());
// f():
// env -> {__proto__: {x: 1}} // Called with env = caller's env.cow()
// > env.x = 3
// env -> {x: 3, __proto__: {x: 1}} // New value is set in current object
// g():
// env -> {__proto__: {x: 3, __proto__: {x: 1}}} // caller's env.cow()
// env.x -> 3 // attribute lookup follows chain of prototypes
// > env.x = 2
// env -> {x: 2, __proto__: {x: 3, __proto__: {x: 1}}}
console.log(env.x);
// env -> {x: 1} // still unchanged!
// env.x -> 1
I don't think so.
That is not how the language works. You have to use something other than variables to refer to this state information. The most "natural" way being to use properties of this, I guess.
In your case, instead of trying to use dynamic scoping to set the constructor, what if you used the return value?
function Class(clazz) {
return function () {
clazz.apply(this, arguments).apply(this, arguments);
};
}
var Rectangle = new Class(function () {
var width, height;
this.area = function () {
return width * height;
};
// Constructor
return function (w, h) {
width = w;
height = h;
};
});
var rectangle = new Rectangle(2, 3);
console.log(rectangle.area());
Why didn't anybody say this?
You can pass variables from the calling scope into the called function by binding it a context.
function called_function () {
console.log(`My env ${this} my args ${arguments}`, this, arguments);
console.log(`JS Dynamic ? ${this.jsDynamic}`);
}
function calling_function () {
const env = Object.create(null);
env.jsDynamic = 'really?';
...
// no environment
called_function( 'hey', 50 );
// passed in environment
called_function.bind( env )( 'hey', 50 );
Perhaps it's worth mentioning that in strict mode, all functions have no "environment" sent to them by default (this is null). In non strict mode the global object is the default this value for a called function.
You can simulate dynamic scoping using global variables, if you have a way to do syntactic sugar (e.g. macros with gensyms) and if you have unwind-protect.
The macro can appear to rebind the dynamic variable by saving its value in a hidden lexical and then assigning a new value. The unwind-protect code ensures that no matter how that block terminates, the original value of the global will be restored.
Lisp pseudocode:
(let ((#:hidden-local dynamic-var))
(unwind-protect
(progn (setf dynamic-var new-value)
body of code ...)
(set dynamic-var #:hidden-local)))
Of course, this is not a thread-safe way of doing dynamic scope, but if you aren't doing threading, it will do! We would hide it behind a macro like:
(dlet ((dynamic-var new-value))
body of code ...)
So if you have unwind-protect in Javascript, and a macro preprocessor to generate some syntactic sugar (so you're not manually open-coding all your saves and unwind-protected restores) it might be doable.
I know this doesn't exactly answer the question but it's too much code to put into a comment.
As an alternative approach, you may want to look into ExtJS's extend function. This is how it works:
var Rectangle = Ext.extend(Object, {
constructor: function (w, h) {
var width = w, height = h;
this.area = function () {
return width * height;
};
}
});
With public properties instead of private variables:
var Rectangle = Ext.extend(Object, {
width: 0,
height: 0,
constructor: function (w, h) {
this.width = w;
this.height = h;
},
area: function () {
return this.width * this.height;
}
});