JavaScript best practices, extending natives with *statics* - javascript

Some people will tell you that adding prototypes to JavaScript natives is evil. For example:
String.prototype.format = function(format, replacements) {
...
};
Now, for those that agree with that (if you don't, do not reply with answer—your opinion is N/A; this is not a discussion about prototypes), is adding static methods to natives equally as evil? (Hitherto and henceforth, "static" meaning simply a method whose context isn't an instance.)
For example, given that creating a String.prototype.format is evil, is adding it as a static an acceptable practice?
String.format = function(format, replacements) {
...
};
How is extending a native with a static method any different, concerning best-practices, than extending a native with a prototype? Either you're against extending natives in any way, or you're not—is there anyone in the camp that static extensions are acceptable while prototypal are not?

Ask yourself why extending natives is evil.
Common reasons are
Not future-proof, what if the standards say "There shall be a String.format"
Not past-proof, adding enumerable properties to prototypes will break bad code.
may lead to confusion as to what's common and what's standard
may break bad-code (duck-typing, looks like a duck, quaks like a --- throws exception :()
It's simply a matter of weighing up how much you value those reasons. I only care about #1 (future-proofing).

Adding a static method to a native Constructor will not likely have an unexpected impact on someone else's code or the time it takes to construct objects using said Constructor.
However, when you add a prototype method to a native Constrcutor, every instance (even the ones created to do operations like "test".indexOf("t")) will have the additional overhead of your method. Iterating over object properties or testing capabilities (since we often can't judge an object by its type) gets more difficult.
Let's say you add String.prototype.forEach in your code. That will leak into every module. Now when some other code tests for the forEach method (thinking it's an array in a modern browser), they'll get a string instead--evil.

Related

Why aren't methods of an object created with class bound to it in ES6?

I like ES6 classes but I can't understand why I have to bind methods in the constructor:
constructor() {
this.someMethod = this.someMethod.bind(this)
}
I need to do this almost for any method.
Is this a real limitation or am I missing something? What is the reason behind this? I know that classes in JS are only syntactic sugar but this could have been part of them.
Quoting Mark Miller's answer to the linked esdiscuss post here:
Several of the early class proposals did so, as they were starting with the semantics of es5 objects-as-closures and classes as compositions-of-instance-traits.
doku.php?do=search&id=traits
The idea was that language support would make this semantics efficient, avoiding the need to eagerly allocate a closure per method per instance.
However, for reasons I understand, these failed to gain traction. Instead, we moves towards sugar for the dominant es5 pattern of encoding classes into prototype inheritance. Initially, we tried to have this purely be sugar, so that people could painlessly refactor code in that dominant pattern into classes.
As we wrestled with the detailed semantics around super and construction, es6 classes deviated from being pure sugar. But this deviation only prevents painless refactoring from es6 classes into the dominant es5 pattern. Practically, it remains painless to refactor from the es5 pattern into es6 classes.
At zenparsing/es-function-bind#17 we realized
we could still have had methods bind on extraction -- accounting for the behavior by decreeing that methods are installed on the prototype as accessors whose getter binds. However, this realization came too late for es6. Since it would have made the refactoring into classes more hazardous -- more of a semantic change -- it is not clear it would have flown even if we had thought of it in time. Instead, under all variations of the decorator designs, one can write such a decorator so that decorated methods are bind-on-extraction, by explicitly creating this accessor property. However(!), if implemented as a user-land decorator, this has much worse performance than objects-as-closures!! Objects-as-closures have higher allocation cost when allocating the object.
jsperf.com/creating-stateful-objects
But are quite efficient at using the object once the object is created:
jsperf.com/strict-where-state
(Note that jsperf is misidentifying Edge 28.14257.1000.0 as Chrome 46.0.2486. This is worth noting because Edge uses the transposed representation for WeakMaps, and so WeakMap-based usage of private state has much less penalty on Edge. Though this is besides the point of this thread.)
To make a decorator for binding-on-extraction efficient, an implementation would need some kind of special case somewhere to avoid the allocation when the method is being immediately invoked, rather than being observably extracted. The only thing TC39 needs to do to enable this is to standardize such a decorator so that implementations can provide it as a builtin that they recognize.
And Kevin Smith's answer:
In general, there is often a tension between making the language "better" (for some subjective value system) and maintaining consistency. I think maintaining consistency was the right call in this case.
That said, the public class fields proposal will allow you to define instance methods as
class Foo {
someMethod = () => {
// do stuff
}
}
(instead of doing the same in the constructor).

how does prototype differ from oop [duplicate]

In JavaScript, every object is at the same time an instance and a class. To do inheritance, you can use any object instance as a prototype.
In Python, C++, etc.. there are classes, and instances, as separate concepts. In order to do inheritance, you have to use the base class to create a new class, which can then be used to produce derived instances.
Why did JavaScript go in this direction (prototype-based object orientation)? what are the advantages (and disadvantages) of prototype-based OO with respect to traditional, class-based OO?
There are about a hundred terminology issues here, mostly built around someone (not you) trying to make their idea sound like The Best.
All object oriented languages need to be able to deal with several concepts:
encapsulation of data along with associated operations on the data, variously known as data members and member functions, or as data and methods, among other things.
inheritance, the ability to say that these objects are just like that other set of objects EXCEPT for these changes
polymorphism ("many shapes") in which an object decides for itself what methods are to be run, so that you can depend on the language to route your requests correctly.
Now, as far as comparison:
First thing is the whole "class" vs "prototype" question. The idea originally began in Simula, where with a class-based method each class represented a set of objects that shared the same state space (read "possible values") and the same operations, thereby forming an equivalence class. If you look back at Smalltalk, since you can open a class and add methods, this is effectively the same as what you can do in Javascript.
Later OO languages wanted to be able to use static type checking, so we got the notion of a fixed class set at compile time. In the open-class version, you had more flexibility; in the newer version, you had the ability to check some kinds of correctness at the compiler that would otherwise have required testing.
In a "class-based" language, that copying happens at compile time. In a prototype language, the operations are stored in the prototype data structure, which is copied and modified at run time. Abstractly, though, a class is still the equivalence class of all objects that share the same state space and methods. When you add a method to the prototype, you're effectively making an element of a new equivalence class.
Now, why do that? primarily because it makes for a simple, logical, elegant mechanism at run time. now, to create a new object, or to create a new class, you simply have to perform a deep copy, copying all the data and the prototype data structure. You get inheritance and polymorphism more or less for free then: method lookup always consists of asking a dictionary for a method implementation by name.
The reason that ended up in Javascript/ECMA script is basically that when we were getting started with this 10 years ago, we were dealing with much less powerful computers and much less sophisticated browsers. Choosing the prototype-based method meant the interpreter could be very simple while preserving the desirable properties of object orientation.
A comparison, which is slightly biased towards the prototypes based approach, can be found in the paper Self: The Power of Simplicity. The paper makes the following arguments in favor of prototypes:
Creation by copying. Creating new objects from prototypes is accomplished by a simple operation, copying, with a simple biological
metaphor, cloning. Creating new objects from classes is accomplished
by instantiation, which includes the interpretation of format
information in a class. Instantiation is similar to building a house
from a plan. Copying appeals to us as a simpler metaphor than
instantiation.
Examples of preexisting modules. Prototypes are more concrete than classes because they are examples of objects rather than descriptions
of format and initialization. These examples may help users to reuse
modules by making them easier to understand. A prototype-based system
allows the user to examine a typical representative rather than
requiring him to make sense out of its description.
Support for one-of-a-kind objects. Self provides a framework that can easily include one-of-a-kind objects with their own behavior.
Since each object has named slots, and slots can hold state or
behavior, any object can have unique slots or behavior. Class-based
systems are designed for situations where there are many objects with
the same behavior. There is no linguistic support for an object to
possess its own unique behavior, and it is awkward to create a class that is guaranteed to have only one
instance [think singleton
pattern]. Self suffers from neither of these disadvantages. Any object
can be customized with its own behavior. A unique object can hold the
unique behavior, and a separate "instance" is not needed.
Elimination of meta-regress. No object in a class-based system can be self-sufficient; another object (its class) is needed to express
its structure and behavior. This leads to a conceptually infinite
meta-regress: a point is an instance of class Point, which is an
instance of metaclass Point, which is an instance of metametaclass
Point, ad infinitum. On the other hand, in prototype-based systems
an object can include its own behavior; no other object is needed to
breathe life into it. Prototypes eliminate meta-regress.
Self is probably the first language to implement prototypes (it also pioneered other interesting technologies like JIT, which later made its way into the JVM), so reading the other Self papers should also be instructive.
You should check out a great book on JavaScript by Douglas Crockford. It provides a very good explanation of some of the design decisions taken by JavaScript creators.
One of the important design aspects of JavaScript is its prototypal inheritance system. Objects are first class citizens in JavaScript, so much that regular functions are also implemented as objects ('Function' object to be precise). In my opinion, when it was originally designed to run inside a browser, it was meant to be used to create lots of singleton objects. In browser DOM, you find that window, document etc all singleton objects. Also, JavaScript is loosely typed dynamic language (as opposed to say Python which is strongly typed, dynamic language), as a result, a concept of object extension was implemented through the use of 'prototype' property.
So I think there are some pros for prototype-based OO as implemented in JavaScript:
Suitable in loosely typed environments, no need to define explicit types.
Makes it incredibly easy to implement singleton pattern (compare JavaScript and Java in this regard, and you'll know what I am talking about).
Provides ways of applying a method of an object in the context of a different object, adding and replacing methods dynamically from an object etc. (things which are not possible in a strongly typed languages).
Here are some of the cons of prototypal OO:
No easy way of implementing private variables. Its possible to implement private vars using Crockford's wizardry using closures, but its definitely not as trivial as using private variables in say Java or C#.
I don't know how to implement multiple inheritances (for what its worth) in JavaScript yet.
The difference between mainstream OOP class-based languages such as c# or java and prototype bases languages such as javascript is the ability to modify object types at runtime whilst in c# or java they gave up this ability in favor of static type checking by making classes fixed at compile time. JS has always been closer to the first original design of OOP of alan Kay and languages such as Smalltalk or simula.
this is achieved by making the blueprint itself an instance, types in prototype-based are actual instances that can be accessed and modified at runtime, in Javascript this is really easy using the prototype object, since every object type has this object.
example: type funcName.prototype.myNewMethod= function{ console.log("hello world")}

The dangers of overwriting JavaScript object and functions

The nature of JavaScript allows for its native objects to be completely re-written. I want to know if there is any real danger in doing so!
Here are some examples of native JavaScript objects
Object
Function
Number
String
Boolean
Math
RegExp
Array
Lets assume that I want to model these to follow a similar pattern that you might find in Java (and some other OOP languages), so that Object defines a set of basic functions, and each other object inherits it (this would have to be explicitly defined by the user, unlike Java, where everything naturally derives from object)
Example:
Object = null;
function Object() {
Object.prototype.equals = function(other) {
return this === other;
}
Object.prototype.toString = function() {
return "Object";
}
Object.equals = function(objA, objB) {
return objA === objB;
}
}
Boolean = null;
function Boolean() {
}
extend(Boolean, Object); // Assume extend is an inheritance mechanism
Foo = null;
function Foo() {
Foo.prototype.bar = function() {
return "Foo.bar";
}
}
extend(Foo, Object);
In this scenario, Object and Boolean now have new implementations. In this respect, what is likely to happen? Am I likely to break things further down the line?
Edit:
I read somewhere that frameworks such as MooTools and Prototype have a similar approach to this, is this correct?
Monkey patching builtin classes like that is a controversial topic. I personally don't like doing that for 2 reaons:
Builtin classes are a global scope. This means that if two different modules try to add methods with the same name to the global classes then they will conflict, leading to subtle bugs. Even more subtly, if a future version of a browsers decides to implement a method with the same name you are also in trouble.
Adding things to the prototypes of common classes can break code that uses for-in loops without a hasOwnProperty check (people new to JS often do that to objects and arrays, since for-in kind of looks like a foreach loop). If you aren't 100% sure that the code you use is using for-in loops safely then monkeypatching Object.prototype could lead to problems.
That said, there is one situation where I find monkeypatching builtins acceptable and that is adding features from new browsers on older browsers (like, for example, the forEach method for arrays). In this case you avoid conflicts with future browser versions and aren't likely to catch anyone by surprise. But even then, I would still recommend using a shim from a third party instead of coding it on your own, since there are often many tricky corner cases that are hard to get right.
There's some level of preference here, but my personal take is that this sort of thing has the potential to become a giant intractable mess.
For example, you start with two projects, A and B, that each decide to implement all sorts of awesome useful fluent methods on String.
Project A has decided that String needs an isEmpty function that returns true if a string is zero-length or is only whitespace.
Project B has decided that String needs an isEmpty function that returns true if a string is zero-length, and an isEmptyOrWhitespace function that returns true if a string is zero-length or is only whitespace.
Now you have a project that wants to use some code from Project A and some code from Project B. Both of them make extensive use of their custom isEmpty functions. Do you have any chance of successfully joining the two? Probably not. You are in a cluster arrangement, so to speak.
Note that this is all very different than extension methods in C#, where you at least have to import the containing static class's namespace to get the extension method, there's no runtime conflict, and could reasonably consume from A and B in the same project as long as you didn't import their extensions namespace (hoping that they had the foresight to put their extension classes in a separate namespace for exactly this reason).
The worst case in JS that I know of along these lines is undefined. You can define it.
You're allowed to do things like undefined = 'blah';.... at which point, you can no longer rely on if(x === undefined). Which could easily break something elsewhere in your code (or, of course, in a third party lib you may be using).
That's completely bonkers, but definitely shows the definitely dangers of arbitrarily overwriting built-in objects.
See also: http://wtfjs.com/2010/02/15/undefined-is-mutable
For a slightly more sane example, take the Sahi browser testing tool. This tool allows you to write automated scripts for the browser to test your site. (similar to Selenium). One problem with doing that is if your site uses alert() or confirm(), the script would stop running while it waits for user input. Sahi gets around this by overwriting these functions with its own stub functions.
I avoid overriding the default behavior of the inherent objects. It's biten me a few times, while others I was fine. A library you can look at for an example is Sugar.js. Its a great library that some folks love, but I generally avoid it simply because it extends the behavior of existing JavScript objects, such as what you are doing.
I think however that you will find that this is purely opinion and style.

Revisiting extending native prototypes after ECMAScript 5

Recently, given the changes to defining properties in ECMAScript 5, I have revisited the question of whether we can safely extend the native JavaScript prototypes. In truth, all along I have extended prototypes like Array and Function, but I avoided doing so with Object, for the obvious reasons. In unit testing with Jasmine, by adding Object.prototype specs to specs for my own personal framework, extending Object.prototype with non-enumerable functions has appeared to be safe. Data properties like a "type" property, however, with getters/setters that do any unusual processing have had unintended consequences. There is still the possibility of conflicts with other libraries--though in my work, that hardly ever comes up. Nevertheless, as long as the functions are not enumerable, it looks like extending Object.prototype can be safe.
What do you think? Is it safe to extend Object.prototype now? Please discuss.
Extending objects native to JavaScript might become a little safer, through many collision concerns still stand. Generally, unless you're extending object to support standartized behavior from more recent standard it really would be still much safer to introduce wrapper - it is much easier to do things right way when you're the only one in control.
Speaking of objects native to environment (DOM elements and nodes, AJAX stuff), new JS standard still don't give and, arguably, can't give you any guarantee about any interaction with those except what defined in their interface standard. Never forget that they're potentially accessible through many different scripting engines and thus not need to be tailored for quirks of one specific language - JS. So recommendation to not extend those either still stands as well.
The definitive, absolute answer is ...
"It depends." :)
Extending any built in JavaScript object can be perfectly safe or it can be a complete disaster. It depends on what you are doing and how you are doing it.
Use smart practices and common sense and test the hell-out-of-it.

Should I be using prototypes if I don't require inheritance or optimization

If my site doesn't require any sort of optimization and my object structure is fairly flat and so there is no need for inheritance, should I be using prototypes at all:
MyObject.prototype.<something>
I refer to this question Javascript when to use prototypes and various writings by John Resig who seems to focus on the performance values of prototype.
I find using the module pattern to define objects and their functions easier to read and manage and as michielvoo mentions, premature optimization tends to cause more harm than good.
Are there any other benefits or uses cases for prototype?
It's a code organization pattern.
There are two good ways to share methods across objects
var oneWay = Object.create(bagOfMethods);
// some value of extend that is sensible
var otherWay = extend({}, bagOfMethods);
The only real advantage prototypes gives is that the link between oneWay and bagOfMethods is live.
Having a live link allows really powerful extensions and monkey patching.
An example is Object.prototype.methodNowLivesOnAllInstances = function () { };
This is a way of doing "asynchronous" programming, you can extend the "class" without worrying whether all instances get the new method.
A note on the modular pattern, it's bloated and not needed. Use a module builder like modul8 or browserify instead.
Well, if you refer to that question, the accepted answer for which starts out by calling it an optimization, then I'd say don't go for premature optimization. You may never reap any benefits (see: YAGNI) and you make your job more difficult, since you yourself (and your team?) prefer the module pattern.

Categories

Resources