In Javascript an array [] can have properties too, because it's an object. However if you send this object to server via ajax/socket.io, it's array content stays, but its properties get lost.
For example:
var arr = ['john','peter'];
arr.a = 1;
console.log(arr.a); // 1
After sending this object via ajax, what the server sees:
console.log(arr.a); // undefined
Does anyone know why? I'm using Node.JS for server btw, thanks!
As others have mentioned, JSON.stringify() doesn't serialize array properties, even if they aren't "inherited" from proto.
If you're using jquery, a quick fix is to just use $.extend({},arr)
http://jsfiddle.net/15vn4shu/1/
If not, you can write a function to convert to an object easily:
function convertArrToObj(arr){
//assuming typeof arr === 'object', put in guards if needed
var obj = {};
for (var key in arr){
if (arr.hasOwnProperty(key))
obj[key]=arr[key];
}
return obj;
}
Personally, I'd just make an object with a property being the value of this array, add add to this parent object whatever you need. I'm not sure how feasible this is in your use case though (I'd have to see the code).
var toSend = {data:arr, customProp:'value'};
Easy, supported by all versions of js, doesn't require a framework, and no O(n) run through to do the conversion (arr is a pointer here... Which may be a gotcha, but it doesn't seem like it for your case).
Related
I am using Map
because I want to store an object as a key.
My question is - can I access a map the same way I would access a plain object?
For example:
let m = new Map();
let obj = {foo:'bar'};
m[obj] = 'baz';
console.log(m[obj]);
is this supposed to work correctly as is, or do I need to use the get/set methods of a Map?
The reason I ask is because if I need to use get/set it forces to me to carefully refactor a lot of code.
Here is a real life example of code that may need to be refactored:
// before (broker.wsLock was plain object)
function addWsLockKey(broker, ws, key) {
let v;
if (!( v = broker.wsLock[ws])) {
v = broker.wsLock[ws] = [];
}
if (v.indexOf(key) < 0) {
v.push(key);
}
}
// after (broker.wsLock is a Map instance)
function addWsLockKey(broker, ws, key) {
let v;
if (!( v = broker.wsLock.get(ws))) {
v = [];
broker.wsLock.set(ws, v);
}
if (v.indexOf(key) < 0) {
v.push(key);
}
}
is there some way to set v on the same line as the set() call?
If you want access to the actual values of the Map object, then you have to use .get() and .set() or various iterators.
var m = new Map();
m.set("test", "foo");
console.log(m.get("test")); // "foo"
Regular property access on a Map such as:
m["test"] = "foo"
just sets a regular property on the object - it does not affect the actual map data structure.
I imagine it was done this way so that you can access the Map object properties separately from the members of the Map data structure and the two shall not be confused with one another.
In addition, regular properties will only accept a string as the property name so you can't use a regular property to index an object. But, map objects have that capability when using .set() and .get().
You asked for a "definitive" answer to this. In the ES6 specification, you can look at what .set() does and see that it operates on the [[MapData]] internal slot which is certainly different than the properties of an object. And, likewise, there is no where in that spec where it says that using normal property access would access the internal object [[MapData]]. So, you'll have to just see that normal property access is describe for an Object. A Map is an Object and there's nothing in the Map specification that says that normal property access should act any different than it does for any other object. In fact, it has to act the same for all the methods on the Map object or they wouldn't work if you happened to put an item in the Map with the same key as a method name. So, you're proof consists of this:
A simple test will show you that property access does not put anything in the Map itself, only a regular property.
The spec describes a Map as an object.
The spec describes how .get() and .set() operate on the internal slot [[MapData]].
There's nothing in the spec that says property access on a Map object should work any different than it always does.
If property access did access the MapData, then you would not be able to access methods if you happened to put a key in the Map that conflicted with a method name - that would be a mess if that was the case.
In my serialization code, I stumbled across a a stinky issue - as I loop through generic object properties, it also serializes array indexes, which is really not the plan - I serialize this data later on without saving the indexes in the stream.
[1].hasOwnProperty("0") // true
So my question is, why are array indexes considered own properties by the hasOwnProperty method? Is there even a way to tell property from array offset? A generic way that also works for TypedArray, HTMLElementCollection and whatever else?
Of course, this can be done, but it stinks:
for(var i in this) {
if(this.hasOwnProperty(i) &&
// If object is an array, we ignore the number offsets as they're not meant to be object properties
(typeof this.length!="number" || !(i<this.length) || i.length==0)) {
And yeah, the i.length==0 is there because you can actually do this:
var obj = {};
obj[""] = "something";
console.log(obj);
Yeah, you're welcome, enjoy your nightmares.
Arrays are objects, just slightly specialised. And as you have discovered, the indexes of an array are just properties called 0, 1, 2 etc.
On a really simple level, the length property just finds the highest numeric property and adds one.
You could make a slightly simpler way of filtering the keys, along the lines of
for (key in obj) {
if (isNaN(+key) && obj.hasOwnProperty(key)) {
doSomething()
}
}
Depends if you want to include the numeric properties of objects. It would be perfectly valid to do a = {'0': 'value'}, which is for the purpose of this exercise the same as b = ['value']. Although b has a length property and a does not, also b has all the other functions that come from being an array.
In my script there is a need to create a hash table, and I searched in google for this. Most of the folks are recommending JavaScript object for this purpose. The The problem is some of the keys in the hash table have a "." in them. I am able to create these keys easily with the associative arrays.
I don't understand why associative arrays are bad. The first thing that is mentioned on the sites that I looked at is the length property.
I am coming from the Perl background, where I used hashes. Most common uses were to get the value from a key, check if a key exists, delete a key-value pair, and add a key-value pair. If these are my common uses, can I safely use an associative array?
In JavaScript, objects are associative arrays...there aren't separate concepts for them. You are also able to safely use '.' in a key name, but you can only access the value using the bracket notation:
var foo = {}
foo['bar'] = 'test';
foo['baz.bin'] = 'value';
alert(foo.bar); // Shows 'test'
alert(foo['baz.bin']); // Shows 'value'
If you're using them already and they work, you're safe.
In a JavaScript, an object and array are pretty much the same thing, with an array having a bit of magical functionality (autoupdating the length property and such) and prototype methods suitable for arrays. It is also much easier to construct an object than using an associative array:
var obj = {"my.key": "myValue"};
vs.
var obj = [];
obj["my.key"] = "myValue";
Therefore never use the array object for this, but just the regular object.
Some functionality:
var obj = {}; // Initialized empty object
Delete a key-value pair:
delete obj[key];
Check if a key exists:
key in obj;
Get the key value:
obj[key];
Add a key-value pair:
obj[key] = value;
Because there is no such thing as built-in associative arrays in JavaScript. That's why it's bad.
In fact, when you use something like:
theArray["a"] = "Hello, World!";
It simply creates a property called "a" and set its value to "Hello, World!". This is why the length is always 0, and why the output of alert(theArray) is empty.
Actually, an "associative array" is pretty much the same as an "array-like object" in ECMAScript. Even arrays are objects in ECMAScript, just with the exception to have numeric keys (which are still strings in the background) and a .length property, along with some inherited methods from Array.prototype.
So, a Perl hash and an ECMAScript object behave similarly. You might not know that you can access object properties not only via a dot, but also with brackets and strings, like
var myObj = { foo: 42 };
myObj.foo; // 42
myObj['foo']; // 42
Knowing that, you can also use keys with .
var myObj = { };
myObj['hello.foo.world'] = 42;
Of course, you can access that key only with the bracket notation.
You can use . in key names on JavaScript objects (AKA associative arrays) if you'd like; they're accepted without issue. The minor drawback is you can't use shortcut notations with the dotted keys, e.g.
var x = {};
x['hello'] = 'there';
alert(x.hello);
is perfectly acceptable and will pop up an alert with 'there' in it. But if you use a dotted name:
var x = {};
x['this.is'] = 'sparta';
alert(x.this.is);
will fail, as JavaScript will look for an attribute named this in the x object, which does not exist. There is only the this.is attribute.
There isn't an associative array. It's just an object.
foo.bar; // Equivalent to...
foo["bar"]; // Looks like associative array.
For the sake of convenience of using data, there should be no difference between an object and an array. You can think of it as an object or you can think of it as an associative array. At the end, you can just think of everything as data.
For PHP, [ ] accepts 0, 1, or more items(array), and it is called an associative array. It is JSON in PHP's coat:
$data = ["message"=>[ "id"=>405, "description"=>"Method not allowed.", "detail"=>[]], "object" => []];
For JavaScript, { } accepts 0, 1, or more items(array), and it is called an object. This data format is JSON:
data = {"message": { "id":405, "description":"Method not allowed.", "detail" : {}}, "object" : {}};
I just call them data. The simplest way to describe data is JSON, or its variants.
var obj = {
destroy: function(){this = null;}
};
obj.destroy();
This works in Chrome, however firefox is throwing an error referencing this for some reason. Is there a better way to kill this object within a method?
Error:
invalid assignment left-hand side
[Break On This Error] destroy: function(){this = null;}
Not sure why Chrome allows for it but you can't assign a value to this. You can reference this, but you can't assign a value to it.
If you have some array destruction you want to perform you can reference this.myArrayName within your destroy method and free up whatever you're trying to release, but you can't just assign null to this to destroy an instance.
I suppose you could try something like this:
var foo = {
// will nullify all properties/methods of foo on dispose
dispose: function () { for (var key in this) this[key] = null; }
}
foo.dispose();
Pretty much as close as you can get to legally nullifying "this"...
Happy coding.
B
Call me old fashion, but:
foo = null;
I'm not sure why you're making this difficult. Javascript is a garbage collected language. All you have to do to allow something to be freed is to make sure there are no more references to it anywhere.
So, if you start with:
var obj = {
data: "foo";
};
and now you want to get rid or "free" that object, all you have to do is clear the reference to it with:
obj = null;
Since there are no longer any references in your code to that data structure that you originally defined and assigned to obj, the garbage collector will free it.
An object cannot destroy itself (because other things may have references to it). You allow it to be freed by removing all references to it. An object can clear out it's own references to other things, though that is generally not required as removing all references to the object itself will also take care of the references it holds (with the exception of some bugs with circular references between JS and the DOM in certain older browsers - particular IE).
One time when you might explicitly "delete" something is if you have a property on an object that you wish to remove. So, if you have:
var obj = {
data: "foo",
count: 4
};
And you wish to remove the "data" property, you can do that with this:
delete obj.data;
of if the property/key was assigned programmatically via a variable like this:
var key = "xxx";
obj[key] = "foo";
you can remove that key with:
delete obj[key];
I have a Javascript object that I'm trying to use as a "hashmap". The keys are always strings, so I don't think I need anything as sophisticated as what's described in this SO question. (I also don't expect the number of keys to go above about 10 so I'm not particularly concerned with lookups being O(n) vs. O(log n) etc.)
The only functionality I want that built-in Javascript objects don't seem to have, is a quick way to figure out the number of key/value pairs in the object, like what Java's Map.size returns. Of course, you could just do something like:
function getObjectSize(myObject) {
var count=0
for (var key in myObject)
count++
return count
}
but that seems kind of hacky and roundabout. Is there a "right way" to get the number of fields in the object?
There is an easier way spec'd in ECMAScript 5.
Object.keys(..) returns an array of all keys defined on the object. Length can be called on that. Try in Chrome:
Object.keys({a: 1, b: 2}).length; // 2
Note that all objects are basically key/value pairs in JavaScript, and they are also very extensible. You could extend the Object.prototype with a size method and get the count there. However, a much better solution is to create a HashMap type interface or use one of the many existing implementations out there, and define size on it. Here's one tiny implementation:
function HashMap() {}
HashMap.prototype.put = function(key, value) {
this[key] = value;
};
HashMap.prototype.get = function(key) {
if(typeof this[key] == 'undefined') {
throw new ReferenceError("key is undefined");
}
return this[key];
};
HashMap.prototype.size = function() {
var count = 0;
for(var prop in this) {
// hasOwnProperty check is important because
// we don't want to count properties on the prototype chain
// such as "get", "put", "size", or others.
if(this.hasOwnProperty(prop) {
count++;
}
}
return count;
};
Use as (example):
var map = new HashMap();
map.put(someKey, someValue);
map.size();
A correction: you need to check myObject.hasOwnProperty(key) in each iteration, because there're can be inherited attributes. For example, if you do this before loop Object.prototype.test = 'test', test will aslo be counted.
And talking about your question: you can just define a helper function, if speed doesn't matter. After all, we define helpers for trim function and other simple things. A lot of javascript is "kind of hacky and roundabout" :)
update
Failure example, as requested.
Object.prototype.test = 'test';
var x = {};
x['a'] = 1;
x['b'] = 2;
The count returned will be 3.
you could also just do myObject.length (in arrays)
nevermind, see this: JavaScript object size
That's all you can do. Clearly, JavaScript objects are not designed for this. And this will only give you the number of Enumerable properties. Try getObjectSize(Math).