I was looking into a function that creates a copy of provided object. I understand mostly what's happening except for the line that involves this keyword. I do understand that the original design of the this keyword was meant to point to an instance of an object in class definitions if we go back to the origins of the this keyword that was borrowed from C++. But JavaScript decided to use this keyword to provide one extra feature, carrying a link to execution context. In the following example I am trying to understand why are we using this keyword. If you have any thoughts, I would really appreciate it.
function clone(obj) {
const replace = {};
let idx = 0;
const undefCache = [];
const replacer = (key, value) => {
let result;
if (value === undefined) {
result = '__undefined__';
} else if (typeof value === 'symbol' || typeof value === 'function') {
const keyIdx = `__replaced__${idx}`;
idx += 1;
replace[keyIdx] = [this, key]; // I understand mostly what's happening except for the line
result = keyIdx;
} else {
result = value;
}
return result;
};
function reviver(key, value) {
let result;
if (value === '__undefined__') {
undefCache.push([this, key]);// I understand mostly what's happening except for the line
} else if (replace[value] !== undefined) {
result = replace[value][0][key];
} else {
result = value;
}
return result;
}
const json = JSON.stringify(obj, replacer);
console.log(json);
const newObject = JSON.parse(json, reviver);
undefCache.forEach(el => {
const [o, key] = el;
o[key] = undefined;
});
return newObject;
}
const source = {
a: 2,
b: '2',
c: false,
g: [
{ a: { j: undefined }, func: () => {} },
{ a: 2, b: '2', c: false, g: [{ a: { j: undefined }, func: () => {} }] }
]
};
const targetOne = clone(source);
console.log(targetOne);
It's used to handle nested objects when doing serialization/deserialization with JSON.parse/stringify on special values.
Within the replacer/reviver functions, the this context is the current object that the serializer (stringify) or deserializer (parse) is working on.
For example, for the object below:
myObject = {
"foo": {
"bar": function () {}
},
"bar": "Different bar"
}
When it's processing the item myObject["foo"]["bar"], this inside the replacer will be a reference to myObject["foo"] with key = "bar" and value = function () {}". This is useful because without the reference, we wouldn't know whether we were processing myObject["bar"] or myObject["foo"]["bar"].
Thus when it is saved into the array, it really just saved pair = [myObject["foo"], "bar"]. Later when it's recovered, for each of these pairs, it can just do pair[0][pair[1]] to recover myObject["foo"]["bar"].
This works similarly with the reviver and undefined. Here the problem is that the reviver cannot return undefined and have the value set to undefined, so instead the code snippet remembers which keys are like this and post-processes the copy of the object to set them properly.
Ref: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#The_replacer_parameter
Related
Is there a way in JS to over-ride the default behavior of an object when it is de-structured?
// Normally destructing lifts properties from an object
const foo = {
a: 1,
b: 2,
};
const { a, b } = foo; // a = 1, b = 2
// I would like to have a method return the properties to be
// destructured
const bar = {
toObject: () => {
return { a, b };
},
};
const { a, b } = bar; // a = undefiner, b = undefined
I know that I could simply use const { a, b } = bar.toObject(); but that requires the consumer of the object to know how it's internals work and breaks the principle of least astonishment.
The closest thing I can think of to what I want is the toJSON magic method.
Nope. The specification requires the right hand side to resolve to a value that can be converted to an object via ToObject, which simply returns the object itself if it is passed one (i.e. no special method on the object is called to convert it to something else).
If you'd use array destructuring, that would work:
const [a, b] = {
*[Symbol.iterator]() {
yield "some"; yield "stuff";
}
};
You can make your toObject work as intended by decorating the target with a Proxy that intercepts ownKeys and get to fake an object for destructuring:
let withToObject = obj => new Proxy(obj, {
ownKeys(o) {
return Object.keys(o.toObject())
},
get(o, prop) {
return o.toObject()[prop]
}
});
let bar = withToObject({
aa: 11,
bb: 22,
cc: 33,
toObject() {
return {
a: this.aa,
b: this.bb
};
}
});
const {a, b} = bar;
console.log(a, b)
Of course, this affects not only destructuring, but also any other interaction with the object, like serialization, so you have to take measures to make these work too. For example, to support JSON, patch get like this:
get(o, prop) {
if (prop === 'toJSON')
return () => o; // or o.toObject(), whatever fits better
return o.toObject()[prop]
I've got a JavaScript object definition which contains a circular reference: it has a property that references the parent object.
It also has functions that I don't want to be passed through to the server. How would I serialize and deserialize these objects?
I've read that the best method to do this is to use Douglas Crockford's stringify. However, I'm getting the following error in Chrome:
TypeError: Converting circular structure to JSON
The code:
function finger(xid, xparent){
this.id = xid;
this.xparent;
//other attributes
}
function arm(xid, xparent){
this.id = xid;
this.parent = xparent;
this.fingers = [];
//other attributes
this.moveArm = function() {
//moveArm function details - not included in this testcase
alert("moveArm Executed");
}
}
function person(xid, xparent, xname){
this.id = xid;
this.parent = xparent;
this.name = xname
this.arms = []
this.createArms = function () {
this.arms[this.arms.length] = new arm(this.id, this);
}
}
function group(xid, xparent){
this.id = xid;
this.parent = xparent;
this.people = [];
that = this;
this.createPerson = function () {
this.people[this.people.length] = new person(this.people.length, this, "someName");
//other commands
}
this.saveGroup = function () {
alert(JSON.stringify(that.people));
}
}
This is a test case that I created for this question. There are errors within this code but essentially I have objects within objects, and a reference passed to each object to show what the parent object is when the object is created. Each object also contains functions, which I don't want stringified. I just want the properties such as the Person.Name.
How do I serialize before sending to the server and deserialize it assuming that the same JSON is passed back?
Circular structure error occurs when you have a property of the object which is the object itself directly (a -> a) or indirectly (a -> b -> a).
To avoid the error message, tell JSON.stringify what to do when it encounters a circular reference.
For example, if you have a person pointing to another person ("parent"), which may (or may not) point to the original person, do the following:
JSON.stringify( that.person, function( key, value) {
if( key == 'parent') { return value.id;}
else {return value;}
})
The second parameter to stringify is a filter function. Here it simply converts the referred object to its ID, but you are free to do whatever you like to break the circular reference.
You can test the above code with the following:
function Person( params) {
this.id = params['id'];
this.name = params['name'];
this.father = null;
this.fingers = [];
// etc.
}
var me = new Person({ id: 1, name: 'Luke'});
var him = new Person( { id:2, name: 'Darth Vader'});
me.father = him;
JSON.stringify(me); // so far so good
him.father = me; // time travel assumed :-)
JSON.stringify(me); // "TypeError: Converting circular structure to JSON"
// But this should do the job:
JSON.stringify(me, function( key, value) {
if(key == 'father') {
return value.id;
} else {
return value;
};
});
BTW, I'd choose a different attribute name to "parent" since it is a reserved word in many languages (and in DOM). This tends to cause confusion down the road...
No-lib
Use below replacer to generate json with string references (similar to json-path) to duplicate/circular referenced objects
let s = JSON.stringify(obj, refReplacer());
function refReplacer() {
let m = new Map(), v= new Map(), init = null;
return function(field, value) {
let p= m.get(this) + (Array.isArray(this) ? `[${field}]` : '.' + field);
let isComplex= value===Object(value)
if (isComplex) m.set(value, p);
let pp = v.get(value)||'';
let path = p.replace(/undefined\.\.?/,'');
let val = pp ? `#REF:${pp[0]=='[' ? '$':'$.'}${pp}` : value;
!init ? (init=value) : (val===init ? val="#REF:$" : 0);
if(!pp && isComplex) v.set(value, path);
return val;
}
}
// ---------------
// TEST
// ---------------
// gen obj with duplicate references
let a = { a1: 1, a2: 2 };
let b = { b1: 3, b2: "4" };
let obj = { o1: { o2: a }, b, a }; // duplicate reference
a.a3 = [1,2,b]; // circular reference
b.b3 = a; // circular reference
let s = JSON.stringify(obj, refReplacer(), 4);
console.log(s);
And following parser function to regenerate object from such "ref-json"
function parseRefJSON(json) {
let objToPath = new Map();
let pathToObj = new Map();
let o = JSON.parse(json);
let traverse = (parent, field) => {
let obj = parent;
let path = '#REF:$';
if (field !== undefined) {
obj = parent[field];
path = objToPath.get(parent) + (Array.isArray(parent) ? `[${field}]` : `${field?'.'+field:''}`);
}
objToPath.set(obj, path);
pathToObj.set(path, obj);
let ref = pathToObj.get(obj);
if (ref) parent[field] = ref;
for (let f in obj) if (obj === Object(obj)) traverse(obj, f);
}
traverse(o);
return o;
}
// ------------
// TEST
// ------------
let s = `{
"o1": {
"o2": {
"a1": 1,
"a2": 2,
"a3": [
1,
2,
{
"b1": 3,
"b2": "4",
"b3": "#REF:$.o1.o2"
}
]
}
},
"b": "#REF:$.o1.o2.a3[2]",
"a": "#REF:$.o1.o2"
}`;
console.log('Open Chrome console to see nested fields:');
let obj = parseRefJSON(s);
console.log(obj);
It appears that dojo can represent circular references in JSON in the form : {"id":"1","me":{"$ref":"1"}}
Here is an example:
http://jsfiddle.net/dumeG/
require(["dojox/json/ref"], function(){
var me = {
name:"Kris",
father:{name:"Bill"},
mother:{name:"Karen"}
};
me.father.wife = me.mother;
var jsonMe = dojox.json.ref.toJson(me); // serialize me
alert(jsonMe);
});
Produces:
{
"name":"Kris",
"father":{
"name":"Bill",
"wife":{
"name":"Karen"
}
},
"mother":{
"$ref":"#father.wife"
}
}
Note: You can also de-serialize these circular referenced objects using the dojox.json.ref.fromJson method.
Other Resources:
How to serialize DOM node to JSON even if there are circular references?
JSON.stringify can't represent circular references
I found two suitable modules to handle circular references in JSON.
CircularJSON https://github.com/WebReflection/circular-json whose output can be used as input to .parse(). It also works in Browsers & Node.js Also see: http://webreflection.blogspot.com.au/2013/03/solving-cycles-recursions-and-circulars.html
Isaacs json-stringify-safe https://github.com/isaacs/json-stringify-safe which maybe more readable but can't be used for .parse and is only available for Node.js
Either of these should meet your needs.
Happened upon this thread because I needed to log complex objects to a page, since remote debugging wasn't possible in my particular situation. Found Douglas Crockford's (inceptor of JSON) own cycle.js, which annotates circular references as strings such that they can be reconnected after parsing. The de-cycled deep copy is safe to pass through JSON.stringify. Enjoy!
https://github.com/douglascrockford/JSON-js
cycle.js: This file contains two functions, JSON.decycle and
JSON.retrocycle, which make it possible to encode cyclical structures
and dags in JSON, and to then recover them. This is a capability that
is not provided by ES5. JSONPath is used to represent the links.
I used the following to eliminate the circular references:
JS.dropClasses = function(o) {
for (var p in o) {
if (o[p] instanceof jQuery || o[p] instanceof HTMLElement) {
o[p] = null;
}
else if (typeof o[p] == 'object' )
JS.dropClasses(o[p]);
}
};
JSON.stringify(JS.dropClasses(e));
I am working on developing some objects within custom functions in JavaScript.
I wrote a piece of code like this:
function updateObject(obj) {
var obj = {
foo: 'foo',
bar: 'bar',
bizz: 'bizz',
bang: 'bang'
};
return obj;
}
console.log(updateObject());
I know this is not wrong in that it outputs an object with key:value pairs in the console, but it fails this test suite from Repl:
/* test suite */
(function testUpdateObject() {
var oldObj = {
cats: 'cats',
dogs: 'dogs',
};
var newObj = updateObject(oldObj);
if (typeof newObj !== 'object') {
console.error('ERROR: `createMyObject` must return an object');
return false
}
['foo', 'bar', 'bizz', 'bang'].forEach(function(key) {
if (!(key in newObj)) {
console.error('ERROR: `' + key + '` not in object returned by `updateObject`');
return false;
}
});
['foo', 'bar', 'bizz', 'bang'].forEach(function(key) {
if (newObj[key] !== key) {
console.error('ERROR: `' + key + '` should be "' + key + '" but was ' + newObj[key]);
return false;
}
});
if (!(newObj.cats === 'cats' && newObj.dogs === 'dogs')) {
console.error('ERROR: your function doesn\'t preserve existing key/value pairs');
return false;
}
console.log('SUCCESS: `updateObject` works correctly!');
})();
Specifically, the function fails to preserve existing key/value pairs. This is what I am trying to resolve.
I also tried it with dot notation and square bracket notation like so:
function updateObject(obj) {
var obj = {};
{
obj.foo ='foo',
obj.bar ='bar',
obj.bizz ='bizz',
obj.bang ='bang'
}
return obj;
}
and
function updateObject(obj) {
var obj = {};
{
obj['foo'] ='foo';
obj['bar'] ='bar';
obj['bizz'] ='bizz';
obj['bang'] ='bang';
}
return obj;
}
Both cases console.log(updateObject());.
I am out of ideas as to how to write this custom function with object that will pass the test where it preserves key/value pair and I am looking for some guidance on how to figure this out.
I have been unable to find a Stack Overflow article that speaks directly to this case, nor have I found any documentation online that speaks to how to write a function that preserves existing key/value pairs.
You never update the object, because you assign a new object to the given variable/parameter. You need to keep the array and assign the values step by step, or use, with ES6 Object.assign.
function updateObject(obj) {
obj = obj || {};
obj.foo = 'foo';
obj.bar = 'bar';
obj.bizz = 'bizz';
obj.bang = 'bang';
return obj;
}
console.log(updateObject());
(function testUpdateObject() {
var oldObj = {
cats: 'cats',
dogs: 'dogs',
};
var newObj = updateObject(oldObj);
if (typeof newObj !== 'object') {
console.error('ERROR: `createMyObject` must return an object');
return false
}
['foo', 'bar', 'bizz', 'bang'].forEach(function (key) {
if (!(key in newObj)) {
console.error('ERROR: `' + key + '` not in object returned by `updateObject`');
return false;
}
});
['foo', 'bar', 'bizz', 'bang'].forEach(function (key) {
if (newObj[key] !== key) {
console.error('ERROR: `' + key + '` should be "' + key + '" but was ' + newObj[key]);
return false;
}
});
if (!(newObj.cats === 'cats' && newObj.dogs === 'dogs')) {
console.error('ERROR: your function doesn\'t preserve existing key/value pairs');
return false;
}
console.log('SUCCESS: `updateObject` works correctly!');
})();
Just don't overwrite your parameter ...
function updateObject(obj) {
obj['foo'] ='foo';
obj['bar'] ='bar';
obj['bizz'] ='bizz';
obj['bang'] ='bang';
return obj;
}
you're redefining obj when you say "var obj". remove the var. Updated code below.
function updateObject(obj) {
var newObj = {
foo: 'foo',
bar: 'bar',
bizz: 'bizz',
bang: 'bang'
};
// Apply all props of newObj to oldObj, while preserving oldObj
return Object.assign(obj, newObj);
}
be aware that changing the original object is enough if you want to update it, you technically don't have to return the updated object (reference is preserved when you pass it into the function), but it's probably good practice to anyway.
if (!(newObj.cats === 'cats' && newObj.dogs === 'dogs')) {
console.error('ERROR: your function doesn\'t preserve existing key/value pairs');
return false;
}
So the problem is you don't preserve existing key/value pairs.
Since you redefine obj with a var, instead of adding the new properties to it, you overwrite all of the old pairs.
So the correct way is editting the obj that gets inputted instead of redefining it. You could add them manually:
function updateObject(obj) {
obj.foo = 'foo';
obj.bar = 'bar';
obj.bizz = 'bizz';
obj.bang = 'bang';
return obj;
}
Or you could use the relatively new Object.assign() method to merge two objects into eachother if your browser supports it:
function updateObject(obj) {
var extension = {
foo: 'foo',
bar: 'bar',
bizz: 'bizz',
bang: 'bang'
};
return Object.assign( obj, extension );
}
Is it possible to assign a value to multiple JavaScript objects at the same time?
Obviously this can be done with a for loop etc, but I'm curious if there's something in the new version of the language that makes this possible. Similar syntax already exists in a number of other languages, I just can't find the JavaScript equivalent.
Ideally, the syntax would look something like this:
{App1, App2, App3}.foo = "bar"
App1.foo === "bar" // true
App2.foo === "bar" // true
You are effectively looking for lenses, which can abstract over such operations and also provide multiple targets. There are various JS implementations around, though I didn't find any that uses lists. With them, it would look something like
set(onList(property("foo")), [App1, App2, App3]);
But that's ugly, right? And you were asking for new ES6 features. Yes, a Proxy can help us make this a lot more beautiful indeed:
ListProxy(App1, App2, App3).foo = "bar";
Here's how you'd implement such a function:
const ListProxy = (() => {
const handler = {
set(target, property, value) {
for (const t of target)
t[property] = value;
},
get(target, property) {
if (typeof target == "function")
target = target.values;
const maybe = target.filter(x => property in Object(x));
if (maybe.length == 0) return undefined;
let values = maybe.map(x => x[property]);
if (values.every(v => typeof v == "function")) {
function fnList(...args) {
return maybe.map(v => v[property](...args));
}
fnList.values = values;
values = fnList;
}
return new Proxy(values, handler);
}
};
return function ListProxy(...args) { return new Proxy(args, handler); };
})();
The get method is not so vitally important, but it does allow for deeper chaining and even function calls instead of assignments:
ListProxy({value:"ax"}, {value:"by"}).value[0].toUpperCase(); // ["A","B"]
There is no native way to do it. However, if you are just looking for similar syntax, you can do something similar. You can create a proxy function which will do it for you.
var _ = (...args) => {
var proxy = new Proxy(args, {
set: (target, property, value) => {
target.forEach(object => object[property] = value);
}
});
return proxy;
};
var App1 = {}, App2 = {}, App3 = {};
_(App1, App2, App3).value = {
foo: 'bar'
};
_(App1, App2, App3).someOtherValue = {
foo: 'baz'
};
console.log(App1); // { value: { foo: 'bar' }, someOtherValue: { foo: 'baz' } }
console.log(App2); // { value: { foo: 'bar' }, someOtherValue: { foo: 'baz' } }
console.log(App3); // { value: { foo: 'bar' }, someOtherValue: { foo: 'baz' } }
The only way to make something like the syntax you propose work is to extend the Object prototype, whether or not one thinks that's a good idea (it's not).
const App1 = {}, App2 = {}, App3 = {};
Object.defineProperty(Object.prototype, 'values', {
set(value) {
for (let prop in this) this[prop].value = value;
}
});
({App1, App2, App3}).values = "foo";
console.log(App1.value);
You would never be able to write {App1, App2, App2}.value, because the JS parser would interpret the leading { as the beginning of a block. Hence the need to enclose it in parentheses.
You cannot use value to set all the values, since that would conflict with the value property you want to set on the individual objects. Hence we use values instead.
I don't think a special syntax is required for this, I'd rather assign it using basic ES6:
const baz = { foo: "bar" };
[App1, App2, App3].forEach(app => app.value = baz);
you can use:
App1.value = App2.value = {foo: "bar"};
Or
App1.value.foo = App2.value.foo = "bar";
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
}