Implicit property access - javascript

I'm trying to do implicit property access with JavaScript. So, I'm trying to do something like:
const array = [];
if (array) { // --> here can it implicitly call .length?
// code
}
I was thinking I'd need to see the internals of if statement, but not sure if that's even possible.

The if statement coerces its argument to a boolean, just like the Boolean() function does.
As all objects are truthy, and arrays are objects, no intermediate primitive coercion needed (whose behavior could be overridden), so what you want is simply not possible.
You'll have to type out array.length.

Related

Weird Array Objects - JavaScript

Arrays are quite something in JavaScript when compared with other programming languages and it's not without its full set of quirks.
Including this one:
// Making a normal array.
var normalArray = [];
normalArray.length = 0;
normalArray.push(1);
normalArray[1] = 2;
normalArray; // returns [1, 2]
normalArray.length // returns 2
So yes, the above is how we all know to make arrays and fill them with elements, right? (ignore the normalArray.length = 0 part for now)
But why is it that when the same sequence is applied on an object that's not purely an array, it looks a bit different and its length property is off by a bit?
// Making an object that inherits from the array prototype (i.e.: custom array)
var customArray = new (function MyArray() {
this.__proto__ = Object.create(Array.prototype);
return this
});
customArray.length = 0;
customArray.push(1);
customArray[1] = 2;
customArray; // returns [1, 1: 2]
customArray.length // returns 1
Not entirely sure what's going on here but some explanation will be much appreciated.
This may not be the perfect answer, but according to my understanding of Javascript arrays, they are a little bit different than usual objects. (Mainly due to the fact that it maintains a length property, and Objects don't).
So if we take your code for an example:
var normalArray = [];
This is the right way to create an array in Javascript. But what about the below one?
var customArray = new (function MyArray() {
this.__proto__ = Object.create(Array.prototype);
return this
});
Are they same? Let's see..
Array.isArray(normalArray); // true -> [object Array]
Array.isArray(customArray); // false -> [object Object]
So it is clear that although you inherit from the array prototype, it doesn't really create an object with Array type. It just creates a plain JS object, but with the inherited array functions. That's the reason why it updates the length when you set the value with customArray.push(1);.
But since your customArray is only a regular object and for a regular JS object, [] notation is used to set a property, it doesn't update the length (because Objects don't have a length property)
Hope it's clear :)
The array you are trying to create is not a pure array (as you are perhaps aware). Its basically a JavaScript object and is supposed to behave like an object.
While treating an object like an array, its up to you to maintain all it's array like features.
You specifically have to assign a length property to it and you did it correctly.
Next, the push method from Array.prototype is supposed to insert an element to the array and increment the length property (if any), so it did increment 0 to 1. There you go, the length now is 1.
Next you used the literal notation of property assignment to Object, which is similar to something like customArray['someProperty'] = 1.
While using literal notation, no method from Array.Prototype is being invoked and hence the customArray object never knows that it has to behave like an Array and its length property remains unaffected. It simply behaves like an object and you get what you got.
Remember the length is just a property on Array class and this property is appropriately incremented and decremented by every method on Array.
Note: Array like objects are not recommended and its up to you entirely to maintain the index and other Array stuff for such objects.
From what I can see, you have a problem with your function:
return this
This should be
return (this);
Just fixes any potential errors you might have. Another thing is you're not using the var keyword to declare customArray. These errors might be breaking your code.

What is the technical definition of a Javascript iterable and how do you test for it?

I've been implementing a useful subclass of the ES6 Set object. For many of my new methods, I want to accept an argument that can be either another Set or an Array, or really anything that I can iterate. I've been calling that an "iterable" in my interface and just use .forEach() on it (which works fine for a Set or an Array. Example code:
// remove items in this set that are in the otherIterable
// returns a count of number of items removed
remove(otherIterable) {
let cnt = 0;
otherIterable.forEach(item => {
if (this.delete(item)) {
++cnt;
}
});
return cnt;
}
Or
// add all items from some other iterable to this set
addTo(iterable) {
iterable.forEach(item => {
this.add(item);
});
}
But, I suspect I may be not really supporting any iterable in the way that ES6 defines it so I'm interested in what the real definition of a Javascript iterable is using the term as the ES6 specification does?
How do you test for it in ES6 Javascript?
How should you iterate a generic iterable?
I've found phrases like this in the ES6 specification:
If the parameter iterable is present, it is expected to be an object
that implements an ##iterator method that returns an iterator object
that produces a two element array-like object whose first element is a
value that will be used as a WeakMap key and whose second element is
the value to associate with that key.
But, that refers to an ##iterator method which I don't seem to be able to access via that property name.
What is the real definition of a Javascript iterable using the term as the ES6 specification does?
§25.1.1.1 defines "The Iterable Interface".
They're objects with a Symbol.iterator-keyed method that returns a valid Iterator (which in turn is an object expected to behave as it should according to §25.1.1.2).
How do you test for it in ES6 Javascript?
We cannot test what the ##iterator method returns without calling it, and we cannot test whether the result conforms to the Iterator interface without trying to run it. The best bet would be to do
function looksIterable(o) {
return typeof o[Symbol.iterator] == "function";
}
however I wouldn't usually test for this but simply let it fail with an exception when it's not iterable.
How should you iterate a generic iterable?
Don't use forEach. (In fact, never use forEach anywhere in ES6).
The proper way to iterate is a for (… of …) loop. It does all the checking for iterability (using the abstract GetIterator operation and running (and even closing) the iterator, and throws appropriate TypeErrors when used on non-iterable values.
There's a special property on the Symbol constructor, Symbol.iterator, whose value is that, uhh, I guess you'd say "conceptual" property name "##iterator". So you can create an iterator method for an object like this:
object[Symbol.iterator] = function* () {
// do something that makes sense
};
You can also test to see if some object is an iterator by doing
if (Symbol.iterator in object)
(and maybe also checking to see if it's a function). Now those iterator functions (the value of the Symbol.iterator property) are generator functions (not the * that I edited into the example). Thus you start them and get values by first calling the function and saving the returned object, and then calling .next(). That'll get you an object with value and done properties.
You can alternatively let for ... of or the "spread" ... operator worry about the iterator function.

Javascript - how do primitives really work

I have the following code:
String.prototype.isLengthGreaterThan = function(limit){
return this.length > limit;
}
console.log("John".isLengthGreaterThan(3));
Number.prototype.isPositive = function(){
return this > 0;
}
console.log(5.isPositive(3)); //error
The 1st example work, the other doesn't. Why?
From my understanding, primitives are not objects – though they do have access to their function constructor's prototype (Number, String, etc.). So, you can add methods directly to the prototype. In my example above it didn't work in one instance.
I am looking for "under the hood" answer, to understand what happens when you do, for example:
var a = 1;
How does it really have an access to its prototype if it isn't an object?
The dot thing isn't to do with primitives, it's just the syntax of writing numeric literals. In a number, the . is a decimal point, whereas of course that's not the case for a string. To use a method on a literal number, you have two choices:
Parens:
console.log((5).isPositive(3));
Two dots (yes, really):
console.log(5..isPositive(3));
In the second case, it works because the first dot is a decimal point; this means that the dot following can't be a decimal point, and so it's the property accessor operator.
How does it really have an access to it's prototype if it isn't an object.
It's promoted to an object automatically by the JavaScript engine when you do the propety access. In the case of numbers, it's as though you called new Number(5) or similar. In the case of strings, it's as though you called new String("the string"). When the property access is complete, the temporary object is then discarded immediately when the expression is complete. Naturally, the JavaScript engine can optimize the object allocation out, but conceptually that's what happens.
So conceptually:
We do (say) var n = 1; The variable a contains the primitive number value 1.
We do n.isPositive(3); to create a string from it using Number.prototype.toFixed. That's a property access operation:
The engine evaluates the left-hand side, n, and gets the result 1 (a primitive number); this is the base it'll use for the property access operation.
The engine evaluates the right-hand side (isPositive) to determine the property key (name) to look up. In this case it's a literal, so the key is the string "isPositive". This is the property key for the property access operation.
The engine goes to look up the property on the base. Since the base we have is a primitive, the JavaScript engine promotes it (coerces it) to an equivalent Number object.
Since that object doesn't have a "isPositive" property, the engine looks at its prototype, Number.prototype, and finds it; it's a reference to a function.
Various things happen that aren't really germane, and ultimately isPositive is called with this being an object coerced from the primitive value 1. It does its work and generates its return value.
Since the temporary object isn't referenced by anything anymore, it's eligible for garbage collection.
The mechanism by which this happens is a bit scattered in the specification:
The runtime semantics of the property accessor operator return something called a Reference specification type, which basically is a purely abstract specification holding object that will get evaluated later. It says that the base will be the result of evaluating the left-hand side of the property accessor, and the property anme will be the result of evaluting the right-hand side.
The GetValue operation on the Reference type, which says (step 5) that if it's a property reference and the base is primitive, it's coerced via the specification's ToObject operation.
ToObject, which defines the rules for doing that.

How "this" in methods of Javascript Number objects be converted to the numeric value of the object?

It seems in Number objects, numeric values can be referenced directly using "this" instead of this.valueOf() or something like "this.value", for example:
Number.prototype.printPlusOne=function(){
var tmp=this+1;
alert(tmp);
}
var n=new Number("5");
n.printPlusOne();
And why is this possible(using "this" directly instead of "this.valueOf()" in the addition above)?
I didn't find this feature in the Number specification, Did I missed something?
And further more, Is this automatic conversion feature used in other classes of objects?
Neither operand of the + operator is a string, so JavaScript calls valueOf to implicitly convert the Object (a Number, in this case) to a primitive value, in order to perform numeric addition. Quoting David Flanagan:
If the object has a valueOf() method that returns a primitive value, JavaScript converts (if necessary) that primitive value to a number and returns the result.
So, this is implicitly converted to its primitive value.
If you did something silly like override Number.valueOf:
Number.prototype.valueOf = function() {
return 10;
}
then your function would alert 11 instead.
Not sure what you're trying to accomplish here, but:
Number.prototype.print=function(){
alert(this.valueOf());
}
var n=new Number("5");
n.print();

what happens when javascript array is treated like an object?

the index variable below is incorrectly initialized because f() will be returning stuff other than numbers, like strings. So what's the worst that can happen here? My testing seems to indicate that it has no effect, but now I am wondering...
function index(o, f) {
var index = []; // should be index = {};
each(o, function(k, v, o) { index[f(k, v, o)] = v; });
return index;
}
Javascript arrays are special objects that have an automatically set length property and inherit Array.prototype.
Unless you use a length property, there is no harm in treating an array as an object.
An array is an object, thus it can be treated as such without much side effects. Doing so however might result in some confusion, as the length property does not count non-numeric keys, and all the array prototype functions will likewise ignore them.
Just change [] to {}
You'll be creating an associative array, which is a valid JavaScript structure. Although, it is technically different than an object, you can interact with the array just like you would an object (for ... in to iterate, myarray[key] to fetch values). You may want to consider returning an object instead of an array if you suspect some keys will be strings.
"f() will be returning stuff other than numbers, like strings"
If f() only returns strings, then you're good to go, you're just using your array as an object and adding properties. The only downside is that the array itself remains empty, so for example you cannot count how many items you have added.
If f() can return both strings and numbers, it's going to create a mess. The loop will populate sometimes the array, sometimes the object properties.
I am not sure what you mean by "like strings", but if what f() returns is neither a number nor a string then it's not going to work.

Categories