Javascript object not recognizing function - javascript

I have a Javascript class defined as below:
function Node(init, parent) {
this.value = init;
this.children = [];
this.updateChildren(this.value);
}
Node.prototype.updateChildren = function(value) {
this.children.push(value);
};
When I run it.. i receive the error,
this.updateChildren() is not defined.
Any clue what I am missing here ?
Full code here :
'use strict';
var millionNumbers = [];
for (var i = 2; i< 1000000; i++) {
if (!millionNumbers[i]) {
millionNumbers[i] = new Node(i, null);
}
}
function Node(init, parent) {
this.value = init;
this.children = [];
if (parent) {
this.parent = parent;
}
var newValue;
if (isEven(this.value)) {
newValue = this.value/2;
} else {
newValue = (this.value * 3) + 1;
}
//whether newValue is 1 or something else, we still have add it to the children list
this.updateChildren(newValue);
if (millionNumbers[newValue]) {
var chainedNode = millionNumbers[newValue];
this.children.concat(chainedNode.children);
}
if (newValue === 1) {
this.endChain();
} else {
new Node(newValue, this);
}
}
Node.prototype.updateChildren = function(value) {
this.children.push(value);
if(this.parent) {
this.parent.updateChildren(value);
}
};
Node.prototype.endChain = function() {
if (!millionNumbers[this.value]) {
millionNumbers[this.value] = this;
this.parent = null;
}
};
function isEven(value) {
if (value % 2 === 0) {
return true;
} else {
return false;
}
}

The for loop instantiates Node objects before Node.prototype.updateChildren is set, so within the Node constructor, this.updateChildren is still undefined.
To fix the problem, just move the for loop to the end of the file.
Also: Best of luck with the Collatz conjecture!

think you are calling these classes without defining them first.
You can try this way:
function updateChildren(value) {
this.children.push(value);
};
function Node(init, parent) {
this.value = init;
this.children = [];
this.updateChildren(value);
}
Node.prototype.updateChildren(value);

Related

How to expose only a function and not the variables from a class function?

Suppose I have this code:
function GraphFactory() {
this.nodeNames = [];
this.pinnedNodes = [];
this.initPinnedNodes = function(nodes) {
if (nodes) {
this.pinnedNodes = nodes;
} else {
this.pinnedNodes = [];
}
}
this.checkIfPinned = function(node) {
if (this.pinnedNodes.indexOf(node) > -1) {
return true;
} else {
return false;
}
}
this.addToPinnedNodes = function(name) {
this.pinnedNodes.push(name);
return true;
}
this.removeFromPinnedNodes = function(name) {
this.pinnedNodes.splice(this.pinnedNodes.indexOf(name), 1);
return true;
}
}
let graphFactory = new GraphFactory();
Right now i can access both the function
graphFactory.checkIfPinned(label);
but also directly the variable
graphFactory.pinnedNodes
How would I set things up so only the functions, but not the variables could get accessed?
Use variables instead of properties on the object.
let nodeNames = [];
They'll be closed over by your dynamically assigned instance methods.
Or see this question for the modern approach.
With the current construct, you can use a closure per object, practically a local variable in your constructor function:
function GraphFactory() {
var nodeNames = [];
var pinnedNodes = [];
this.initPinnedNodes = function(nodes) {
if (nodes) {
pinnedNodes = nodes;
} else {
pinnedNodes = [];
}
}
this.checkIfPinned = function(node) {
if (pinnedNodes.indexOf(node) > -1) {
return true;
} else {
return false;
}
}
this.addToPinnedNodes = function(name) {
pinnedNodes.push(name);
return true;
}
this.removeFromPinnedNodes = function(name) {
pinnedNodes.splice(this.pinnedNodes.indexOf(name), 1);
return true;
}
}
let graphFactory = new GraphFactory();
However such variables will not be accessible to methods you add later, from outside. For example that nodeNames exists in vain this way.

`this` reference gets lost in prototype chain

This is quite extensive, but fun and detailed.
I have defined two "classes" as follow, using the standard JavaScript ways of prototypes:
Source
function Source() {
this._sourceGuid = "";
this._periodGuid = "";
this._eventName = "";
this._eventKind = 0;
if (arguments.length > 0) {
if (arguments[0].__proto__ === this.__proto__) {
this.sourceGuid(arguments[0].sourceGuid());
this.periodGuid(arguments[0].periodGuid());
this.eventName(arguments[0].eventName());
this.eventKind(arguments[0].eventKind());
} else {
this.sourceGuid(arguments[0].sourceGuid);
this.periodGuid(arguments[0].periodGuid);
this.eventName(arguments[0].eventName);
this.eventKind(arguments[0].eventKind);
}
}
};
Source.prototype.sourceGuid = function (value) {
if (arguments.length > 0) {
this._sourceGuid = value
} else {
return this._sourceGuid;
}
};
Source.prototype.periodGuid = function (value) {
if (arguments.length > 0) {
this._periodGuid = value
} else {
return this._periodGuid;
}
};
Source.prototype.eventName = function (value) {
if (arguments.length > 0) {
this._eventName = value
} else {
return this._eventName;
}
};
Source.prototype.eventKind = function (value) {
if (arguments.length > 0) {
this._eventKind = value
} else {
return this._eventKind;
}
};
Source.prototype.toJSON = function() {
return {
sourceGuid: this.sourceGuid(),
periodGuid: this.periodGuid(),
eventName: this.eventName(),
eventKind: this._eventKind()
};
};
AnalogCamerasSource extends Source
function AnalogCamerasSource() {
this.__proto__.apply(this, arguments);
this._serverGuid = "";
this._cameraId = 0;
this._waitingTime = 0;
if (arguments.length > 0) {
if (arguments[0].__proto__ === this.__proto__) {
this.serverGuid(arguments[0].serverGuid());
this.cameraId(arguments[0].cameraId());
this.waitingTime(arguments[0].waitingTime());
} else {
this.serverGuid(arguments[0].serverGuid);
this.cameraId(arguments[0].cameraId);
this.waitingTime(arguments[0].waitingTime);
}
}
};
AnalogCamerasSource.prototype = Source;
AnalogCamerasSource.prototype.serverGuid = function (value) {
if (arguments.length > 0) {
this._serverGuid = value
} else {
return this._serverGuid;
}
};
AnalogCamerasSource.prototype.cameraId = function (value) {
if (arguments.length > 0) {
this._cameraId = value
} else {
return this._cameraId;
}
};
AnalogCamerasSource.prototype.waitingTime = function (value) {
if (arguments.length > 0) {
this._waitingTime = value
} else {
return this._waitingTime;
}
};
AnalogCamerasSource.prototype.toJSON = function() {
var json = this.__proto__.toJSON();
json.serverGuid = this.serverGuid();
json.cameraId = this.cameraId();
json.waitingTime = this.waitingTime();
return json;
};
Now I need an instance of AnalogCamerasSource, and I am trying to create it as simply as that:
var data = {"sourceGuid":"{05A00E05-F30D-497D-A272-156F135E1486}","periodGuid":"{8A071454-B473-4937-9C54-4899F866D7FA}","eventName":"signal-lost","eventKind":3,"serverGuid":"{9976B57D-486B-4BCA-8432-78D7C8EDB52B}","cameraId":4,"waitingTime":5};
var c = new AnalogCamerasSource(data);
Now, the line this.__proto__.apply(this, arguments); is responsible for calling the parent's constructor, and it seems it should do it fine, but at the moment it should call a parent's function in the parent's constructor, the following error is thrown:
TypeError: this.sourceGuid is not a function
at Function.Source (http://localhost:8081/js/app/events/model/0-Source.js:14:12)
at AnalogCamerasSource (http://localhost:8081/js/app/events/model/AnalogCamerasSource.js:3:33)
Now, here's a picture from Chrome's debugger showing the properties are there but way down the prototype chain, under this.__proto__.prototype when called like this.__proto__.apply(this, arguments) from AnalogCamerasSource.
So, why this.sourceGuid is not a function? How should I call the parent's constructor to have this chain working correctly?
Here's a fiddle: https://jsfiddle.net/98bp8pvh/
Refactored your code (avoid using __proto__ in your code)
function Source() {
var args = arguments[0] || {};
this._sourceGuid = args.sourceGuid || '';
this._periodGuid = args.periodGuid || '';
this._eventName = args.eventName || '';
this._eventKind = args.eventKind || 0;
};
Source.prototype.sourceGuid = function(value) {
if (arguments.length > 0) {
this._sourceGuid = value || '';
} else {
return this._sourceGuid;
}
};
Source.prototype.periodGuid = function(value) {
if (arguments.length > 0) {
this._periodGuid = value || '';
} else {
return this._periodGuid;
}
};
Source.prototype.eventName = function(value) {
if (arguments.length > 0) {
this._eventName = value || '';
} else {
return this._eventName;
}
};
Source.prototype.eventKind = function(value) {
if (arguments.length > 0) {
this._eventKind = value || 0;
} else {
return this._eventKind;
}
};
Source.prototype.toJSON = function() {
return {
sourceGuid: this.sourceGuid(),
periodGuid: this.periodGuid(),
eventName: this.eventName(),
eventKind: this.eventKind()
};
};
function AnalogCamerasSource() {
var args = arguments[0] || {};
this._serverGuid = args.serverGuid || '';
this._cameraId = args.cameraId || 0;
this._waitingTime = args.waitingTime || 0;
this.sourceGuid(args.sourceGuid);
this.periodGuid(args.periodGuid);
this.eventName(args.eventName);
this.eventKind(args.eventKind);
};
AnalogCamerasSource.prototype = Object.create(Source.prototype);
AnalogCamerasSource.prototype.serverGuid = function(value) {
if (arguments.length > 0) {
this._serverGuid = value || '';
} else {
return this._serverGuid;
}
};
AnalogCamerasSource.prototype.cameraId = function(value) {
if (arguments.length > 0) {
this._cameraId = value || 0;
} else {
return this._cameraId;
}
};
AnalogCamerasSource.prototype.waitingTime = function(value) {
if (arguments.length > 0) {
this._waitingTime = value || 0;
} else {
return this._waitingTime;
}
};
AnalogCamerasSource.prototype.toJSON = function() {
var json = Source.prototype.toJSON.call(this);
json.serverGuid = this.serverGuid();
json.cameraId = this.cameraId();
json.waitingTime = this.waitingTime();
return json;
};
var data = {
"sourceGuid": "{05A00E05-F30D-497D-A272-156F135E1486}",
"periodGuid": "{8A071454-B473-4937-9C54-4899F866D7FA}",
"eventName": "signal-lost",
"eventKind": 3,
"serverGuid": "{9976B57D-486B-4BCA-8432-78D7C8EDB52B}",
"cameraId": 4,
"waitingTime": 5
};
var c = new AnalogCamerasSource(data);
console.log(c);
Keep it simple, do your inheritance like this:
AnalogCamerasSource.prototype = Source.prototype;
...and just apply the constructor function of the parent class in the constructor of the subclass:
function AnalogCamerasSource() {
Source.apply(this, arguments);
...
}
There usually isn't any need to reference __proto__ directly.
Try changing AnalogCamerasSource.prototype = Source; to AnalogCamerasSource.prototype = new Source; and this.__proto__.apply(this, arguments); to Source.apply(this, arguments);.
The problem is that with your approach AnalogCameraSource.prototype gets set to a function (constructor for Source object), rather than to an object. This function itself does not have the methods defined on its prototype. If you set AnalogCameraSource.prototype to a new Source object (created with new Source), then AnalogCameraSource.prototype is an object, which is how things ought to be. This puts all methods defined on the Source prototype in the prototype chain of AnalogCameraSource, and the Source constructor is able to run without a hitch.
I solved my problem with this workaround:
I changed the prototype definition:
AnalogCamerasSource.prototype = new Source();
Then I added the following function to AnalogCamerasSource:
AnalogCamerasSource.prototype.super = function() {
Source.apply(this, arguments);
};
And then in the constructor I am calling it as:
function AnalogCamerasSource() {
this.super.apply(this, arguments);
...
};

How this and $(this) end up being the same when extending jQuery?

I'm trying to work with a plugin which extends jQuery like so:
$.extend({
StatelessDeferred: function () {
var doneList = $.Callbacks("memory"),
promise = {
done: doneList.add,
// Get a promise for this deferred
// If obj is provided, the promise aspect is added to the object
promise: function (obj) {
var i,
keys = ['done', 'promise'];
if (obj === undefined) {
obj = promise;
} else {
for (i = 0; i < keys.length; i += 1) {
obj[keys[i]] = promise[keys[i]];
}
}
return obj;
}
},
deferred = promise.promise({});
deferred.resolveWith = doneList.fireWith;
return deferred;
}
});
Problem is (and I'm not even sure it's caused here), after the callback loads, inside a done callback, both this and $(this) are the same, so I end up for example with: this === $(this) === $(document).
I'm not really sure I understand what's being extended. The plugin works fine with it except for the false assignment.
Question:
Could the above extension be causing this === $(this) === $(document)?
EDIT:
Full plugin (120lines):
"use strict";
(function (window, $) {
$.extend({
StatelessDeferred: function () {
var doneList = $.Callbacks("memory"),
promise = {
done: doneList.add,
// Get a promise for this deferred
// If obj is provided, the promise aspect is added to the object
promise: function (obj) {
var i,
keys = ['done', 'promise'];
if (obj === undefined) {
obj = promise;
} else {
for (i = 0; i < keys.length; i += 1) {
obj[keys[i]] = promise[keys[i]];
}
}
return obj;
}
},
deferred = promise.promise({});
deferred.resolveWith = doneList.fireWith;
// All done!
return deferred;
}
});
var routes = [],
current_priority = 0,
methods = {
add: function (pattern, priority) {
var i = 0,
inserted = false,
length = routes.length,
dfr = $.StatelessDeferred(),
context = $(this),
escapepattern,
matchingpattern;
if (priority === undefined) {
priority = 0;
}
if (pattern !== undefined) {
// http://simonwillison.net/2006/Jan/20/escape/
escapepattern = pattern.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&");
matchingpattern = escapepattern
.replace(/<int:\w+>/g, "(\\d+)")
.replace(/<path:\w+>/g, "(.+)")
.replace(/<\w+>/g, "([^/]+)");
while (!inserted) {
if ((i === length) || (priority >= routes[i][2])) {
routes.splice(i, 0, [new RegExp('^' + matchingpattern + '$'), dfr, priority, context]);
inserted = true;
} else {
i += 1;
}
}
}
return dfr.promise();
},
go: function (path, min_priority) {
var dfr = $.Deferred(),
context = $(this),
result;
if (min_priority === undefined) {
min_priority = 0;
}
setTimeout(function () {
var i = 0,
found = false,
slice_index = -1,
slice_priority = -1;
for (i = 0; i < routes.length; i += 1) {
if (slice_priority !== routes[i][2]) {
slice_priority = routes[i][2];
slice_index = i;
}
if (routes[i][2] < min_priority) {
break;
} else if (routes[i][0].test(path)) {
result = routes[i][0].exec(path);
dfr = routes[i][1];
context = routes[i][3];
current_priority = routes[i][2];
found = true;
break;
}
}
if (i === routes.length) {
slice_index = i;
}
if (slice_index > -1) {
routes = routes.slice(slice_index);
}
if (found) {
dfr.resolveWith(
context,
result.slice(1)
);
} else {
dfr.rejectWith(context);
}
});
return dfr.promise();
},
};
$.routereset = function () {
routes = [];
current_priority = 0;
};
$.routepriority = function () {
return current_priority;
};
$.fn.route = function (method) {
var result;
if (methods.hasOwnProperty(method)) {
result = methods[method].apply(
this,
Array.prototype.slice.call(arguments, 1)
);
} else {
$.error('Method ' + method +
' does not exist on jQuery.route');
}
return result;
};
}(window, jQuery));
So I can use this as a router and set a route like so:
$(".element").add("route", "/foo/bar/<path:params>", 2).done(function(params){
// do something, for example
console.log(this);
console.log($(this));
console.log("which will be the same = $('.element'));
});
Hope it's more clear now.
Thanks for having a look.
From the documentation:
If only one argument is supplied to $.extend(), this means the target argument was omitted. In this case, the jQuery object itself is assumed to be the target.
Most cases, jQuery is attached to your document with : $(document).ready()
I think what's happening is jQuery object is wrapped onto the document. Then you merged it with $.extend(myObject). This returns a single object that is both jQuery object and myObject.

JavaScript extend Array and add methods to child protype?

I want to create a js class that resembles simple music playlist (Array). I want to instantiate this playlist with IDs, each ID being a track ID in my database. I have interface like this:
function Playlist() {
Playlist.prototype.current = 0;
Playlist.prototype.prev = function() {
if (this.current-1 < 0) {
return null;
}
return this[--this.current];
};
Playlist.prototype.next = function() {
if (this.current+1 >= this.length) { // length is index + 1
return null;
}
return this[++this.current];
};
Playlist.prototype.seek = function(id) {
for (i in this) {
if (this[i] == id) {
this.current = parseInt(i);
return i;
}
}
return false;
};
Playlist.prototype.getCurrent() {
return this.current;
};
};
The code above DOES NOT do what I want, because I imagine it as class that has it's method defined, that can be instantiated like this:
var newPlaylist = Playlist(2,3,5,10/* those are my ids */);
And currently the only way I've found is something like:
Playlist.prototype = new Array(2, 3, 5, 10/* those are my ids */);
Which does not make any sense since it can be instantiated as different objects. Any ideas are very welcome!
Best way - nested array;
function Playlist() {
this.current = 0;
this.list = Array.prototype.slice.call(arguments);;
};
Playlist.prototype.prev = function() {
if (this.current-1 < 0) {
return null;
}
return this.list[--this.current];
};
Playlist.prototype.next = function() {
if (this.current+1 >= this.list.length) { // length is index + 1
return null;
}
return this.list[++this.current];
};
Playlist.prototype.getCurrent = function() {
return this.current;
};
var newPlaylist = new Playlist(2,3,5,10/* those are my ids */);
But you can't use list[i] to get element by index, but you just need add at() method to your class that provide similar functionality
PlayList.prototype.at(i) {
return this.list[i];
}
Since you cannot subclass Array, you should build wrapper objects with your Playlist constructor:
Playlist = (function() {
function Playlist(list) {
this.list = list || [];
}
Playlist.prototype.current = 0;
Playlist.prototype.prev = function() {
if (this.current <= 0)
return null;
return this.list[--this.current];
};
Playlist.prototype.next = function() {
if (this.current+1 >= this.length)
return null;
return this.list[++this.current];
};
Playlist.prototype.seek = function(id) {
return this.list.indexOf(id);
};
return Playlist;
})();
Usage:
var newPlaylist = new Playlist([2,3,5,10]);

Converting a javascript library to chain methods

var Stuff = (function() {
return {
getId: function (id) {
return document.getElementById(id);
},
attr: function (ele, attr, newVal) {
var newVal = newVal || null;
if (newVal) {
ele.setAttribute(attr, newVal);
} else {
var attrs = ele.attributes,
attrslen = attrs.length,
result = ele.getAttribute(attr) || ele[attr] || null;
if (!result) {
for (var i = 0; i < attrslen; i++)
if (attr[i].nodeName === attr) result = attr[i].nodeValue;
}
return result;
}
}
}
})();
With this html:
<div id="foo" data-stuff="XYZ">Test Div</div>
The current implementation:
(function ($) {
console.log(
$.attr($.getId('foo'), 'data-stuff') // XYZ
);
})(Stuff);
How do I rewrite my library above to make it chain like the following?
(function ($) {
console.log(
$.getId('foo').attr('data-stuff') // XYZ
);
})(Stuff);
Building specifically from your code, you could do this:
Example: http://jsfiddle.net/patrick_dw/MbZ33/
var Stuff = (function() {
return {
elem:null,
getId: function (id) {
this.elem = document.getElementById(id);
return this;
},
attr: function (attr, newVal) {
var newVal = newVal || null;
var ele = this.elem;
if (newVal) {
ele.setAttribute(attr, newVal);
} else {
var attrs = ele.attributes,
attrslen = attrs.length,
result = ele.getAttribute(attr) || ele[attr] || null;
if (!result) {
for (var i = 0; i < attrslen; i++)
if (attr[i].nodeName === attr) result = attr[i].nodeValue;
}
return result;
}
}
}
})();
This adds a elem property which stores the result of the getId. The getId returns this, which is the object referenced by Stuff containing all the methods. As such, you can directly call attr from the returned object.
I would imagine you would want to include a return value for attr of this for when the attribute is being set, so that chaining can continue.

Categories