How to create a 'real' JavaScript array in Rhino - javascript

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);

Related

Overriding global functions in 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)

Difference between overloading __proto__ in two similar functions

In my investigation into making Array-like objects, I made this function,
Array2 = function(){
var out = [];
Object.defineProperty(out, 'prototype', { value : Array2.prototype }); // store a reference
out.__proto__ = Array2.prototype; // necessary as Array uses __proto__ and not prototype
if(arguments.length > 1) Array.prototype.push.apply(out, arguments); // re-implement constructor's
else if(arguments.length === 1) out.length = arguments[0]; // argument handling behaviour
return out;
};
// allow for normal prototyping behaviour
Array2.prototype = [];
Object.defineProperty(Array2.prototype, 'constructor', { value : Array2 });
and noticed that calling Array2() was returning the same as calling new Array2(), which isn't what I was expecting, so I considered a similar function for integers
Int = function(n){
var out = ~~n;
out.prototype = Int.prototype;
out.__proto__ = Int.prototype;
this.value = out; // added to check value when working as object
return out;
};
Int.prototype = 0;
Int.prototype.constructor = Int;
this time, Int returns a normal instance of a Number (__proto__ and prototype as for any number literal) and new Int returns an "Int" Object with Empty as __proto__ and undefined for prototype, with the number available through .value, same as calling without new.
Why are these very similar functions acting so differently, and why is new resulting in the for the first one? It is most likely something obvious I've overlooked.
Only tested in Google Chrome.
Actually, your Array2 function return real Arrays instead of only Array-like objects, this does not change when setting the [[prototype]] to an object that inherits from Array.prototype (altough you should not have created an array using [], but a plain object using Object.create(Array.prototype).
Your function Int has several problems.
out is a primitive number value, and has no properties. When assigning some, it will be implicitly casted to a Number object, which is discarded rightafter. The same problem with the "constructor" property on Int.prototype = 0.
Also, you can't use primitive values like 0 as prototype objects. When creating a new Int instance, it will inherit from the default Object.prototype as 0 is not of type "object". I'm not sure what happens when assigning such to the non-standard __proto__ property, but I guess it just fails.
Use this instead:
function Int(n){
var out = ~~n;
this.valueOf = function(){ return out; };
return out; // when not used as a constructor, return int-casted number
};
Int.prototype = Object.create(Number.prototype, {
constructor:{value:Int}
});

Understanding how javascript hashtables work

Could anyone explain to me why the code sample below reports true? I would have assumed that like in C# the instance of Test1 != instance of Test2.
Update: So I think I will go with some unique identifier stored in the base of both Test1 and Test2.
function Test1() { };
function Test2() { };
var test1 = new Test1();
var test2 = new Test2();
var dict = new Array();
dict[test1] = true;
alert(dict[test2]);
Your object (JavaScript's hashtable) is not using the instance of test1 or test2, but the string representation, as a key. Since both test1 and test2 have the same string representation: "[object Object]", the true value is associated with that key.
Try doing something like below instead:
function Test1(id) { this.id=id };
function Test2(id) { this.id=id };
var test1 = new Test1('1');
var test2 = new Test2('2');
var dict = {};
dict[test1.id] = true;
console.log(dict[test1.id]);
Keys in 'hashtables' (objects, basically) are always strings. So anything you add will be converted to a string.
new Test1();
returns a instance of Test1. Converted as a string, this is:
"[object Object]"
The same goes for Test2. So in fact, when storing true under the key of new Test1() as a string, you are working with the exact same record as the one by obtaining with the key new Test2() as a string. In other words,
(new Test1()).toString() == (new Test2()).toString();
The actual object is therefore simply:
{
"[object Object]": true
}
A solution is overwriting .toString() like this:
Test1.prototype.toString = function() { return "Test1" };
Test2.prototype.toString = function() { return "Test2" };
Then dict[test1] will be stored as dict['Test1'] and dict[test2] as dict['Test2'], which allows you to differ between them. Still, manually setting dict['Test1'] would overwrite things. As far as I know, there is no way to assign an object as a key.
Javascript objects aren't exactly hashtables; they're actually objects with string keys.
When you use an object as a key, the object is converted to a string by calling toString().
toString() will return the same string for all custom classes (unless you create your own toString), so they end up using the same key.
First: Use arrays only for numerical keys. For anything else use objects.
Property names can only be strings. Anything else is converted to its string representation. In case of objects, this is [object Object] or whatever toString() returns.
Which means, that if you want to make both objects distinguishable, you have to override this method and let it return something which is unique to each instance.
This question might help you: Hash/associative array using several objects as key

What does MooTools' Function.prototype.overloadSetter() do?

I'm looking through the MooTools source to try and understand its .implement() and .extend() utilities.
The definition of each refers to a function defined like this:
var enumerables = true;
for (var i in {toString: 1}) enumerables = null;
if (enumerables) enumerables = ['hasOwnProperty', 'valueOf', 'isPrototypeOf', 'propertyIsEnumerable', 'toLocaleString', 'toString', 'constructor'];
Function.prototype.overloadSetter = function(usePlural){
var self = this;
return function(a, b){
if (a == null) return this;
if (usePlural || typeof a != 'string'){
for (var k in a) self.call(this, k, a[k]);
if (enumerables) for (var i = enumerables.length; i--;){
k = enumerables[i];
if (a.hasOwnProperty(k)) self.call(this, k, a[k]);
}
} else {
self.call(this, a, b);
}
return this;
};
};
However, I am having a tough time understanding what it does.
Can you explain how this function works and what it does?
overloadSetter
overloadSetter, together with overloadGetter, are two function decorator methods. The overloadSetter function is used to transform functions that have the signature fn(key, value) to functions that could accept object arguments, ie: fn({key: value}).
In order to do this, overloadSetter must wrap the original function. This wrapper function has the signature fn(a, b) which is a shortcut for fn(key, value). This effectively becomes the new overloaded version of the original function.
First thing this overloaded function does is check whether the passed key argument (a) is of the string type or not. If it's not a string, the function assumes that we're passing an object. So it iterates over each key-value pair in the object and applies the original function to it. If it's a string, on the other hand, it simply applies the function to the values of the a and b arguments.
Example
To illustrate, let's say we have the following function:
var fnOrig = function(key, value){
console.log(key + ': ' + value);
};
var fnOver = fnOrig.overloadSetter();
fnOver('fruit', 'banana');
fnOver({'fruit': 'banana', 'vegetable': 'carrot'});
In the first invocation, the fnOver function is invoked with two arguments, a key and a value. When the function checks the type of the a argument value, it'll see that it is a string. Therefore, it will simply invoke the original fnOrig function: fnOrig.call(this, 'fruit', 'banana'). Our console output is 'fruit: banana'.
For the second invocation, the fnOver function is invoked with an object argument. Since we passed an object instead of a string, fnOver will iterate through the members of this object and invoke the fnOrig function for each one of them. Thus, fnOrig will be invoked twice in this case: fnOrig.call(this, 'fruit', 'banana') and fnOrig.call(this, 'vegetable', 'carrot'). Our console output is 'fruit: banana' and 'vegetable: carrot'.
Extras
Inside the wrapper function, you'll see that there's an check for the value of usePlural. This is an argument for the overloadSetter method itself. If you set this value to true, the new function will treat all arguments as object. This means that even if you pass a string key argument, it will still be processed as an object.
The other thing, the enumerables code that preludes the actual method declaration, is there because it fixes an issue with some browsers wherein the native Object methods are not enumerated in for/in loops even if the object itself implements its own version of it.
The part that had me scratching my head for a while was the
var enumerables = true; for (var i in {toString: 1}) enumerables = null;
part, which turns out to be a test for the DontEnum bug that some browsers have. At first glance it seems like it should just set enumerables to null, but with the DontEnum bug toString is suppressed (wrongly, because the object's prototype.toString has the DontEnum flag) and enumerables is left as true.
overloadSetter (or rather the resulting function) then has to check one at a time for the seven properties that the DontEnum bug affects, to see if they exist in the object argument.

What is this javascript code doing?

this.String = {
Get : function (val) {
return function() {
return val;
}
}
};
What is the ':' doing?
this.String = {} specifies an object. Get is a property of that object. In javascript, object properties and their values are separated by a colon ':'.
So, per the example, you would call the function like this
this.String.Get('some string');
More examples:
var foo = {
bar : 'foobar',
other : {
a : 'wowza'
}
}
alert(foo.bar); //alerts 'foobar'
alert(foo.other.a) //alerts 'wowza'
Others have already explained what this code does. It creates an object (called this.String) that contains a single function (called Get). I'd like to explain when you could use this function.
This function can be useful in cases where you need a higher order function (that is a function that expects another function as its argument).
Say you have a function that does something to each element of an Array, lets call it map. You could use this function like so:
function inc (x)
{
return x + 1;
}
var arr = [1, 2, 3];
var newArr = arr.map(inc);
What the map function will do, is create a new array containing the values [2, 3, 4]. It will do this by calling the function inc with each element of the array.
Now, if you use this method a lot, you might continuously be calling map with all sorts of arguments:
arr.map(inc); // to increase each element
arr.map(even); // to create a list of booleans (even or odd)
arr.map(toString); // to create a list of strings
If for some reason you'd want to replace the entire array with the same string (but keeping the array of the same size), you could call it like so:
arr.map(this.String.Get("my String"));
This will create a new array of the same size as arr, but just containing the string "my String" over and over again.
Note that in some languages, this function is predefined and called const or constant (since it will always return the same value, each time you call it, no matter what its arguments are).
Now, if you think that this example isn't very useful, I would agree with you. But there are cases, when programming with higher order functions, when this technique is used.
For example, it can be useful if you have a tree you want to 'clear' of its values but keep the structure of the tree. You could do tree.map(this.String.Get("default value")) and get a whole new tree is created that has the exact same shape as the original, but none of its values.
It assigns an object that has a property "Get" to this.String. "Get" is assigned an anonymous function, which will return a function that just returns the argument that was given to the first returning function. Sounds strange, but here is how it can be used:
var ten = this.String["Get"](10)();
ten will then contain a 10. Instead, you could have written the equivalent
var ten = this.String.Get(10)();
// saving the returned function can have more use:
var generatingFunction = this.String.Get("something");
alert(generatingFunction()); // displays "something"
That is, : just assigns some value to a property.
This answer may be a bit superflous since Tom's is a good answer but just to boil it down and be complete:-
this.String = {};
Adds an object to the current object with the property name of String.
var fn = function(val) {
return function() { return(val); }
}
Returns a function from a closure which in turn returns the parameter used in creating the closure. Hence:-
var fnInner = fn("Hello World!");
alert(fnInner()); // Displays Hello World!
In combination then:-
this.String = { Get: function(val) {
return function() { return(val); }
}
Adds an object to the current object with the property name of String that has a method called Get that returns a function from a closure which in turn returns the parameter used in creating the closure.
var fnInner = this.String.Get("Yasso!");
alert(fnInner()); //displays Yasso!

Categories

Resources