I have the following code (which is actually creating a Set class, or to be more explicit is creating an unordered collection of values, with no duplicates).
Unfortunately it doesn't work ==> running it in a debugger I see that the following line is returning "undefined":
o[prop] = Set._v2s.next++;
I think this is happening because o="o", so that I can't use this Set class with strings.
Any idea how to modify the code such that I can use it with strings (like in my attached example)?
Here is the code:
function Set(){ // the constructor
this.values = {}; // an empty object that will keep all the set elements'
// names as properties
this.n = 0; // #values in the set
this.add.apply(this, arguments); // when initially build the set then add all the arguments of the constructor into the set
}
// Add each of the arguments of the constructor to the set
Set.prototype.add = function(){
for (var i=0; i<arguments.length; i++){ // for each argument of the constructor
var val = arguments[i];
var str = Set._v2s(val); // transform the value to a string
if (!this.values.hasOwnProperty(str)){ // If not already in the set
this.values[str]=val; // Load the element in the set
this.n++;
}
}
return this; // support chained method call
};
// Remove each of the arguments from the set
Set.prototype.remove = function(){
for (var i=0; i<arguments.length; i++){ // for each argument
var str = Set._v2s(arguments[i]);
if (this.values.hasOwnProperty(str)){ // If the element is in the set already
delete this.values[str];
this.n--; // Delete it
}
}
return this;
};
// Return true if the set contains a value; false otherwise
Set.prototype.contains = function(value){
return this.values.hasOwnProperty(Set._v2s(value));
};
// Return the size of the set
Set.prototype.size = function(){
return this.n;
};
// Call function f on the specified context for each element of the set.
Set.prototype.foreach = function(f,context){
for (var s in this.values)
if (this.values.hasOwnProperty(s)) // ignore inherited props
f.call(context,this.values[s]); // call f on the value
};
// This internal function maps any JavaScript value to a unique string.
Set._v2s = function(val){
switch (val){
case undefined: return 'u'; // special primitive
case null: return 'n';
case true: return 't';
case false: return 'f';
default: switch(typeof val){
case 'number': return '#' + val; // numbers get the # prefix
case 'string': return '#' + objectId(val);
}
}
};
// for any object, return a string ( a unique one per object, and if applied repeatedly on the same object will return the same string. The key technique is to add a (nonenumerable and read-only in ES5) property to object o.
function objectId(o){
var prop = "|**objectid**|"; // private property name for storing ids
if (!o.hasOwnProperty(prop)) // if the object has no id
o[prop] = Set._v2s.next++; // assign it the next available
return o[prop];
};
Set._v2s.next = 100; // start assigning objectids at this value.
var my_set = new Set("o","pojo");
alert(my_set.size);
Well, By running your code, I found one small mistake alert(my_set.size);
In the definition,
Set.prototype.size = function(){
return this.n;
};
So, size is a method. So you should call it as method to return the correct result such as
var result = my_set.size()
Apparently you cannot assign new properties to primitives in JS. How about this:
var objectId = (function() {
var next = 100; // start assigning objectids at this value.
var ids = {};
return function(o){
if (!ids.hasOwnProperty(o)) // if the object has no id
ids[o] = next++; // assign it the next available
return ids[o];
};
}) ();
Related
I need to initialize some kind of a prototype on already existing jQuery elements collection. The main problem is that the prototype should be accessible only inside of that collection and on elements produced by built-in jQuery functions like .find() on that collection or on some children objects inside of that collection, for example:
var $a = $('a');
$a.__proto__.foo/*some magic over here*/ = function(){ alert('foo!'); };
$a.foo(); //should show alert('foo!')
$a.find('b').foo(); //should produce the same action
$('a').foo(); //should produce an error (method not found)
If using $a.__proto__ like in example above, the jQuery.prototype is accessed, so all the new elements in outside of that jQuery-collection (for example, $('a')) are granting an access to .foo() method. That behaviour is unacceptable on a problem statement.
Is that actually possible?
Okay, here's the thing, I have a rather complex ES6 solution, so I won't be able to explain it in great depth, but if you have some particular questions, go ahead.
var wrap = (function wrapper() {
var store = {};
function wrap(fn) {
return new Proxy(fn, {
apply(target, thisArg, argumentsList) {
var result = Reflect.apply(target, thisArg, argumentsList);
// `jQuery(...)` returns a "rich" object that always contain `.length`
if (result.length > 0) {
result = new Proxy(result, {
get(target, propertyKey, receiver) {
var value = Reflect.get(target, propertyKey, receiver);
if (Object.keys(store).includes(propertyKey)) {
value = store[propertyKey];
}
return value;
},
set(target, propertyKey, value, receiver) {
// TODO: use `Reflect.set(), somehow`
// return Reflect.set(store, propertyKey, value, receiver);
return (store[propertyKey] = value);
},
});
}
return result;
}
});
}
return wrap;
})();
var $ = wrap(jQuery);
$.prototype.find = wrap(jQuery.prototype.find); // TODO: implement recursively in `wrap()`
var x = $('div');
var xx = x.find('div');
var xxx = x.find('divvv');
xx.foo = 123;
console.log(x.foo); // 123
console.log(xx.foo); // 123
console.log(xxx.foo); // undefined
I'm trying to backwards engineer the array methods push, pull, shift, unshift, but I can't seem to figure out how to a) construct it b) call it. How could I do this? Here are the conditions:
returns an empty array object. this object should have the
following methods: push(val) adds val to the end of the array
pop() removes a value from the end and returns it unshift(val) adds
val to the beginning of the array shift() removes a value from the
beginning and returns it the goal of this problem is to reverse
engineer what array methods are actually doing and return an object
that has those methods
Here is what I initially thought it should look like.
function createArray() {
//CODE HERE
this.push = function Push(value){
if(index >= 0){
Mainarray[index++]=value;}
};
this.pop = function (){
if(index >= 0){
index--;
return Mainarray[index];
}
else{
// display message of Empty Array
console.log('Error: Item is not array');
}
};
this.unshift = function(){return ;};
}
You could use prototypes — like this:
function YourArray() {
this.arr = [];
this.index = 0;
}
YourArray.prototype.push = function( value ) {
this.arr[ this.index++ ] = value;
return this;
}
var arr = new YourArray();
arr.push('foo');
function NewArray() {
this.array = [];
}; /* Class */
NewArray.prototype.push = function(data) {
this.array.push(data);
} /* Method */
/* You should use prototypes, because all methods will became common, and if you are created methods like this.pop = function (){} then any instance will copy this functions */
var n = new NewArray();
n.push(2);
console.log(n);
Advantages of using prototype, vs defining methods straight in the constructor?
You can recreate the push method by assigning you array at position the length of the same array a value.
This is the prototype for the push:
Array.prototype.push = function(element) {
this[this.length] = element;
};
and this is for the pop method:
Array.prototype.pop = function() {
var key = this.stack.pop();
var prop = this.object[key];
delete this.object[key];
return prop;
};
You can make your own methods by changing the prototype names.
push to mypush or sthing
Example for your push function createArray:
this.push = function pushValue(value) {
this.arr[this.arr.length] = value;
};
I used native arrays methods as values assigned to keys in the returned object. The trick is to declare an array inside the object and use it as a reference. It should pass the checks you`re looking for.
function createArray() {
//CODE HERE
return {
arr: [],
push: function (val) {this.arr.push(val)},
pop: function() {return this.arr.pop()},
unshift: function (val) {return this.arr.unshift(val)},
shift: function() {return this.arr.shift()}
}
}
I am trying you get a better understanding of JavaScript, especially the prototype functionality. I am having trouble with this case:
I am trying to define a function someObject with a type function so that it will behave like the following:
var myTestObject = someObject();
If I call:
myTestObject() ===> "The object is initailType"
and then when this is called
myTestObject.type() ===> "InitialType"
Then if I make this call
myTestObject.type("newtype")
myTestObject.type() ===> "newType"
A call to
myTestObject() ===> "The Object is newType".
I have tried both this How does JavaScript .prototype work?
and this How do you create a method for a custom object in JavaScript?
,but I am getting several different errors depending on how it is implemented, mostly this though (Uncaught TypeError: Object myTestObject has no method 'type'). I feel like I am making this harder then it should be.
edit: more code.
function box(){
var _current = "initialType"
Object.defineProperty(this, "current", {
get: function(){return _current;},
set: function(value){
if(arguments.length === 1){
_current = value;
} }
})
return "The Object is " + this.type(this.current)
}
box.prototype.type = function(newValue){
var type = null;
if(arguments.length == 0){
type = "initialType";
}else {
type = newValue
}
return type
}
I would use something like this:
function Box(){}
Box.prototype.type = "initialType";
Box.prototype.toString = function() {
return "The Object is " + this.type + ".";
};
And use it like this:
var b = new Box();
b.type; // "initialType"
b + ''; // "The Object is initialType."
b.type = 'otherType'; // "otherType"
b.type; // "otherType"
b + ''; // "The Object is otherType."
This does what you've asked, but I don't understand what you want to do with the prototype, so this code doesn't use that. For example, the sample code doesn't use new, so the return value of someObject won't use its prototype.
function someObject()
{
var currentType = "initailType";
var formatter = function() {
return "The object is " + currentType;
};
formatter.type = function(value) {
if (arguments.length == 0) {
return currentType;
} else {
currentType = value;
}
};
return formatter;
}
var myTestObject = someObject();
myTestObject(); // => "The object is initailType"
myTestObject.type(); // => "initialType"
myTestObject.type("newType");
myTestObject.type(); // => "newType"
myTestObject(); // => "The object is newType".
see demo
Edit: example using prototype and new.
function Box() { // class name starts with a capital letter
this._type = "initialType"; // set up default values in constructor function
} // no "return" in constructor function, using "new" handles that
Box.prototype.type = function(value) { // adding method to the prototype
if (arguments.length == 0) { // magic arguments local variable...
return this._type; // initially returns the value set in the constructor
} else {
this._type = value; // update the stored value
}
};
Box.prototype.format = function() // another method on the box, rather than a return from the constructor
{
return "The object is " + this.type(); // could use this._type instead
};
var box = new Box(); // instance variable with lowercase name
console.log(box.type()); // read the default value
console.log(box.format()); // print the message with the initial value of type
box.type("another type"); // set the type property, no return value
console.log(box.format()); // print the new message
I have the following javascript code:
function testClass() {
this.SaveValue = function (value) {
var isInstance = value instanceof TestEnum;
if (!isInstance) {
return;
}
}
}
TestEnum = {
VALUE_0: 0,
VALUE_1: 1,
VALUE_2: 2
}
I create an instance of this object in the following way:
$(function () {
var a = new testClass();
a.SaveValue(TestEnum.VALUE_1);
});
All I'd like to do is test that the value passed to the SaveValue function is actually the type of TestEnum. However, when I run this code I get the following error: Uncaught TypeError: Expecting a function in instanceof check, but got 1
Am I going about this the right way? I tried typeof but it only returns number which is not particularly useful to me.
You could create the values as instances of the "class":
function TestEnum(value) {
this._value = value;
}
TestEnum.prototype.valueOf = function() {
return this._value;
}
TestEnum.prototype.toString = function() {
return 'TestEnum_' + this._value;
}
TestEnum.VALUE_0 = new TestEnum(0);
TestEnum.VALUE_1 = new TestEnum(1);
The following would work then:
TestEnum.VALUE_0 instanceof TestEnum
But it also means you'd have to explicitly access the numerical value of one value with .valueOf. In some cases JS will do this automatically for you (like in 5 + TestEnum.VALUE_1). Overriding toString so that you can use a value as property might also be necessary.
It really depends on your use case whether this is a viable solution.
Alternatively, if just want to test whether a value is part of the enum, you can have an additional property which holds all possible values:
TestEnum.values = {0: true, 1: true, ...};
And then test it with
value in TestEnum.values
// or more reliable (fails for inherited `Object` properties)
TestEnum.values.hasOwnProperty(value);
You could even automate this:
function collectValues(obj) {
var values = {}; // or Object.create(null) if available
for (var prop in obj) {
if (obj.hasOwnProperty(prop)) {
values[obj[prop]] = true;
}
}
return values;
}
TestEnum.values = collectValues(TestEnum);
This will only reliably work for primitive values though and won't distinguish between the string "1" and the number 1.
You are passing a number to the function in
a.SaveValue(TestEnum.VALUE_1);
Since TestEnum is simply an Object, and you are referencing a number property on that object, you're calling your function with a number. You should instead create a TestEnumValue object and use that for your Object's properties:
JSFiddle link for below
function testClass() {
this.SaveValue = function (value) {
var isInstance = value instanceof TestEnumValue;
if (!isInstance) {
return;
}
}
}
TestEnumValue = function(arg) {
arg = arg ? arg : 0; // sensible default
this.key = 'VALUE_' + arg;
this.val = arg;
}
Level = {
NumSpiders : new TestEnumValue(0),
NumCreepers: new TestEnumValue(1),
NumZombies : new TestEnumValue(2),
NumChickens: new TestEnumValue // uses default enum value
};
$(function() {
var a = new testClass();
a.SaveValue(Level.NumSpiders);
$('#hi').text(Level.NumSpiders.key);
});
Playing around with this, I noticed that you can leverage the fact that an enum compiles into an object that binds the values both ways combined with a hasOwnProperty check.
export enum TEST_ENUM{
ZERO, // 0
ONE, // 1
TWO, // 2
}
let a = 1;
let b = TEST_ENUM.TWO // 2
let c = 5 // incorrect value
TEST_ENUM.hasOwnProperty(a); // TRUE
TEST_ENUM.hasOwnProperty(b); // TRUE
TEST_ENUM.hasOwnProperty(c); // FALSE
This comes with a few caveats though;
// An object's keys are always strings...
// Although this shouldn't not matter usually (e.g. parsed user input)
TEST_ENUM.hasOwnProperty("2"); // TRUE
// And the enum is bound two-way so:
let input = "TWO";
if (TEST_ENUM.hasOwnProperty(input) { // TRUE
let result = input // "TWO"
// result is now the enum's value, instead of the key.
result = TEST_ENUM[input]; // this would be the correct assignment
};
Of course you can fix both of these with a typeof check, in case of a string assign it TEST_ENUM[mystring].
Note that my intellisense didn't autocomplete the hasOwnProperty function on an enum, but it doesn't complain about it either, and it's available on all browsers.
Edit
Here's an example of how you could do it.
function TestEnum(val) {
this.vals = this.vals || [];
if (this.vals.indexOf(val) == -1) console.log('nope: ' + val);
else console.log('ok: ' + val);
}
(function() {
var vals = {
VALUE_0: 0,
VALUE_1: 1,
VALUE_2: 2
};
TestEnum.prototype.vals = [];
for (var key in vals) {
TestEnum[key] = vals[key];
TestEnum.prototype.vals.push(vals[key]);
}
})();
Now new TestEnum(TestEnum.VALUE_0); is OK, but if you try, say, new TestEnum(3), then it throws an exception.
This is a bit backwards -- x instanceof y means that x has been created as x = new y(). Since TestEnum isn't even a function, you can't create an instance of it, so this isn't going to work.
What you could do is maybe something like this:
function MyEnum(enumVal) { this.val = enumVal; }
a.SaveValue( new MyEnum(TestEnum.VALUE_1) );
Then check using isInstance = value instanceof MyEnum.
Do JavaScript objects/variables have some sort of unique identifier? Like Ruby has object_id. I don't mean the DOM id attribute, but rather some sort of memory address of some kind.
If you want to lookup/associate an object with a unique identifier without modifying the underlying object, you can use a WeakMap:
// Note that object must be an object or array,
// NOT a primitive value like string, number, etc.
var objIdMap=new WeakMap, objectCount = 0;
function objectId(object){
if (!objIdMap.has(object)) objIdMap.set(object,++objectCount);
return objIdMap.get(object);
}
var o1={}, o2={}, o3={a:1}, o4={a:1};
console.log( objectId(o1) ) // 1
console.log( objectId(o2) ) // 2
console.log( objectId(o1) ) // 1
console.log( objectId(o3) ) // 3
console.log( objectId(o4) ) // 4
console.log( objectId(o3) ) // 3
Using a WeakMap instead of Map ensures that the objects can still be garbage-collected.
No, objects don't have a built in identifier, though you can add one by modifying the object prototype. Here's an example of how you might do that:
(function() {
var id = 0;
function generateId() { return id++; };
Object.prototype.id = function() {
var newId = generateId();
this.id = function() { return newId; };
return newId;
};
})();
That said, in general modifying the object prototype is considered very bad practice. I would instead recommend that you manually assign an id to objects as needed or use a touch function as others have suggested.
Actually, you don't need to modify the object prototype. The following should work to 'obtain' unique ids for any object, efficiently enough.
var __next_objid=1;
function objectId(obj) {
if (obj==null) return null;
if (obj.__obj_id==null) obj.__obj_id=__next_objid++;
return obj.__obj_id;
}
I've just come across this, and thought I'd add my thoughts. As others have suggested, I'd recommend manually adding IDs, but if you really want something close to what you've described, you could use this:
var objectId = (function () {
var allObjects = [];
var f = function(obj) {
if (allObjects.indexOf(obj) === -1) {
allObjects.push(obj);
}
return allObjects.indexOf(obj);
}
f.clear = function() {
allObjects = [];
};
return f;
})();
You can get any object's ID by calling objectId(obj). Then if you want the id to be a property of the object, you can either extend the prototype:
Object.prototype.id = function () {
return objectId(this);
}
or you can manually add an ID to each object by adding a similar function as a method.
The major caveat is that this will prevent the garbage collector from destroying objects when they drop out of scope... they will never drop out of the scope of the allObjects array, so you might find memory leaks are an issue. If your set on using this method, you should do so for debugging purpose only. When needed, you can do objectId.clear() to clear the allObjects and let the GC do its job (but from that point the object ids will all be reset).
const log = console.log;
function* generateId() {
for(let i = 0; ; ++i) {
yield i;
}
}
const idGenerator = generateId();
const ObjectWithId = new Proxy(Object, {
construct(target, args) {
const instance = Reflect.construct(target, args);
instance['id'] = idGenerator.next().value;
return instance;
}
})
const myObject = new ObjectWithId({
name: '##NativeObject'
});
log(myObject.id);