I have created a node module with a couple of custom methods for arrays and strings.
First I just used it like a regular module and got the functions from a require like this:
Alt 1.
const invSlice = require('inverted-slice');
let arr1 = [1,2,3,4];
invSlice.iSlice(arr, start, stop);
This works but it would be nicer to call iSlice as a method on the Array object. I solved this by adding the following code in my library:
Array.prototype.iSlice = iSliceBuiltin; // iSliceBuiltin is my function
And the method can now be used like:
Alt 2.
require('inverted-slice');
let arr1 = [1,2,3,4];
arr1.iSlice(start, stop);
Which I think is nicer then Alt 1.
Question
My question is if there is any best practice or guidelines to follow when adding custom methods like in Alt 2 to built-in Objects like Array or String ?
Extending built-in prototypes has always triggered debates, and I think we can conclude it is not considered best practice.
On the other hand it is indeed nice if you can call these custom methods as object methods instead of plain functions.
You might consider a wrapper function that will return an Array instance that has the extra methods defined for it: i.e., not on the prototype, but on the Array instance itself.
Your module could look like this:
function iArray(arr) {
return Object.assign([], arr || [], {
iSlice: iSliceBuiltin,
iSplice: iSpliceBuiltin
});
}
// ... your module functions come here, but excluding the changes to the Array prototype
module.exports = {
iArray
}
Then you would use it like this:
const iArray = require('inverted-slice');
let arr1 = iArray([1,2,3,4]); // enrich array with extra methods
let result = arr1.iSlice(0, 1);
To allow chaining, you could change the return statement in iSliceSpliceHelper to:
return iArray(newArr);
So, now you can write:
let arr1 = iArray([1,2,3,4]); // enrich array with extra methods
let result = arr1.iSlice(0, 1).iSlice(1, 2);
Existing libraries might implement your alternative 1 (e.g. underscore), but many also go for something like I propose here. See for instance Sugar (new Sugar.Array([1,2,3])), or Lazy (Lazy([1,2,3])).
In small doses I think it's not that big of a deal to use Alt 2, but I believe that over usage can create problems. If I remember correctly, they had to completely redo Cut The Rope due to performance problems that I believe stemmed largely in part from prototype extensions. You may also want to consider posting this on https://codereview.stackexchange.com/
A couple references:
http://perfectionkills.com/whats-wrong-with-extending-the-dom/
https://softwareengineering.stackexchange.com/questions/104320/why-is-extending-the-dom-built-in-object-prototypes-a-bad-idea
Related
I have a problem where I'm generating many values and need to make sure I only work with unique ones.
Since I'm using node js, with the --harmony flag, and have access to harmony collections, I decided that a Set may be an option.
What I'm looking for is something similar to the following example:
'use strict';
function Piece(x,y){
this.x = x
this.y = y
}
function Board(width,height,pieces){
this.width = width
this.height = height
this.pieces = pieces
}
function generatePieces(){
return [
new Piece(0,0),
new Piece(1,1)
]
}
//boardA and boardB are two different but equivalent boards
var boardA = new Board(10,10,generatePieces())
var boardB = new Board(10,10,generatePieces())
var boards = new Set()
boards.add(boardA)
boards.has(boardB) //return true
Now normally to achieve this in another language, say c#, I would expect to have to implement an equals function, as well as a hash code generating function for both Board and Piece. Since I'd expect the default object equality to be based on references.
Or perhaps use a special immutable value type (say, a case class in scala)
Is there a means to define equality for my objects to solve my problem?
Is there a means to define equality for my objects to solve my problem?
No not really. There has been some discussion about this on the mailing list. The result is:
Build your own Set/Map abstraction on top of Set/Map, which would convert the objects to a primitive value according to your hashing function.
Wait for value objects coming in ES7.
This will do it for constructors like what you're working with.
var sameInstance = function(obj1, obj2){
return obj2 instanceof ob1.constructor;
};
Some other types of objects you might need something different, but this should be ok for what you need.
The above is what you need in function form. To get it to work with Set you will have to inherit the Set object, and override the has method with your own has.
I am receiving an ajax feed of documents that looks something like this (much simplified):
aDocs = [{title:'new doc', ext:'pdf'}, {title:'another', ext:'xlsx'}];
I am going to iterate through the aDocs array and display information about each doc, while adding some methods to each doc that will allow for modifying the HTML for display and making API calls to update the database.
I read here that in order to add methods to existing objects, you can use the __proto__ attribute. Something along the lines of:
function Doc(){}
Doc.prototype.getExt = function(){return this.ext}
Doc.prototype.getTitle = function(){return this.title}
for (var i=0; i<aDocs.length; i++){
aDocs[i].__proto__ = Doc.prototype
}
According to that article above,this isn't official javascript, isn't supported by IE (never will be), and will likely be deprecated in webkit browsers.
Here's an alternative stab at it:
function getExt(){ return this.ext }
function getTitle(){return this.title}
for (var i=0; i<aDocs.length; i++){
aDocs[i].getExt = getExt;
aDocs[i].getTitle = getTitle;
}
Is this second alternative viable and efficient? Or am I re-creating those functions and thereby creating redundant overhead?
Again the above examples are simplified (I know aDocs[i].ext will solve the problem above, but my methods for display and API calls are more complicated).
Is this second alternative viable and efficient?
Yes.
Or am I re-creating those functions and thereby creating redundant overhead?
No, the functions are reused, not re-created. All of the objects will share the single copy of the getExt and getTitle functions. During the call to the functions from (say) aDocs[1], within the call, this will refer to the object the function is attached to. (This only applies if you call it as part of an expression retrieving it from the object, e.g., var title = aDocs[1].getTitle();)
Alternately, if you liked, you could create new objects which shared a prototype, and copy the properties from the aDocs objects to the new objects, but you've asked about assigning new functions to existing objects, so...
Augmenting (adding methods to) the prototype is often the best way to go, but since you're dealing with object literals (or JSON.parse results), you'd have to either augment the Object.prototype which is not done, or create a wrapper constructor, with the methods you need attached to its prototype. The problem will be: getting to grips with this in that case... I'd leave things as they are: use the second approach: a simple loop will do just fine. Besides: prototype methods are (marginally) slower anyway...
The function objects themselves are being created ASAP (if they are defined in the global namespace, they're created as soon as the script is parsed). By simply looping through those objects, and assigning a reference to any function to each object, you're not creating additional functions at all.
Just try this:
var someObj = {name:'someObj'},
anotherObj = {name: 'anotherObj'},
someFunction = function()
{
console.log(this);
};
someObj.func = someFunction;
anotherObj.func = someFunction;
//or, shorter
someObj.func = anotherObj.func = someFunction;
//therefore:
console.log(someObj.func === anotherObj.func);//logs true! there is only 1 function object
someObj.func();//logs {name: 'someObj'}
anotherObj.func();//logs: {name: 'anotherObj'}
There have been posted many questions (and answers) that deal with this matter more in-depth, so if you're interested:
Objects and functions in javascript
Print subclass name instead of 'Class' when using John Resig's JavaScript Class inheritance implementation
What makes my.class.js so fast?
What are the differences between these three patterns of "class" definitions in JavaScript?
Are all more or less related to your question
In this case, I would just pass the object to the constructor of Doc;
function Doc(obj){
this.obj = obj;
}
Doc.prototype.getExt = function(){
return this.obj.ext;
}
Doc.prototype.getTitle = function(){
return this.obj.title;
}
var docs = [];
for (var i=0; i<aDocs.length; i++){
docs.push(new Doc(aDocs[i]));
}
There are two problems with your approach:
You have to copy each method individually for every instance.
Your "class" is not documented anywhere, making it a class makes it clearer that your object has those methods.
I want to create a javascript pipeline like powershell, bash (|) or f# (|>). Ie. something equivalent to
getstuff() | sort() | grep("foo") | take(5)
I saw a discussion about this in coffeescript forum but in the end they shelved it because everybody said that you could do the same thing with function chaining. But as far as I can see that requires getstuff returns something that has a sort method on it; the sort method must return something that has grep method on it etc. This is pretty restrictive as it requires all potential pipeline members to know about each other in advance. I know JavaScript has some pretty clever tricks in it and I am still at the 101 level - so is this doable
getstuff().sort.().grep().take()
without that constraint
is this doable
getstuff().sort.().grep().take()
without that constraint
No.
I like short answers! Can you suggest any way that something like it could be done
At a high level, you could do something similar to what jQuery does under the hood to allow chaining. Create an array-like wrapper object type which has all of the functions you want to be able to call; each successive chained call can operate on an internal stack in addition to the explicitly-passed arguments.
Not to keep beating the dead jQuery horse, but one of the best ways to understand what I'm talking about is to just start digging through the jQuery core source code and figure out how the chaining works.
Defining an object to support the kind of function chaining you want is actually quite easy:
getStuff = ->
sort: ->
# set #stuff...
this
grep: (str) ->
# modify #stuff...
this
take: (num) ->
#stuff[num]
That's all you need to make getstuff().sort.().grep('foo').take(5) work.
You can make those calls without worrying about the return values having the appropriate methods like so:
take(5, grep("foo", sort(getstuff())));
But, that doesn't get through the problem of each function needing to be passed data that is meaningful to it. Even JavaScript isn't that slippery. You can call sort() on an image (for example,) but there's no meaningful way to generate results.
You could do something similar by returning a special object that has all required methods on it, but can be used instead of the final value. For example, you could return an Array instance that has all these methods on it.
var getstuff = function () {
obj = Array.apply(this, arguments);
obj.take = function (n) {
return this[n];
};
obj.grep = function (regexp) {
return getstuff.apply(this, Array.prototype.filter.apply(this, [function (item) {
return item.toString().search(regexp) !== -1;
}]));
};
obj.splice = function () {
return getstuff.apply(this, Array.prototype.splice.apply(this, arguments));
}
return obj;
}
// shows [-8, 1]
console.log(getstuff(3, 1, 2, 'b', -8).sort().grep(/\d+/).splice(0, 2));
// shows 3
var stuff = getstuff(3, 1, 2, 'b', -8).grep(/\d+/);
console.log(stuff.sort()[stuff.length]);
Note that the above is not a particularly fast implementation, but it returns arrays with special methods by still keeping the global Allay's prototype clean, so it won't interfere with other code.
You could make it faster by defining these special methods on the Array.prototype, but you should be careful with that...
Or, if your browser supports subclassing Array, then all you need is a supclass and a handy constructor, getstuff().
I need to make a Javascript object that would behave as an associative array, but with some functions that are called before getting and setting properties.
For example, the task may be like this: we should make an object, that would contain a squared value of a key, like this:
obj.two should be equal to 4,
obj.four should be equal to 16,
obj['twenty one'] should be equal to 441.
This is an example. Actually I need to make setting operation overridden too. The getting and setting operations would go to the database, and they not necceserily would take strings as keys, but any types of objects, from which it would create a DB query.
How would I do that a) with as less thirdparty libraries as possible and b) to make it work on as much platforms as possible?
I am new to JS, I've found that JS has no associative arrays, relying on the ability to define objects on the fly with arbitrary properties. I googled and had an idea to use or even override lookupgetter (and setter), where define a new getter/setter on the fly, but I coundn't find if the interpreter would use this method every time it encounters new key. Anyway, it looks like I wouldn't be able to use anything except strings or maybe numbers as keys.
In Java, I would just implement java.util.Map.
Please help me, how would I do the same in Javascript?
edit
I think I will get what I want if I manage to override [[Get]] and [[Put]] methods mentioned here http://interglacial.com/javascript_spec/a-8.html#a-8.6.2.1
For your example, doesn't this do what you want:
var myObj = {};
myObj["two"] = 4;
myObj["four"] = 16;
myObj["twenty one"] = 441;
alert(myObj["four"]); // says 16
Or are you trying to say that the object should magically calculate the squares for you?
JavaScript object keys are strings. If you try to use a number as a key JavaScript basically converts it to a string first.
Having said that, you can use objects as keys if you define a meaningful toString method on them. But of course meaningful is something that happens on a case by case basis and only you will know what needs to be done for your case.
You can also define objects that maintain their own internal data structures which you access via object methods. I think explaining that is beyond the scope of this post. Google "javascript module pattern" for some pointers to get you started.
See http://ejohn.org/blog/javascript-getters-and-setters/
Also this particular answer: Javascript getters and setters for dummies?
edit
According to Does JavaScript have the equivalent of Python's __getattribute__? and Is there an equivalent of the __noSuchMethod__ feature for properties, or a way to implement it in JS? there is no nice way of accomplishing exactly what the OP wants. Getters and setters are not useful because you must know the name of what you're looking for in advance.
My recommendation would thus be to do something like:
var database = {}
database.cache = {}
database.get = function(key) {
// INSERT CUSTOM LOGIC to recognize "forty-two"
if (!(key in database.data))
database.cache[key] = fetch_data_from_database();
return database.cache[key];
}
database.put = function(key, value) {
database.cache[key] = value;
send_data_to_database(key, value);
}
I decided that the most correct way to implement this is to use Harmony:Proxies. It isn't working on all platforms, but it lets implement this in the most seamless way; and it may be supported in more platforms in the future.
This page contains an example that I used as a template to do what I want:
http://wiki.ecmascript.org/doku.php?id=harmony:proxies
I read a few questions and answers about javascript dictionary implementations, but they don't meet my requirements:
the dictionary must be able to take objects as keys
the values must be accessible by the []-operator
So I came up with the idea to overwrite the valueOf-method in Object.prototype, as follows:
Object.__id__ = 0;
Object.prototype.valueOf = function() {
if(!this.__id__)
this.__id__ = ++Object.__id__;
return "__id__" + this.__id__;
}
Object.prototype.toString = Object.prototype.valueOf;
//test
var x = {p1: "5"};
var y = [6];
var z = {};
z[x] = "7";
z[y] = "8";
console.log(z[x], z[y]);
I tested this with google-chrome and it seems to work well, but I'm a bit sceptical, whether this will cause some drawbacks, since it was so easy to implement.
Considering that the valueOf method is not used for other purposes in the whole code, do you think there are any disadvantages?
It's an interesting idea. I suggest my jshashtable. It meets your first requirement but not the second. I don't really see the advantage of insisting on using the square bracket property access notation: do you have a particular requirement for it?
With jshashtable, you can provide a hashing function to the Hashtable constructor. This function is passed an object to be used as a key and must return a string; you could use a function not dissimilar to what you have there, without having to touch Object.prototype.
There are some disadvantages to your idea:
Your valueOf method will show up in a for...in loop over any native object;
You have no way determining which keys should be considered equal, which is something you may want to do. Instead, all keys will be considered unique.
This won't work with host objects (i.e. objects provided by the environment, such as DOM elements)
It is an interesting question, because I had so far assumed that any object can be used as an index (but never tried with associative arrays). I don't know enough about the inner workings of JavaScript to be sure, but I'd bet that valueOf is used somewhere else by JavaScript, even if not in your code. You might run into seemingly inexplicable problems later. At least, I'd restrict myself to a new class and leave Object alone ;) Or, you explicitly call your hashing function, calling it myHash() or whatever and calling z[x.myHash()] which adds clutter but would let me, personally, sleep better ;) I can't resist thinking there's a more JavaScript-aware solution to this, so consider all of these ugly workarounds ;)
If you came upon this question looking for a JS dictionary where objects are keys look at Map Map vs Object in JavaScript