I'm trying to learn about objects and JavaScript.
The problem I have is, it appears as if both the following do the same thing. My research shows nothing about what (if any) differences there are but this may be because it's very hard to question this within a search box.
Within the code, I've added comments to emphasise the differences
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
</head>
<body>
<div id = "results"></div>
<script>
"use strict";
const results = document.getElementById("results");
const Thing = function(){ /* ****** THIS */
var _x;
this.set = function(x){
_x = x;
},
this.alert = function(){
results.innerHTML += _x + "<br />";
};
};
function Other(){ /* ****** AND THIS */
var _x;
this.set = function(x){
_x = x;
},
this.alert = function(){
results.innerHTML += _x + "<br />";
};
};
const t = new Thing();
t.set("b");
t.alert();
const t2 = new Thing();
t2.set("b2");
t2.alert();
t.alert();
t2.alert();
const o = new Other();
o.set("d");
o.alert();
const o2 = new Thing();
o2.set("d2");
o2.alert();
o.alert();
o2.alert();
</script>
</body>
</html>
JSFIDDLE
What is the difference or are they both valid?
EDIT
I have seen the duplicate var functionName = function() {} vs function functionName() {}. The difference between the questions is I'm using the word new. My question is, does that actually make a difference?
The steps that new takes are (from MDN):
A new object is created, inheriting from Foo.prototype.
The constructor function Foo is called with the specified arguments, and with this bound to the newly created object. new Foo is
equivalent to new Foo(), i.e. if no argument list is specified, Foo is
called without arguments.
The object returned by the constructor function becomes the result of the whole new expression. If the constructor function doesn't
explicitly return an object, the object created in step 1 is used
instead. (Normally constructors don't return a value, but they can
choose to do so if they want to override the normal object creation
process.)
It doesn't matter that you used a function expression or a function declaration at that point; just that you used a function.
function Foo() {
this.hello = "world";
}
const Bar = function() {
this.fizz = "buzz";
}
const foo = new Foo();
const bar = new Bar();
console.log(foo instanceof Foo);
console.log(bar instanceof Bar);
You might find this interesting.
You can use new on regardless of function declaration or expression.
Look at the console output. New creates a new instance of the function. There doesn't appear to be any other side effect.
function funcOne() {};
var f1 = funcOne;
var f2 = funcOne;
var fx = new funcOne();
console.log(funcOne==f1); //true
console.log(f1==f2); //true
console.log(funcOne==fx); //false
const funcTwo = function(){};
var f3 = new funcTwo();
var f4 = new funcTwo();
console.log(funcTwo==f3) //false
console.log(f3==f4); //false
If you're looking for some discussion of functions as objects, I recommend this thread javascript : function and object...?
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);
So I can define a property on a function from the outside such as: createNewPerson.hmm = 3; and I get the result 3 when I use the alert. However if I try to do the same but from within a function (as in define a property within a function) it doesn't work.. I tried all the commented lines below. Is there a different way to achieve this or is it simply not possible? If it's not possible, then how does javascript append the .hmm property to the function and later call it?
Full code:
function createNewPerson(name) {
//var hello = "hgfgh";
//hello: "hgfgh"
//hello = hgfgh;
//this.hello = hgfgh;
}
createNewPerson.hmm = 3;
alert(createNewPerson.hmm);
alert(createNewPerson.hello);
I think you are trying to create objects. In javascript, you do it this way:
function Test(){
this.foo = "bar";
this.func = function(){
console.log(this.foo);
}
}
const test = new Test();
test.foo = "baz";
test.func(); //Will print "baz".
note the use of "new". This is what enables the mechanism of allowing code to modify properties of object. The modified values of the properties is then accessible by the object itself.
Hi please try below code,
jsFiddle
function createNewPerson(name) {
//var hello = "hgfgh";
//hello: "hgfgh"
//hello = hgfgh;
this.hello = "hgfg";
this.name = name;
}
var cnp = new createNewPerson('de');
cnp.hmm = 3;
alert(cnp.hmm);
alert(cnp.hello);
alert(cnp.name);
I have looked at similar questions here on the stack but could not come up with a solution.
My issue is that the following code's this does not point to the object in question, instead it points to the environment (window) in Node.JS.
I'm sure it's something trivial but after about 2 hours of research I have officially given up and it's time to ask for help.
Code:
var slice = Array.prototype.slice;
function Chain(options, links) {
if (!(this instanceof Chain)) return new Chain(options, slice.call(arguments));
return this;
}
Chain.prototype.quicklink = function quicklink(fn) {
console.log(this);
};
var Chain = require('./chains');
var chain = Chain();
var _ = chain.quicklink;
_('extern_var', 'something', '$callback');
Expected:
this to point to Chain object.
Result:
this points to environment or window in Node.JS
Prototyping:
I use JSBin for prototyping. Here is the prototype there. It has essentially the same effect as my Node environment.
http://jsbin.com/OSaHaZAK/1/edit
Call it with apply or call to set the expected this and pass the arguments
_.apply(chain, ['extern_var', 'something', '$callback']);
FIDDLE
You must maintain the context for quicklink:
var _ = chain.quicklink.bind(chain);
The problem you have is classic:
var o = {};
o.f = function () { console.log(this); };
o.f(); //o
var f2 = o.f;
f2(); //window
Demo
read about how this works on MDN
instead of
var chain = Chain();
do this
var chain = new Chain();
new keyword create new instance.
more info from MDN forum. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/new
Here is another way to solve this riddle (to maintain the context).
var slice = Array.prototype.slice;
function Chain(options, links) {
if (!(this instanceof Chain)) return new Chain(options, slice.call(arguments));
return this;
}
Chain.prototype.quicklink = function quicklink() {
var args = arguments;
console.log(args);
};
var chain = Chain();
function _() {
var args = slice.call(arguments);
chain.quicklink.apply(chain, args);
}
_('extern_var', 'bob', '$callback');
In Javascript, the way to create classes (or objects) is to use;
function MyObj()
{
this.someVar = "xyz";
this.someMethod = function(){}
}
My simple question is how different is this function from a normal JavaScript function...say a function which adds 2 numbers?
The function aren't different. What makes the difference is how you call them.
For example, those have the same effect :
function MyObj(){
this.someVar = "xyz";
this.someMethod = function(){
console.log(this.someVar);
}
}
var obj = new MyObj();
obj.someMethod();
and
function someMethod(){
console.log(this.someVar);
}
function MyObj(){
this.someVar = "xyz";
}
var obj = new MyObj();
someMethod.call(obj);
and
function someMethod(){
console.log(this.someVar);
}
function MyObj(){
this.someVar = "xyz";
}
var obj = new MyObj();
obj.f = someMethod;
obj.f();
As you tagged your question prototypal-inheritance, I'll complete by saying the best way to build your function would have been this one :
function MyObj(){
this.someVar = "xyz";
}
MyObj.prototype.someMethod = function(){
console.log(this.someVar);
}
var obj = new MyObj();
obj.someMethod();
This way, all instances of MyObj share the same function and thus are lighter.
The difference is not so much in the contents of the function, but in how you call it.
If you call var myObj = new MyObj() then a new object is created. By convention functions intended for use like this start with a capital letter.
If you were to call the function without the new keyword then exactly the same things happen inside the function, except that this will be the global object instead of the newly created object. This wouldn't matter in a simple "add 2 numbers" function, but can cause very odd bugs if you forget it.
One way to ensure that it doesn't matter if you forget the new call is to put this in the top of your function:
function MyObj() {
if (! (this instanceof MyObj)) {
return new MyObj();
}
...
}
None. What matters is the use of the new keyword.
See here:
function Fun(){
this.method = function(){
return "Bar";
}
return "Foo";
}
var value = Fun(); // assigns the return value of Fun() to value
alert(value); // "Foo"
// alert(value.method()); // won't work because "Foo".method doesn't exist
var instance = new Fun(); // assigns a new instance of Fun() to instance
alert(instance); // [object Object]
alert(instance.method()); // "Bar"
Given var x, what is the best way to determine if x can have properties? Can I just do
if(x instanceof Object)
Is that sufficient to ensure that x can have properties or do I need to check for anything else? I know primitives can't have properties but is there anything else? I've been going through checking various types:
var a = false;
a.foo = "bar";
console.log(a["foo"]);
// Logs undefined
var b = "b";
b.foo = "bar";
console.log(b["foo"]);
// Logs undefined
var c = new Array(1,2,3);
c.foo = "bar";
console.log(c["foo"]);
// Logs bar
var d = new Object();
d.foo = "bar";
console.log(d["foo"]);
// Logs bar
var e = new RegExp("");
e.foo = "foo";
console.log(e["bar"]);
// Logs bar
var f = new Number(1);
f.foo = "bar";
console.log(f["foo"]);
// Logs bar
var g = function(){};
g.foo = "bar";
console.log(g["foo"]);
// Logs bar
etc..
Yes, that is sufficient. Note: String can also accept properties, which you are not checking for:
var a = new String("hello");
a.foo = "bar";
But since String instanceof Object == true you should be fine.
For fun, try this (it works, since /x/ instanceof Object == true):
var x = /hello/;
x.foo = "bar";
Another note: your instanceof check will catch this, but I want you to be aware that while normal a javascript function is an Object, a closure (function() { })() is not necessarily an Object, but it might be depending on the value it returns.
Hope that helps.
-tjw
You need to create a JavaScript object first like so:
<script>
var foo = {};
foo.bar = 'test';
console.log(foo.bar);
</script>
Vars a and b are just plain ole variables. The var foo = {}; bit is similar to a builtin object called foo and you created an instance like var bar = new foo();