If I have this code
var node = function(n) {
var name = n;
var children = [];
var finished = false;
var failed = false;
this.getName = function() {
return name
};
this.downloadData = function(obj) {
};
this.getChildren = function() {
return children;
};
this.setChildren = function(c) {
Array.prototype.push.apply(children, c);
};
this.isFinished = function() {
return finished;
};
this.setFinished = function() {
finished = true;
}
this.isFailed = function() {
return failed;
}
this.setFailed = function() {
failed = true;
}
};
How can I convert this into an object like:
var a = new node("a");
var j = JSON.stringify(a);
result
{"name":"a","children":[],"finished":false,"failed":false}
thanks
This could be done by implementing the toJSON function.
If an object being stringified has a property named toJSON whose value
is a function, then the toJSON() method customizes JSON
stringification behavior: instead of the object being serialized, the
value returned by the toJSON() method when called will be serialized.
- Mozilla
eg:
var node = function(n) {
var name = n;
var children = [];
var finished = false;
var failed = false;
this.toJson = function toJson() {
return {"name":name ... };
}
}
You need object properties instead of variables.
So, instead of declaring var name = n;, you would declare this.name = n;. Which would make it look something like
var node = function(n) {
this.name = n;
this.children = [];
this.finished = false;
this.failed = false;
///other functions here
}
Related
function HashTable(){
var size = 0;
var entry = new Object();
this.add = function(key,value){
if(!containsKey(key)){
size++;
}
entry[key] = value;
}
this.getValue = function(key){
return containsKey(key)?entry[key]:null;
}
this.remove = function(key){
if (containsKey(key) && delete entry[key]) {
size--;
}
}
this.containsKey = function(key){
return (key in entry);
}
this.containsValue = function(value){
for(var prop in entry){
if(entry[prop] == value){
return true;
}
}
return false;
}
//get all values
this.getValues = function(){
var values = new Array();
for(var prop in entry){
values.push(entry[prop]);
}
return values;
}
//get all keys
this.getKeys = function(){
var keys = new Array();
for(var prop in entry){
values.push(prop);
}
return keys;
}
this.getSize = function(){
return size;
}
this.clear = function(){
size = 0;
entry = new Object;//???????????????????
}
}
var hashtest = new HashTable();
hashtest.add('name','LiMing');
I want to implement the hashtable in javascript but when I test it ,there is an exception like this:
Uncaught ReferenceError: containsKey is not defined
at HashTable.add (:8:3)
at :64:10
The problem you have is an issue of scope. The javascript interpreter does not know you mean the containsKey from the HashTable class.
When calling functions within the class scope be sure to reference them with "this". So containsKey should be referenced as this.containsKey(key). That way the interpreter knows you are referring to the class' scope and not the local scope.
You should also do this for the variables with class scope. So when you do size++ you should actually write this.size++. Same for entry. If you do not add "this", it will assume it is a local function or variable defined within that function itself.
So you should rewrite your add() function as
this.add = function(key,value){
if(!this.containsKey(key)){
this.size++;
}
this.entry[key] = value;
}
Why are you manually keeping track of the size anyway? You can simply define "entry" as an array and use this.entry.size
Considering the more specific case of the hashmap, I would advice you to simply create two arrays within the object, one for the keys and one for the values, this will drastically simplify the problem for you because then you can simply use the builtin Javascript array functions. Both arrays will have a numerical index but the key and the value will both always have the same index so you can easily math them up. The result would be like this:
function HashTable() {
this.keys = new Array();
this.values = new Array();
this.add = function(key, value) {
if (this.containsKey(key)) {
var index = this.keys.indexOf(key);
this.values[index] = value;
} else {
this.keys.push(key);
this.values.push(value);
}
}
this.containsKey = function(key) {
return this.keys.includes(key);
}
this.containsValue = function(value) {
return this.values.includes(value);
}
this.get = function(key) {
var index = this.keys.indexOf(key);
return this.values[index];
}
this.remove = function(key) {
if (this.containsKey(key)) {
var index = this.keys.indexOf(key);
this.keys.splice(index, 1);
this.values.splice(index, 1);
}
}
this.size = function() {
return this.keys.length;
}
this.clear = function() {
this.keys = new Array();
this.values = new Array();
}
}
// Create hashtable
var hashTable = new HashTable();
// Add some test data
hashTable.add('name', 'LiMing');
hashTable.add('location', 'At home');
hashTable.add('eyes', 'blue');
// Updates the value, doesn't add it
hashTable.add('eyes', 'brown');
console.log(hashTable.get("eyes"));
// Get the size
console.log(hashTable.size());
// Check if a value or key is in the hashtable
console.log(hashTable.containsValue("test")); // False
console.log(hashTable.containsValue("LiMing")); // True
console.log(hashTable.containsKey("name")); // True
console.log(hashTable.containsKey("age")); // False
// Get all the keys and values
console.log(hashTable.keys);
console.log(hashTable.values);
// Remove an item
hashTable.remove('eyes');
console.log(hashTable.keys);
console.log(hashTable.values);
// Clear hashtable
hashTable.clear();
console.log(hashTable.keys);
console.log(hashTable.values);
Use this.containsKey instead, as containsKey is a 'member' method inside the object created by HashTable, you have to reference it with this.
function HashTable(){
var size = 0;
var entry = new Object();
this.containsValue = function(value){
for(var prop in entry){
if(entry[prop] == value){
return true;
}
}
return false;
}
this.add = function(key,value){
if(!this.containsKey(key)){
size++;
}
entry[key] = value;
}
this.getValue = function(key){
return this.containsKey(key)?entry[key]:null;
}
this.remove = function(key){
if (this.containsKey(key) && delete entry[key]) {
size--;
}
}
this.containsKey = function(key){
return (key in entry);
}
//get all values
this.getValues = function(){
var values = new Array();
for(var prop in entry){
values.push(entry[prop]);
}
return values;
}
//get all keys
this.getKeys = function(){
var keys = new Array();
for(var prop in entry){
values.push(prop);
}
return keys;
}
this.getSize = function(){
return size;
}
this.clear = function(){
size = 0;
entry = new Object;//???????????????????
}
}
var hashtest = new HashTable(); hashtest.add('name','LiMing');
console.log(hashtest.getValues())
There's a better way to organize your code using ES6:
class HashTable {
constructor() {
this.size = 0;
this.entry = new Object();
}
containsValue(value) {
for(var prop in entry){
if(this.entry[prop] == value){
return true;
}
}
return false;
}
add(key,value) {
if(!this.containsKey(key)){
this.size++;
}
this.entry[key] = value;
}
getValue(key) {
return this.containsKey(key) ? this.entry[key] : null;
}
remove(key) {
if (this.containsKey(key) && delete this.entry[key]) {
size--;
}
}
containsKey(key) {
return (key in this.entry);
}
//get all values
getValues() {
var values = new Array();
for(var prop in this.entry){
values.push(this.entry[prop]);
}
return values;
}
//get all keys
getKeys() {
var keys = new Array();
for(var prop in this.entry){
values.push(prop);
}
return keys;
}
getSize() {
return this.size;
}
clear() {
this.size = 0;
this.entry = new Object();//???????????????????
}
}
var hashtest = new HashTable(); hashtest.add('name','LiMing');
console.log(hashtest.getValues())
This doesn't work.
var genericClickHandler = function () {
this.handlers = [];
if (console && console.log) {
console.log("this:", this);
console.log("event:", event);
}
};
genericClickHandler.addHandler = function (handlerSpec) {
this.handlers.push(handlerSpec);
return this;
};
genericClickHandler.executeHandler = function (handlerName) {
for (var i = 0; i < this.handlers.length; i++) {
if (handlerName === this.handlers[i][0]) {
this.handlers[i][1]();
}
}
return this;
};
It doesn't work because the addHandler can't see the this.handlers in genericClickHandler.
Anyway what I'm after is function that gets defined once, but has methods and properties. I want to be able to use the function with Google Maps like this:
heatmap.addListener("click", genericClickHandler)
circle.addListener("click", genericClickHandler)
polygons.addListener("click", genericClickHandler)
So in the first instance, it only reports the this and event object. However, I then want to write code which extends the genericClickHandler dynamically so that it can implement map-object-specific behaviour.
Here's an example of what I meant using an object rather than a function.
var genericClickHandler = {
handlers: []
};
genericClickHandler.addHandler = function (name, fn) {
this.handlers.push([name, fn]);
return this;
};
genericClickHandler.executeHandler = function (name) {
for (var i = 0, l = this.handlers.length; i < l; i++) {
if (this.handlers[i][0] === name) this.handlers[i][1]();
}
};
genericClickHandler.addHandler('click', function () {
console.log('hi');
});
genericClickHandler.addHandler('click', function () {
console.log('hallo again');
});
genericClickHandler.executeHandler('click'); // hi... hallo again
DEMO
if you want to create an object, here you can see 2 ways to do the same thing, javascript got multiple way to write the same things.
var genericClickHandler = function()
{
this.handlers = [];
this.addHandler = function (handlerSpec)
{
this.handlers.push(handlerSpec);
return this;
},
this.executeHandler = function (handlerName)
{
this.handlers[handlerName]();
return this;
}
};
//sample:
var tmp = new genericClickHandler();
console.log(tmp.handlers);
console.log(tmp.addHandler("TEST"));
Another way to write the same object, but more optimised : prototype will be stored once for each object
var genericClickHandler = function(){}
genericClickHandler.prototype =
{
handlers:[],
addHandler : function (handlerSpec)
{
this.handlers.push(handlerSpec);
return this;
},
executeHandler : function (handlerName)
{
this.handlers[handlerName]();
return this;
}
}
//sample:
var tmp = new genericClickHandler();
console.log(tmp.handlers);
console.log(tmp.addHandler("TEST"));
I have a sealed object with an array member on which I want to prevent direct pushes.
var myModule = (function () {
"use strict";
var a = (function () {
var _b = {},
_c = _c = "",
_d = [];
Object.defineProperty(_b, "c", {
get: function () { return _c; }
});
Object.defineProperty(_b, "d", {
get { return _d; }
});
_b.addD = function (newD) {
_d.push(newD);
};
Object.seal(_b);
return _b;
}());
var _something = { B: _b };
return {
Something: _something,
AddD: _b.addD
};
}());
myModule.Something.c = "blah"; // doesn't update = WIN!!
myModule.AddD({}); // pushed = WIN!
myModule.Something.d.push({}); // pushed = sadness
How can I prevent the push?
UPDATE:
Thanks for all the thoughts. I eventually need the JSON to send to the server. It looks like I might need to use an object for the array then figure out a way to generate and return the JSON needed, or change _something to use .slice(). Will play and report.
you could override the push method:
var _d = [];
_d.__proto__.push = function() { return this.length; }
and when you need to use it in your module, call Array.prototype.push:
_b.addD = function (newD) {
Array.prototype.push.call(_d, newD);
};
I haven't done any performance tests on this, but this certainly helps to protect your array.
(function(undefined) {
var protectedArrays = [];
protectArray = function protectArray(arr) {
protectedArrays.push(arr);
return getPrivateUpdater(arr);
}
var isProtected = function(arr) {
return protectedArrays.indexOf(arr)>-1;
}
var getPrivateUpdater = function(arr) {
var ret = {};
Object.keys(funcBackups).forEach(function(funcName) {
ret[funcName] = funcBackups[funcName].bind(arr);
});
return ret;
}
var returnsNewArray = ['Array.prototype.splice'];
var returnsOriginalArray = ['Array.prototype.fill','Array.prototype.reverse','Array.prototype.copyWithin','Array.prototype.sort'];
var returnsLength = ['Array.prototype.push','Array.prototype.unshift'];
var returnsValue = ['Array.prototype.shift','Array.prototype.pop'];
var funcBackups = {};
overwriteFuncs(returnsNewArray, function() { return []; });
overwriteFuncs(returnsOriginalArray, function() { return this; });
overwriteFuncs(returnsLength, function() { return this.length; });
overwriteFuncs(returnsValue, function() { return undefined; });
function overwriteFuncs(funcs, ret) {
for(var i=0,c=funcs.length;i<c;i++)
{
var func = funcs[i];
var funcParts = func.split('.');
var obj = window;
for(var j=0,l=funcParts.length;j<l;j++)
{
(function() {
var part = funcParts[j];
if(j!=l-1) obj = obj[part];
else if(typeof obj[part] === "function")
{
var funcBk = obj[part];
funcBackups[funcBk.name] = funcBk;
obj[part] = renameFunction(funcBk.name, function() {
if(isProtected(this)) return ret.apply(this, arguments);
else return funcBk.apply(this,arguments);
});
}
})();
}
}
}
function renameFunction(name, fn) {
return (new Function("return function (call) { return function " + name +
" () { return call(this, arguments) }; };")())(Function.apply.bind(fn));
};
})();
You would use it like so:
var myArr = [];
var myArrInterface = protectArray(myArr);
myArr.push(5); //Doesn't work, but returns length as expected
myArrInterface.push(5); //Works as normal
This way, you can internally keep a copy of the interface that isn't made global to allow your helper funcs to modify the array as normal, but any attempt to use .push .splice etc will fail, either directly, or using the .bind(myArr,arg) method.
It's still not completely watertight, but a pretty good protector. You could potentially use the Object.defineProperty method to generate protected properties for the first 900 indexes, but I'm not sure of the implications of this. There is also the method Object.preventExtensions() but I'm unaware of a way to undo this effect when you need to change it yourself
Thank you, dandavis!
I used the slice method:
var myModule = (function () {
"use strict";
var a = (function () {
var _b = {},
_c = _c = "",
_d = [];
Object.defineProperty(_b, "c", {
get: function () { return _c; }
});
Object.defineProperty(_b, "d", {
get { return _d.slice(); } // UPDATED
});
_b.updateC = function (newValue) {
_c = newValue;
};
_b.addD = function (newD) {
_d.push(newD);
};
Object.seal(_b);
return _b;
}());
var _something = { B: _b };
return {
Something: _something,
AddD: _b.addD
};
}());
myModule.Something.c = "blah"; // doesn't update = WIN!!
myModule.AddD({}); // pushed = WIN!
myModule.Something.d.push({}); // no more update = happiness
This allows me to protect from direct push calls enforcing some logic.
I've got a class that is basically a native Javascript Array, but it raises events when items are added or removed.
hb.extend( {
Classes: {
Collection: hbClass.inherit({
init: function (arr) {
// get the functions we want to retain
var _on = this.on,
_trigger = this.trigger,
_push = this.push,
_remove = this.remove,
_reset = this.reset,
_from = this.fromArray,
_watch = this.watch;
// Set the object up as an Array
this.__proto__ = Array.prototype;
// get the Array functions we want to use
this.arrPush = this.push;
// reapply the old functions
this.push = _push;
this.remove = _remove;
this.reset = _reset;
this.fromArray = _from;
this.on = _on;
this.trigger = _trigger;
this.watch = _watch;
if (arr && (arr.length && typeof arr !== "string")) this.fromArray(arr, true);
},
fromArray: function (arr, stopEvent) {
this.reset();
for (var i = 0, len = arr.length; i < len; i++) {
this.arrPush(arr[i]);
}
if (!stopEvent) this.trigger('change', this);
},
push: function () {
this.arrPush.apply(this, arguments);
this.trigger('add', this);
this.trigger('change', this);
return this;
},
remove: function (from, to) {
var rest = this.slice((to || from) + 1 || this.length);
this.length = from < 0 ? this.length + from : from;
this.arrPush.apply(this, rest);
this.trigger('remove', this);
this.trigger('change', this);
return this;
},
reset: function () {
this.length = 0;
this.trigger('change', this);
this.trigger('remove', this);
}
})
}
});
There may be better ways to do it, but it works for me.......except in IE.
In IE at the line this.arrPush.appy(this, arguments); under the push method, it hits a Stack Overflow error.
Specifically:
SCRIPT28: Out of stack space
But this does NOT occur in Firefox or Chrome.
Anyone have any advice?
EDIT
Trigger code:
this.hbClass.prototype.trigger = function(type, data, context) {
var listeners, handlers, i, n, handler, scope;
if (!(listeners = this.listeners)) {
return;
}
if (!(handlers = listeners[type])){
return;
}
for (i = 0, n = handlers.length; i < n; i++){
handler = handlers[i];
if (handler.method.call(
handler.context, this, type, data
)===false) {
return false;
}
}
return true;
}
The issue is probably this line:
this.__proto__ = Array.prototype;
as __proto__ is not supported in some versions of IE. It has been codified in the ES6 specification, but that isn't implemented in some versions of IE. I don't understand exactly how your code works, but the safe way to set a prototype is like this:
Working demo: http://jsfiddle.net/jfriend00/ff99G/
function myClass() {
// add new methods to this instance in the constructor
this.fromArray = function() {};
};
// become an array and get all its methods
myClass.prototype = Array.prototype;
var x = new myClass();
Here's an example of the kind of thing you're doing using .prototype that works in IE:
function log(msg) {
var result = document.getElementById("result");
var div = document.createElement("div");
div.innerHTML = msg;
result.appendChild(div);
}
function myClass() {
var _push = this.push;
this.count = function() {
return this.length;
}
this.trigger = function(type, name) {
var str = type;
if (name) {
str += ", " + name;
}
log(str);
}
this.push = function() {
var retVal = _push.apply(this, arguments);
this.trigger("change", "push");
return retVal;
}
};
// become an array and get all its methods
myClass.prototype = Array.prototype;
var x = new myClass();
x.push("foo");
x.push("whatever");
log(x.count());
Basically how do I call a base method using this patter below?
var GS = {};
GS.baseClass = function (somedata) {
var that = {};
that.data = somedata;
//Base class method
that.someMethod = function(somedata) {
alert(somedata);
};
return that;
};
GS.derivedClass = function(somedata) {
var that = GS.baseClass(somedata);
//Overwriting base method
that.someMethod = function(somedata) {
//How do I call base method from here?
//do something else
};
return that;
};
Thanks.
var GS = {};
GS.baseClass = function (somedata) {
var that = {};
that.data = somedata;
//Base class method
that.someMethod = function(somedata) {
alert(somedata);
};
return that;
};
GS.derivedClass = function(somedata) {
var that = GS.baseClass(somedata);
//Overwriting base method
var basemethod = that.someMethod;
that.someMethod = function(somedata) {
//How do I call base method from here?
basemethod.apply(that, [somedata]);
//do something else
};
return that;
};
Cheers.