Consider the following article explaining hashtables / dictionaries in JavaScript:
Can anyone recommend a good Hashtable implementation in Javascript?
Given the accepted answer, I want to be able to do this:
var instance = new Dictionary();
instance['key1'] = 'Foo';
instance['key2'] = 'Bar';
instance['key3'] = 123;
instance['key4'] = true;
but I want the key/value pairs to point to an object within the Dictionary internally. consider the following code structure
var Dictionary = function() {
var dictionary; // <-- key value pairs should point to this, not to the Dictionary function;
this.setValue = function(key, value) {
dictionary[key] = value;
}
this.getValue = function() {
return dictionary[key];
}
}
Is this possible?
EDIT:
One way I thought of designing the Dictionary object was like so:
var Dictionary = function() {
this.setValue = function(key, value) {
this[key] = value;
}
this.getValue = function(key) {
return this[key];
}
}
The problems with this are:
I can assign like so instance['key1'] = 'foo'; and read like this instance.key1; I don't want this!!
I can assign this instance['getValue'] = null; and then cannot get a value back because the function is now null!!
Neither of the above should occurr, hence the reason that the set and get functionality should apply to the internal object, not to the dictionary itself.
function Dictionary()
{
this.store = {};
this.setValue = function(key, value) {
store[key] = value;
}
this.getValue = function(key)
{
return store[key];
}
return this;
}
//Testing
var dict = Dictionary();
dict.setValue('key','value');
alert(dict.getValue('key'));//displays "value"
alert(dict.getValue('unassignedKey'));//displays "undefined"
alert(dict['key']);//displays "undefined" (an unfortunate lack of syntactic convenience, we have to go through getValue).
alert(dict.key);//displays "undefined"
var dict2 = Dictionary();
dict2.setValue('key2', 'value2');
alert(dict2.getValue('key'));//displays "undefined"
alert(dict2.getValue('key2'));//displays "value2"
Related
is there a way to listen for a property call on a JavaScript Class
for example when i go something like this:
myForm = new Form();
myForm.name = 'Name';
-> when i set the name i dont only want to set the property but i also want to update my Vuex store.
Same thing with get i would like to read from Vuex store.
I knoew there are thins like Proxy but for this i need to wrap my Class with a Proxy object. Not so sure if i like this.
module.exports = new Proxy(new Form({}), {
get (receiver, name) {
console.log('getting property from Vuex Store');
}
});
What i need is something like this:
module.exports = class Form {
//this should be triggered when form.something
get(property) {
return this[property];
}
//this should be triggered when from.something = 'something'
set(property, value) {
return this[property] = value;
}
};
it there a best practice for this?
Javascript supports getters and setters
class Form{
set foo(val){
console.log("setting foo")
this.fooValue = val;
}
get foo(){
console.log("getting foo");
return this.fooValue;
}
}
let frm = new Form();
frm.foo = "bar";
console.log(frm.foo);
You could make this more dynamic by writing a withGetterSetter method which wraps each property of an object with a getter/setter.
var form = {
a: "aValue",
b: "bValue"
}
function withGetterSetter(obj){
var keys = Object.keys(obj);
var result = {};
for(var i=0;i<keys.length;i++){
var key = keys[i];
result[key+"_internal"] = obj[key];
(function(k){
Object.defineProperty(result,k, {
get:function() {
console.log("getting property:",k);
return this[k + "_internal"];
},
set: function(x) {
console.log("setting property:",k);
this[k + "_internal"] = x
}
});
})(key)
}
return result;
}
var setterObj = withGetterSetter(form);
console.log(setterObj.a);
setterObj.a = "updated";
console.log(setterObj.a);
It works by copying each property p to a new object with p_internal and creating a dynamic get/set for the original property name.
Say I have an object like below:
var obj = {};
obj.test = function() { console.log(?); }
Is there anyway to print out "test", the key that this function is value of, but not know the obj name in advance?
Not really. Relationships in JS are one-way.
You could search for a match…
var obj = {};
obj.not = 1;
obj.test = function() {
var me = arguments.callee;
Object.keys(obj).forEach(function(prop) {
if (obj[prop] === me) {
console.log(prop);
}
});
};
obj.test();
But look at this:
var obj = {};
obj.not = 1;
obj.test = function() {
var me = arguments.callee;
Object.keys(obj).forEach(function(prop) {
if (obj[prop] === me) {
console.log(prop);
}
});
};
obj.test2 = obj.test;
obj.test3 = obj.test;
window.foo = obj.test;
obj.test();
The same function now exists on three different properties of the same object … and as a global.
Might be a bit of a convoluted solution, but this might be useful -
You can have a method that will add functions to your object at a specific key. Using the bind method, we can predefine the first argument to the function to be the key that was used to add it.
The function that I am adding to the key is _template, it's first argument will always be the key that it was added to.
var obj = {};
function addKey(key) {
obj[key] = _template.bind(null, key)
}
function _template(key, _params) {
console.log('Key is', key);
console.log('Params are',_params);
}
addKey('foo')
obj.foo({ some: 'data' }) // this will print "foo { some: 'data' }"
Reference - Function.prototype.bind()
try this Object.keys(this) and arguments.callee
var obj = {};
obj.test = function() {
var o = arguments.callee;
Object.values(this).map((a,b)=>{
if(a==o){
console.log(Object.keys(this)[b])
}
})
}
obj.one = "hi"
obj.test()
You can get the name of the method called with
arguments.callee.name
var a ={ runner_function : function(){ console.log(arguments.callee.name ); } };
a.runner_function() //It will return "runner_function"
Is it possible in any way to store a function in an IndexedDB datastore? I have done some searching and found nothing on the data types that IndexedDB supports. I tried simply adding a function and the instance of a function to an object store as follows, but it didn't work.
var myclass = function () {
self = this;
self.name = 'Sam';
self.hello = function () {
console.log('Hello ' + self.name);
};
}
var transaction = db.transaction(['codeobject'], 'readwrite');
var store = transaction.objectStore('codeobject');
var request = store.put({ 'classname': 'someclass', 'object': myclass });
And I have tried.
var request = store.put({ 'classname': someclass', 'object': new myclass() });
I would really like to be about to store a class in object db. Even if I had to store it as some type of blob and then serialize it back into a function on its way out.
Thank you
If I get your question right, what you want to do is serialize the entire object including functions?
This is possible to do, you only need to make it possible to serialize functions. For more information about it, see my blogpost about it.
function serialize(key, value) {
if (typeof value === 'function') {
return value.toString();
}
return value;
}
function deserialize(key, value) {
if (value && typeof value === "string" && value.substr(0, 8) == "function") {
var startBody = value.indexOf('{') + 1;
var endBody = value.lastIndexOf('}');
var startArgs = value.indexOf('(') + 1;
var endArgs = value.indexOf(')');
return new Function(value.substring(startArgs, endArgs), value.substring(startBody, endBody));
}
return value;
}
var jsonObj = {
func: function(){ return ""; }
}
// Turns an object into a json string including functions
var objectstring = JSON.stringify(jsonObj, serialize);
// Turns an object string in JSON into an object including functions.
var object = JSON.parse(objectstring , deserialize);
The objectstring you get can then be saved into the indexeddb.
You can store the function as a string:
store.put({ 'classname': 'someclass', 'object': myclass.toString() });
And then deserialize it back into a function via eval:
var myclass = Function('"use strict";return ' + functionString);
I am trying to create an object that defines getters/setters automatically for a new instance of an object. I want the setter to put the values in a separate object property called newValues. Why in the following code snippet does setting the value of prop1 actually set the value of newValues.prop2 instead of newValues.prop1?
Am I doing something silly here? It's totally possible as I am on only a few hours of sleep... :)
var Record = function(data) {
this.fieldValues = {}
this._data = data;
var record = this;
for(var key in data) {
record.__defineGetter__(key, function() {
return record._data[key];
});
record.__defineSetter__(key, function(val) {
record.fieldValues[key] = val;
});
}
}
var myRecord = new Record({prop1: 'prop1test', prop2: 'prop2test'});
myRecord.prop1 = 'newvalue';
console.log(myRecord.fieldValues.prop1); // undefined
console.log(myRecord.fieldValues.prop2); // 'newvalue'
Because when you eventually use the function that you've created for the getter/setter, key has its final value. You need to close over the value of key for each iteration of the loop. JavaScript has functional scope, not block scope.
var Record = function(data) {
var key;
this.fieldValues = {}
this._data = data;
for(key in data) {
//closure maintains state of "key" variable
//without being overwritten each iteration
(function (record, key) {
record.__defineGetter__(key, function() {
return record._data[key];
});
record.__defineSetter__(key, function(val) {
record.fieldValues[key] = val;
});
}(this, key));
}
}
This is the usual thing where people stumble with JS: The closure in a loop problem.
This explains it quite nicely along with a solution: http://www.mennovanslooten.nl/blog/post/62
I can't seem to find the way to overload the [] operator in javascript. Anyone out there know?
I was thinking on the lines of ...
MyClass.operator.lookup(index)
{
return myArray[index];
}
or am I not looking at the right things.
You can do this with ES6 Proxy (available in all modern browsers)
var handler = {
get: function(target, name) {
return "Hello, " + name;
}
};
var proxy = new Proxy({}, handler);
console.log(proxy.world); // output: Hello, world
console.log(proxy[123]); // output: Hello, 123
Check details on MDN.
You can't overload operators in JavaScript.
It was proposed for ECMAScript 4 but rejected.
I don't think you'll see it anytime soon.
The simple answer is that JavaScript allows access to children of an Object via the square brackets.
So you could define your class:
MyClass = function(){
// Set some defaults that belong to the class via dot syntax or array syntax.
this.some_property = 'my value is a string';
this['another_property'] = 'i am also a string';
this[0] = 1;
};
You will then be able to access the members on any instances of your class with either syntax.
foo = new MyClass();
foo.some_property; // Returns 'my value is a string'
foo['some_property']; // Returns 'my value is a string'
foo.another_property; // Returns 'i am also a string'
foo['another_property']; // Also returns 'i am also a string'
foo.0; // Syntax Error
foo[0]; // Returns 1
foo['0']; // Returns 1
Use a proxy. It was mentioned elsewhere in the answers but I think that this is a better example:
var handler = {
get: function(target, name) {
if (name in target) {
return target[name];
}
if (name == 'length') {
return Infinity;
}
return name * name;
}
};
var p = new Proxy({}, handler);
p[4]; //returns 16, which is the square of 4.
We can proxy get | set methods directly. Inspired by this.
class Foo {
constructor(v) {
this.data = v
return new Proxy(this, {
get: (obj, key) => {
if (typeof(key) === 'string' && (Number.isInteger(Number(key)))) // key is an index
return obj.data[key]
else
return obj[key]
},
set: (obj, key, value) => {
if (typeof(key) === 'string' && (Number.isInteger(Number(key)))) // key is an index
return obj.data[key] = value
else
return obj[key] = value
}
})
}
}
var foo = new Foo([])
foo.data = [0, 0, 0]
foo[0] = 1
console.log(foo[0]) // 1
console.log(foo.data) // [1, 0, 0]
As brackets operator is actually property access operator, you can hook on it with getters and setters. For IE you will have to use Object.defineProperty() instead. Example:
var obj = {
get attr() { alert("Getter called!"); return 1; },
set attr(value) { alert("Setter called!"); return value; }
};
obj.attr = 123;
The same for IE8+:
Object.defineProperty("attr", {
get: function() { alert("Getter called!"); return 1; },
set: function(value) { alert("Setter called!"); return value; }
});
For IE5-7 there's onpropertychange event only, which works for DOM elements, but not for other objects.
The drawback of the method is you can only hook on requests to predefined set of properties, not on arbitrary property without any predefined name.
one sneaky way to do this is by extending the language itself.
step 1
define a custom indexing convention, let's call it, "[]".
var MyClass = function MyClass(n) {
this.myArray = Array.from(Array(n).keys()).map(a => 0);
};
Object.defineProperty(MyClass.prototype, "[]", {
value: function(index) {
return this.myArray[index];
}
});
...
var foo = new MyClass(1024);
console.log(foo["[]"](0));
step 2
define a new eval implementation. (don't do this this way, but it's a proof of concept).
var MyClass = function MyClass(length, defaultValue) {
this.myArray = Array.from(Array(length).keys()).map(a => defaultValue);
};
Object.defineProperty(MyClass.prototype, "[]", {
value: function(index) {
return this.myArray[index];
}
});
var foo = new MyClass(1024, 1337);
console.log(foo["[]"](0));
var mini_eval = function(program) {
var esprima = require("esprima");
var tokens = esprima.tokenize(program);
if (tokens.length == 4) {
var types = tokens.map(a => a.type);
var values = tokens.map(a => a.value);
if (types.join(';').match(/Identifier;Punctuator;[^;]+;Punctuator/)) {
if (values[1] == '[' && values[3] == ']') {
var target = eval(values[0]);
var i = eval(values[2]);
// higher priority than []
if (target.hasOwnProperty('[]')) {
return target['[]'](i);
} else {
return target[i];
}
return eval(values[0])();
} else {
return undefined;
}
} else {
return undefined;
}
} else {
return undefined;
}
};
mini_eval("foo[33]");
the above won't work for more complex indexes but it can be with stronger parsing.
alternative:
instead of resorting to creating your own superset language, you can instead compile your notation to the existing language, then eval it. This reduces the parsing overhead to native after the first time you use it.
var compile = function(program) {
var esprima = require("esprima");
var tokens = esprima.tokenize(program);
if (tokens.length == 4) {
var types = tokens.map(a => a.type);
var values = tokens.map(a => a.value);
if (types.join(';').match(/Identifier;Punctuator;[^;]+;Punctuator/)) {
if (values[1] == '[' && values[3] == ']') {
var target = values[0];
var i = values[2];
// higher priority than []
return `
(${target}['[]'])
? ${target}['[]'](${i})
: ${target}[${i}]`
} else {
return 'undefined';
}
} else {
return 'undefined';
}
} else {
return 'undefined';
}
};
var result = compile("foo[0]");
console.log(result);
console.log(eval(result));
You need to use Proxy as explained, but it can ultimately be integrated into a class constructor
return new Proxy(this, {
set: function( target, name, value ) {
...}};
with 'this'. Then the set and get (also deleteProperty) functions will fire. Although you get a Proxy object which seems different it for the most part works to ask the compare ( target.constructor === MyClass ) it's class type etc. [even though it's a function where target.constructor.name is the class name in text (just noting an example of things that work slightly different.)]
So you're hoping to do something like
var whatever = MyClassInstance[4];
?
If so, simple answer is that Javascript does not currently support operator overloading.
Have a look at Symbol.iterator. You can implement a user-defined ##iterator method to make any object iterable.
The well-known Symbol.iterator symbol specifies the default iterator for an object. Used by for...of.
Example:
class MyClass {
constructor () {
this._array = [data]
}
*[Symbol.iterator] () {
for (let i=0, n=this._array.length; i<n; i++) {
yield this._array[i]
}
}
}
const c = new MyClass()
for (const element of [...c]) {
// do something with element
}