Overriding global functions in Javascript - javascript

After overriding the Array function, why the behavior of the two objects is different?
function Array(){}
var array1 = ['1','2']
var array2 = new Array();
console.log(array1.length); // 2
console.log(array2.length); // undefined
Also, Object() returns an empty object instance but with a user-defined function, we must use new operator, why?

That (Object() without new) is a shortcut, which you can provide too:
function Stuff(x){
if(!(this instanceof Stuff)){
console.log("You forgot 'new'");
return new Stuff(x);
}
this.x=x;
}
var a=Stuff(1);
var b=new Stuff(2);
console.log(a,b);
And yes, when you locally override the built-in Array, you are stuck with that one. But already existing arrays remain to be original, they do not get fitted retroactively to your own type, and the [] syntax is not affected by your override (case of c):
var a=[1,2];
var b=new Array(3,4);
(function(){
function Array(){}
var c=[1,2];
var d=new Array(3,4);
console.log("a:",a);
console.log("b:",b);
console.log("c:",c);
console.log("d:",d);
})()
(The function-magic is necessary for b to remain unaffected by function hoisting)

Related

Prototype lost on slice on array-like construct

I have created an array like prototype:
function METracker() {}
METracker.prototype = Object.create(Array.prototype);
METracker.prototype.myMethod = function(aStd) {
return true;
};
now i create an instance:
var aInst = new METracker('a', 'b', 'c');
Now I want to clone it so I do:
var cloneInst = aInst.slice();
however cloneInst no longer has the method .myMethod is there a way to keep the prototype on the clone?
Thanks
If you're going to create your own array-alike, the trick is to extend an array instance, not the array prototype:
function MyAry() {
var self = [];
[].push.apply(self, arguments);
// some dark magic
var wrap = 'concat,fill,filter,map,slice';
wrap.split(',').forEach(function(m) {
var p = self[m];
self[m] = function() {
return MyAry.apply(null, p.apply(self, arguments));
}
});
// add your stuff here
self.myMethod = function() {
document.write('values=' + this.join() + '<br>');
};
return self;
}
a = new MyAry(11,44,33,22);
a.push(55);
a[10] = 99;
a.myMethod()
b = a.sort().slice(0, 4).reverse();
b.myMethod();
Basically, you create a new array (a normal array, not your object), wrap some Array methods so that they return your object instead of generic arrays, and add your custom methods to that instance. All other array methods and the index operation keep working on your object, because it's just an array.
I have created an array like prototype:
No, you haven't.
function METracker() {}
METracker.prototype = Object.create(Array.prototype);
METracker.prototype.myMethod = function(aStd) {
return true;
};
The METracker constructor does nothing at all, it will just return a new Object with Array.prototype on its [[Prototype]] chain.
var aInst = new METracker('a', 'b', 'c');
Just returns an instance of METracker, it has no data since the constructor doesn't do anything with the arguments passed. Assigning Array.prototype to the inheritance chain doesn't mean the Array constructor is invoked.
var cloneInst = aInst.slice();
Note that callling slice() on aInst just returns a new, empty array. aInst doesn't have a length property, so the algorithm for slice has nothing to iterate over. And even if aInst had properties, slice will only iterate over the numeric ones that exist with integer values from 0 to aInst.length - 1.
If you want to create a constructor that creates Array–like objects, consider something like:
function ArrayLike() {
// Emulate Array constructor
this.length = arguments.length;
Array.prototype.forEach.call(arguments, function(arg, i) {
this[i] = arg;
}, this);
}
ArrayLike.prototype = Object.create(Array.prototype);
var a = new ArrayLike(1,2,3);
document.write(a.length);
document.write('<br>' + a.join());
The above is just play code, there is a lot more to do. Fixing the length issue isn't easy, I'm not sure it can be done. Maybe there needs to be a private "fixLength" method, but methods like splice need to adjust the length and fix indexes as they go, so you'll have to write a constructor that emulates the Array constructor and many methods to do re–indexing and adjust length appropriately (push, pop, shift, unshift, etc.).

Effect of array1.concat(array2) not persisting after function call

this works:
var f = function(a){
a.push(1);
};
var a = [];
f(a);
console.log(a);//[1];
But this:
var f = function(a){
a = a.concat([1]);
};
var a = [];
f(a);
console.log(a);//[];
Does not work. With work I mean that the changes made persist after the function call.
Now I realise that this most likely has something to do with the arguments being passed 'as reference by value', meaning that a change in reference (ie assigning a new object) does not persist after the function call.
So I wonder, is there a persistent version of concat? Or do I manualy have to push all elements from one array into the other?
Edit: All of you suggesting to return the newly created array: that's pretty much exactly what I don't want. If I wanted to do that I wouldn't have created this question. And I certainly would not have ended it specifically asking for a persistent version of concat.
concat returns a new array, it doesn't mutate the source array.
var f = function(a){
return a.concat([1]);
};
var a = f([]);
console.log(a);//[1];
If you do want to mutate the array, just use push.apply to pass array elements as individual arguments to the push function.
var f = function(a) {
a.push.apply(a, [1]);
};
var a = [];
f(a);
console.log(a); //1
That's because the function parameter, a, is not the same as the variable a declared in the outer scope. When you assign a new value to the parameter a, it has no effect on the outer variable.
a = a.concat([1]);
Instead, simply return the new array, and use the return value in the outer scope, like this:
var f = function(a){
return a.concat([1]);
};
var a = [];
a = f(a);
Or perhaps consider using push like this:
var f = function(a){
Array.prototype.push.apply(a, [1, 2, 3]);
};
This will push multiple values onto the source array, so it's pretty much equivalent to concat except that it modifies the original array.

Create a JavaScript constructor function which subclasses an array

I want to create a constructor function in javascript, which has a .prototype property and can be used with the new keyword to create new objects that have this property in their protochains. I also would like this object to subclass an Array.
I have been able to create an object that subclasses an Array (all needed functionality works)
I have not figured out how to make this object act as a function, so that it can be used as a constructor.
SubArray = function() {this.push.apply(this, arguments);};
SubArray.prototype = Object.create(Array.prototype);
SubArray.prototype.constructor = SubArray;
SubArray.prototype.last = function(){return this[this.length -1]};
var arr = new SubArray(0); // [0]
arr.push(1,2,3); // [0,1,2,3]
console.log(arr, arr.length); // [0,1,2,3], 4
arr.length = 2;
console.log(arr, arr.length); // [0,1], 2
console.log(arr.last()); // 2
console.log(arr instanceof Array); // true
console.log(arr instanceof SubArray); // true
I have read that by adding certain keys to the arr object it can be used as a constructor function.
I believe I would have to do something like this.
var arrayFunction = new SubArray(0); // [0]
arrayFunction.prototype = {
constructor: arrayFunction,
//shared functions
};
arrayFunction.call = function(){//this would be the constructor?};
arrayFunction.constructpr = function(){//I remember seeing this as well, but I can't find the original source where I saw this};
I would really appreciate any insight into how this can be done, thanks in advance for your help
I recall John Resig tried to subclass an Array for Jquery, but found it not possible.

How to disable pointers usage in JS?

Why is the result {"a":"b","c":1}?
var foo = {"a":"b","c":0};
var bar = foo;
bar.c++;
alert(JSON.stringify(foo));
How to disable this behavior?
Both foo and bar variables reference the same object. It doesn't matter which reference you use to modify that object.
You cannot disable that behaviour, this is how JavaScript and many other major languages work. All you can do is to clone the object explicitly.
var foo = {"a":"b","c":0};
var bar = {"a":foo.a, "c": foo.c};
bar.c++;
What you're doing is making a second reference to an object but what it seems you want is a copy of that object instead.
If you want that functionality then you really want a copy function that copies all of the properties, one by one, into a new object:
// Take all the properties of 'obj' and copy them to a new object,
// then return that object
function copy(obj) {
var a = {};
for (var x in obj) a[x] = obj[x];
return a;
}
var foo = {"a":"b","c":0};
var bar = copy(foo);
bar.c++;
alert(JSON.stringify(foo));
and you'll get {"a":"b","c":0}
First, Javascript doesn't pass pointers, it passes references, slightly different. Secondly, there's no way to modify Javascript's default behavior, unfortunately fortunately.
What you might want to do is create a constructor and use that to create two similar, but separate instances of an object.
function Foo(a, b) {
this.a = a;
this.b = b;
}
var bar1 = new Foo(0, 0);
var bar2 = new Foo(0, 0);
bar2.b++;
console.log(bar1);
console.log(bar2);
>> {a:0, b:0};
>> {a:0, b:1};
You can't disable the way javascript works.
If you change a reference object, it effects all the object references...

How to create a 'real' JavaScript array in Rhino

Okay, I'm a little stumped. I'm probably missing something blatantly obvious but apparently I just can't see the forest for the trees:
I'm trying to call a JavaScript function that expects its parameter to be an array, i.e. it checks if (arg instanceof Array)... Unfortunately, I (or Rhino) just can't seem to create such an array:
Context cx = Context.enter();
Scriptable scope = cx.initStandardObjects();
String src = "function f(a) { return a instanceof Array; };";
cx.evaluateString(scope, src, "<src>", 0, null);
Function f = (Function) scope.get("f", scope);
Object[] fArgs = new Object[]{ new NativeArray(0) };
Object result = f.call(cx, scope, scope, fArgs);
System.out.println(Context.toString(result));
Context.exit();
And alas, result is false.
What am I missing here?
Edit:
Just a little more information: both [] instanceof Array and new Array() instanceof Array return true as one would expect. If I add elements to the array they show up in the JavaScript code with the right indices (numeric, starting from zero):
NativeArray a = new NativeArray(new Object[]{ 42, "foo" });
When output using this JavaScript function:
function f(a) {
var result = [];
result.push(typeof a);
for (var i in a) {
result.push(i + ' => ' + a[i]);
}
return result.join('\\n');
}
The result is:
object
0 => 42
1 => foo
So it works. Except that I want a 'real' array :)
Almost forgot: Object.prototype.toString.call(a) returns [object Array]
Okay, that's the crucial information. That tells us that the array really is an array, it's just that it's being initialized by an Array constructor in a different scope than the one that the function is testing for, exactly as though you were testing an array from one window against another window's Array constructor in a browser-based app. E.g., there's a scope problem.
Try replacing
Object[] fArgs = new Object[]{ new NativeArray(0) };
with
Object[] fArgs = new Object[]{ cx.newArray(scope, 0) };
...to ensure the correct Array constructor is used. Because you've gone directly to the NativeArray constructor, you've bypassed ensuring that its scope is right, and so the array object's constructor is an Array constructor, but not the same Array constructor as the one on the global object the function sees.
For those who are intentionally creating a different subclass of the array implementation, and therefore can't use cx.newArray, what you can do is:
add this line
ScriptRuntime.setBuiltinProtoAndParent(fArgs, scope, TopLevel.Builtins.Array);

Categories

Resources