In Ruby, you can capture a call to a method which is missing and define it on the fly.
What I wanna accomplish in JavaScript is to have an object with no methods. I want a missing method to be translated into a call to emit():
app.isReady() -> app.emit("isReady")
soldier.kills() -> soldier.emit("kills")
I think it's better to capture the missing method error and run emit(methodName) rather than defining all methods (from a fixed list) at runtime. That way we don't have performance overhead if there are hundreds or thousands of events for an object.
What is the best way to do this?
UPDATE: This is an API design so I rather stay out of:
try {
app.isReady()
} catch(e) {
...
}
I want to know how I can accomplish this behind the scenes so the users can use methods as usual.
In that way we don't have a performance overhead if there are hundreds/thousands of events for an object.
I think it's a massive misconception to think the performance overhead of adding methods to an object is smaller then the performance overhead of converting method invocations into emit calls.
However you cannot implement this feature in ES5
One could however implement this using Harmony proxies.
I recommend looking at simulating __noSuchMethod__.
I believe ES6 proxies are experimental and can be turned on in V8 so you could use them with node.js today.
It's not possible to do that consistently at this stage, unless you can guarantee your app will only run on Mozilla, in which case noSuchMethod is what you're after.
As far as I know, none of the other browsers implement this yet.
Use a RegExp test on the function pointer by using the following process:
pass the object literal as an argument
pass the name of the default method as a string
pass the name of the fallback method as a string
using subscript notation to dereference the function pointer
use a regexp to check the type against the name function
if it succeeds, call the default using subscript notation
if it fails, call the fallback using subscript notation
For example:
/* Define mapping */
var app = {"emit": emit};
/* Define interface */
function emit(){}
function \u1000missing(object, method, fallback)
{
/* Existence check */
if (/function/.test(object[method]) )
{
object[method]();
}
/* reify */
else
{
object[fallback](method)
}
}
\u1000missing(app,"isReady","emit")
You might ask why you'd ever want to use subscript notation. Subscript notation allows for dynamic creation of properties and methods. So if you ever do any sort of metaprogramming, you'll most likely be using the subscript notation.
References
Are there equivalents to Ruby's method_missing in other languages?
JSPerf: Method Missing
Handling a call to a missing method in different languages
jQuery is a Monad
Message forwarding in Smalltalk
Performance from Aligning Smalltalk and JavaScript Classes
Can Perl be Smalltalk?
How learning Smalltalk can make you a better developer
Reflective Facilities in Smalltalk-80
12.1. QMetaObject - The Meta Object Pattern
Object Programming and Object-Oriented Programming
Things you should know about JavaScript
Respond to an Unknown Method Call
Creating Thread-Safe Components With OnMissingMethod()
On the design of the ECMAScript Reflection API (pdf)
Does Not Understand
I've create a Node.js package to deal with your situation. It's called auto-object.
Here's a glance:
const myObject = autoObject.createObject(function(name) {
if(name.startsWith("say")) {
return function() {
return name.substr(3);
}
}
return name;
});
myObject.foo; ///< "foo"
myObject.sayFoo(); ///< "Foo"
myObject.sayBar(); ///< "Bar"
What's more, this works with class too.
Related
Google JavaScript Style Guide advises against extending the Array.prototype.
However, I used Array.prototype.filter = Array.prototype.filter || function(...) {...} as a way to have it (and similar methods) in browsers where they do not exist. MDN actually provides similar example.
I am aware about Object.prototype issues, but Array is not a hash table.
What issues may arise while extending Array.prototype that made Google advise against it?
Most people missed the point on this one. Polyfilling or shimming standard functionality like Array.prototype.filter so that it works in older browsers is a good idea in my opinion. Don't listen to the haters. Mozilla even shows you how to do this on the MDN. Usually the advice for not extending Array.prototype or other native prototypes might come down to one of these:
for..in might not work properly
Someone else might also want to extend Array with the same function name
It might not work properly in every browser, even with the shim.
Here are my responses:
You don't need to use for..in on Array's usually. If you do you can use hasOwnProperty to make sure it's legit.
Only extend natives when you know you're the only one doing it OR when it's standard stuff like Array.prototype.filter.
This is annoying and has bit me. Old IE sometimes has problems with adding this kind of functionality. You'll just have to see if it works in a case by case basis. For me the problem I had was adding Object.keys to IE7. It seemed to stop working under certain circumstances. Your mileage may vary.
Check out these references:
http://perfectionkills.com/extending-native-builtins/
http://blip.tv/jsconf/jsconf2011-andrew-dupont-everything-is-permitted-extending-built-ins-5211542
https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/filter
https://github.com/kriskowal/es5-shim
Good luck!
I'll give you the bullet points, with key sentences, from Nicholas Zakas' excellent article Maintainable JavaScript: Don’t modify objects you don’t own:
Dependability: "The simple explanation is that an enterprise software product needs a consistent and dependable execution environment to be maintainable."
Incompatible implementations: "Another peril of modifying objects that you don’t own is the possibility of naming collisions and incompatible implementations."
What if everyone did it?: "Simply put: if everyone on your team modified objects that they didn’t own, you’d quickly run into naming collisions, incompatible implementations, and maintenance nightmares."
Basically, don't do it. Even if your project is never going to be used by anyone else, and you're never going to import third party code, don't do it. You'll establish a horrible habit that could be hard to break when you start trying to play nice with others.
As a modern update to Jamund Ferguson's answer:
Usually the advice for not extending Array.prototype or other native prototypes might come down to one of these:
for..in might not work properly
Someone else might also want to extend Array with the same function name
It might not work properly in every browser, even with the shim.
Points 1. and 2. can now be mitigated in ES6 by using a Symbol to add your method.
It makes for a slightly more clumsy call structure, but adds a property that isn't iterated over and can't be easily duplicated.
// Any string works but a namespace may make library code easier to debug.
var myMethod = Symbol('MyNamespace::myMethod');
Array.prototype[ myMethod ] = function(){ /* ... */ };
var arr = [];
// slightly clumsier call syntax
arr[myMethod]();
// Also works for objects
Object.prototype[ myMethod ] = function(){ /* ... */ };
Pros:
For..in works as expected, symbols aren't iterated over.
No clash of method names as symbols are local to scope and take effort to retrieve.
Cons:
Only works in modern environments
Slightly clunky syntax
Extending Array.prototype in your own application code is safe (unless you use for .. in on arrays, in which case you need to pay for that and have fun refactoring them).
Extending native host objects in libraries you intend others to use is not cool. You have no right to corrupt the environment of other people in your own library.
Either do this behind an optional method like lib.extendNatives() or have [].filter as a requirement.
Extending Natives and Host Objects
Prototype does this. It's evil. The following snippet demonstrates how doing so can produce unexpected results:
<script language="javascript" src="https://ajax.googleapis.com/ajax/libs/prototype/1.7.0.0/prototype.js"></script>
<script language="javascript">
a = ["not", "only", "four", "elements"];
for (var i in a)
document.writeln(a[i]);
</script>
The result:
not only four elements function each(iterator, context) { var index = 0; . . .
and about 5000 characters more.
I want to add an additional answer that allows extending the Array prototype without breaking for .. in loops, and without requiring use of hasOwnPropery:
Don't use this bad approach which causes prototype values to appear in for .. in:
Array.prototype.foo = function() { return 'foo'; };
Array.prototype.bar = function() { return 'bar'; };
let a = [ 1, 2, 3, 4 ];
console.log(`Foo: ${a.foo()}`);
console.log(`Bar: ${a.bar()}`);
console.log('==== Enumerate: ====');
for (let v in a) console.log(v);
Instead use Object.defineProperty, with enumerable: false - it exists for pretty much exactly this reason!
Object.defineProperty(Array.prototype, 'foo', {
value: function() { return 'foo'; },
enumerable: false
});
Object.defineProperty(Array.prototype, 'bar', {
value: function() { return 'bar'; },
enumerable: false
});
let a = [ 1, 2, 3, 4 ];
console.log(`Foo: ${a.foo()}`);
console.log(`Bar: ${a.bar()}`);
console.log('==== Enumerate: ====');
for (let v in a) console.log(v);
Note: Overall, I recommend avoiding enumerating Arrays using for .. in. But this knowledge is still useful for extending prototypes of classes where enumeration is appropriate!
Some people use for ... in loops to iterate through arrays. If you add a method to the prototype, the loop will also try to iterate over that key. Of course, you shouldn't use it for this, but some people do anyway.
You can easily create somekind of sandbox with poser library.
Take a look on https://github.com/bevacqua/poser
var Array2 = require('poser').Array();
// <- Array
Array2.prototype.eat = function () {
var r = this[0];
delete this[0];
console.log('Y U NO .shift()?');
return r;
};
var a = new Array2(3, 5, 7);
console.log(Object.keys(Array2.prototype), Object.keys(Array.prototype))
I believe this question deserves an updated ES6 answer.
ES5
First of all, as many people have already stated. Extending the native prototypes to shim or polyfill new standards or fix bugs is standard practice and not harmful. For example if a browser doesn't support the .filter method if (!Array.prototype.filter) you are free to add this functionality on your own. In-fact, the language is designed to do exactly this to manage backwards compatibility.
Now, you'd be forgving for thinking that since JavaScript object use prototypal inheritance, extending a native object like Array.prototype without interfering should be easy, but up until ES6 it's not been feasible.
Unlike objects for example, you had to rely and modifying the Array.prototype to add your own custom methods. As others have pointed out, this is bad because it pollutes the Global namespace, can interfere with other code in an unexpected way, has potential security issues, is a cardinal sin etc.
In ES5 you can try hacking this but the implementations aren't really practically useful. For more in depth information, I recommend you check out this very informative post: http://perfectionkills.com/how-ecmascript-5-still-does-not-allow-to-subclass-an-array/
You can add a method to an array, or even an array constructor but you run into issues trying to work with the native array methods that rely on the length property. Worst of all, these methods are going to return a native Array.prototype and not your shiny new sub-class array, ie: subClassArray.slice(0) instanceof subClassArray === false.
ES6
However, now with ES6 you can subclass builtins using class combined with extends Array that overcomes all these issues. It leaves the Array.prototype intact, creates a new sub-class and the array methods it inherits will be of the same sub-class! https://hacks.mozilla.org/2015/08/es6-in-depth-subclassing/
See the fiddle below for a demonstration:
https://jsfiddle.net/dmq8o0q4/1/
Extending the prototype is a trick that only works once. You do and you use a library that also does it (in an incompatible way) and boom!
The function you are overriding could be used by the internal javascript calls and that could lead to unexpected results. Thats one of the reasons for the guideline
For example I overrode indexOf function of array and it messed up accessing array using [].
Often, we are presented with an array (IEnumerable) property that specific values need to be extracted. in c# we can do something similar to:
public AssetModel PromoImage {
get
{
return Assets.FirstOrDefault(x => x.AssetTypeCd == "promoimage");
}
private set { }
}
Is there a way to easily to this within Angular 2?
Lodash provides similar functionality to LINQ for JavaScript programs (and then some), though not deferred execution -- LINQ queries are deferred until they are enumerated, while lodash (usually) performs the query immediately and returns an array/object of results. (Though in this case LINQ wouldn't even defer it since FirstOrDefault returns a scalar and not a queryable/enumerable.)
In your case, you would do something like this:
let obj = {
get promoImage() {
return _.find(assets, a => a.assetTypeCd === 'promoimage');
},
// ...
};
Then accessing obj.promoImage will execute the function to obtain the attribute's value.
(Here I assume this is where we are creating the new object, and assets is the assets list in the lexical scope of the constructor function. You can change it to reference this if you are storing data on the object itself and not in constructor upvalues.)
Notes:
Lodash does not depend on Angular at all.
ES6 provides a find() method on the Array prototype, so this feature will be built-in to browsers once ES6 is adopted. Sadly, IE is (as usual) the outlier without any support for it. However, Lodash is still a very useful library to have in your toolkit, and note that Lodash's find() works on objects too, not just arrays.
For example I loaded a script on some website, and I would like to know if JSON.parse/stringify wasn't monkey patched.
I noticed that if I use toString on the function in Chrome/FF, JSON.stringify.toString, then I get back:
function stringify() {
[native code]
}
My question is do you think this is a good way to verify if a function was monkey patched? Also would love to hear of any other approaches to this problem.
One could easily fake JSON.stringify.toString
JSON.stringify = function() {}
JSON.stringify.toString = function() {return 'ha-ha'}
console.log(JSON.stringify); //ha-ha
A little more robust way would be to use Function.prototype.toString
Function.prototype.toString.call(JSON.stringify)
But really bad monkeypatcher could patch Function.prototype.toString as well :)
Yes, this is the only practical way to check whether or not a native function had been overridden or not.
const isNative = fn => !!fn.toString().match(/\[native code\]/)
console.log(isNative(JSON.stringify));
A more robust solution could use Function.prototype.toString() instead of direct call of fn.toString(), but both are monkeypatchable as well. The joys of JavaScript :)
The spec ( http://www.ecma-international.org/ecma-262/7.0/index.html#sec-function.prototype.tostring ) does not specify the exact string returned for a builtin function :
19.2.3.5 Function.prototype.toString
When the toString method is called on an object func, the following
steps are taken:
If func is a Bound Function exotic object, then Return an
implementation-dependent String source code representation of func.
The representation must conform to the rules below. It is
implementation dependent whether the representation includes bound
function information or information about the target function. If
Type(func) is Object and is either a built-in function object or has
an [[ECMAScriptCode]] internal slot, then Return an
implementation-dependent String source code representation of func.
The representation must conform to the rules below. Throw a TypeError
exception. toString Representation Requirements:
The string representation must have the syntax of a
FunctionDeclaration, FunctionExpression, GeneratorDeclaration,
GeneratorExpression, ClassDeclaration, ClassExpression, ArrowFunction,
MethodDefinition, or GeneratorMethod depending upon the actual
characteristics of the object. The use and placement of white space,
line terminators, and semicolons within the representation String is
implementation-dependent. If the object was defined using ECMAScript
code and the returned string representation is not in the form of a
MethodDefinition or GeneratorMethod then the representation must be
such that if the string is evaluated, using eval in a lexical context
that is equivalent to the lexical context used to create the original
object, it will result in a new functionally equivalent object. In
that case the returned source code must not mention freely any
variables that were not mentioned freely by the original function's
source code, even if these “extra” names were originally in scope. If
the implementation cannot produce a source code string that meets
these criteria then it must return a string for which eval will throw
a SyntaxError exception.
So checking for [Native Code] may or may not work depending on the interpreter. Furthermore, an implementation could well implement builtin functions as normal javascript code.
So in answer to your question, you cannot determine, is a Javascript specified way whether a builtin function has been monkey-patched.
That said it appears that Chrome and Firefox both return the [Native Code] string subject to verification on other implementations that may be a pragmatic solution.
I just wanted to add that, after ES6, all solutions that involve checking "[native code]" are even less reliable because of ES6 proxy traps.
// Example monkey-patching the Fetch API using an ES6 proxy trap
window.fetch = new Proxy(window.fetch, {
apply(fetch, that, args) {
const result = fetch.apply(that, args);
result.then((response) => {
console.log("Intercepted!", args, response);
});
return result;
}
});
// True
console.log(window.fetch.toString().includes("[native code]"));
// True
console.log(Function.prototype.toString.call(window.fetch).includes("[native code]"));
For more info, check this answer.
I tried to develop some of the ideas from other replies into a working script - here it is:
https://gist.github.com/mindplay-dk/767a5313b0052d6daf2b135fdecd775f
Paste it into the Chrome (or Edge) console and press ENTER - it'll print out a list of any constructors and class-methods not matching their native counterparts. (It does this by comparing against the native APIs in an iframe - which it creates via document.createElement, so, technically, it's possible to fool it by overriding that method, if you were intending to do so deliberately; this isn't a security tool.)
Note that this currently gives false positives for window.location, window.fetch and window.length - this appears to be because these properties aren't correctly reflected by their native browser implementations? If you know how to fix it, please post a comment.
Here is example output from a site that was incorrectly loading some IE11 polyfills into Chrome:
The MDN gives the following working example of Symbol.species:
class MyArray extends Array {
// Overwrite species to the parent Array constructor
static get [Symbol.species]() { return Array; }
}
var a = new MyArray(1,2,3);
var mapped = a.map(x => x * x);
console.log(mapped instanceof MyArray); // false
console.log(mapped instanceof Array); // true
The ECMAScript 2015 specifications says:
A function valued property that is the constructor function that is used to create derived objects.
The way I understand it it would be mainly used to Duck-Type in some way custom objects in order to trick the instanceof operator.
It seems very useful but I have not managed to use it on plain objects (FF 44.0a2):
let obj = {
get [Symbol.species]() {
return Array
}
}
console.log(obj instanceof Array) //false =(
console.log(obj instanceof Object) //true
Is there any way to use Symbol.species on plain objects in order to trick the instanceof operator?
The way I understand it it would be mainly used to Duck-Type in some way custom objects in order to trick the instanceof operator.
Nope, that's what Symbol.hasInstance is good for (though it makes tricky constructors, e.g. for mixins, not tricky instances).
The point of Symbol.species is to let built-in methods determine the proper type for derived objects. Whenever a function is supposed to return a new instance of the same kind, it usually instantiates a new this.constructor, which might be a subclass of the class that defined the method. But you might not always want this when subclassing, and that's where Symbol.species comes in.
The example given by MDN is quite good, you just must not miss the distinction between a and mapped:
var a = new MyArray(1,2,3);
var mapped = a.map(x => x * x);
Object.getPrototypeOf(a) == MyArray.prototype; // true
Object.getPrototypeOf(mapped) == Array.prototype; // true
(As you know, instanceof is was just sugar for the reverse of isPrototypeOf)
So what happens here is that the Array map method is called on an instance of MyArray, and creates a derived object that is now an instance of Array - because a.constructor[Symbol.species] said so. Without it, map would have created another MyArray.
This trick would work with plain objects just as well:
var b = {
length: 0,
map: Array.prototype.map,
constructor: { // yes, usually a function
[Symbol.species]: MyArray
}
};
var mapped = b.map(x => x*x);
console.log(mapped instanceof MyArray); // true
I can't say whether it's any useful in this example, though :-)
Good question. I admit that I'm not 100% sure, but I've gotten the impression that the answer is no.
I used two methods to determine this answer:
Finding an environment that clearly supports all of the features necessary to test this, and running the code in that environment directly
Referencing the spec
The first point wasn't easy to test, and I wasn't entirely sure that I successfully did it. You used Firefox 44, but I decided to test another environment due to the fact that Kangax's support table says it only supports the existence of Symbol.species, and none of the Array-like features. To see this, you can expand the Symbol.species section in the link above.
In this case, even Kangax is a bit confusing. I would expect there to be non-Array specific tests for Symbol.species aside from the existence test, but maybe I'm just not knowledge enough to understand that those tests cover all of the functionality of Symbols. I'm not sure!
While browsing the table, I saw that Edge supports all of the Symbol.species features. I loaded up my Windows VM, hopped on over to Edge 20, and pasted your code.
In Edge 20, the code had the same result as your test in Firefox 44.
So this began to give me confidence that the feature doesn't work in the way that you want it to. Just to make sure that Edge had its JavaScript in order, I decided to run the code from the MDN article you linked, and...I got a syntax error! It appears Edge is still updating its Class implementation due to late-in-the-game changes to the Class spec, so it's not enabled.
Their current implementation is, however, available through an experimental flag. So I flipped that on and was able to reproduce the result that MDN got.
And that was the extent of my JavaScript environment testing. I don't have 100% confidence in this test, but I would say that I'm fairly confidence in the result.
So onto the second method I used to determine my result: the specification.
I admit that I'm no expert at the ECMAScript specification. I do my best to understand it, but it is likely that I might misinterpret some of the information in there. The reason for this is because I'm not an ECMAScript master, so some of the technical jargon used is just way over my head.
Nevertheless, I can sometimes get the gist of what is being said. Here's a snippet that's about the Symbol ##species:
Methods that create derived collection objects should call ##species to determine the constructor to use to create the derived objects. Subclass constructor may over-ride ##species to change the default constructor assignment.
The phrase "derived objects" is used quite frequently in describing this symbol, but it's not defined anywhere. One might interpret it to be a synonym of sorts with instantiated. If that's a correct interpretation, then this can likely only be used with constructors or classes.
Either way, it sounds as if ##species is an active Symbol, rather than a passive one. By this I mean that it sounds like it's a function that's actually called during the creation of an Object, which is then what determines the instanceof value, rather than a function that's called at the time of instanceof being called.
But anyway, take that interpretation with a grain of salt. Once again, I'm not 100% certain of any of this. I considered posting this as a comment rather than an answer, but it ended up getting quite long.
Edit: I found this interesting library which looks like it can do exactly what I was describing at the bottom: https://github.com/philbooth/check-types.js
Looks like you can do it by calling check.quacksLike.
I'm fairly new to using javascript and I'm loving the amount of power it offers, but sometimes it is too flexible for my sanity to handle. I would like an easy way to enforce that some argument honors a specific interface.
Here's a simple example method that highlights my problem:
var execute = function(args)
{
executor.execute(args);
}
Let's say that the executor expects args to have a property called cmd. If it is not defined, an error might be caught at another level when the program tries to reference cmd but it is undefined. Such an error would be more annoying to debug than explicitly enforcing cmd's existence in this method. The executor might even expect that args has a function called getExecutionContext() which gets passed around a bit. I could imagine much more complex scenarios where debugging would quickly become a nightmare of tracing through function calls to see where an argument was first passed in.
Neither do I want to do something on the lines of:
var execute = function(args)
{
if(args.cmd === undefined || args.getExecutionContext === undefined ||
typeof args.getExecutionContext !== 'function')
throw new Error("args not setup correctly");
executor.execute(args);
}
This would entail a significant amount of maintenance for every function that has arguments, especially for complex arguments. I would much rather be able to specify an interface and somehow enforce a contract that tells javascript that I expect input matching this interface.
Maybe something like:
var baseCommand =
{
cmd: '',
getExecutionContext: function(){}
};
var execute = function(args)
{
enforce(args, baseCommand); //throws an error if args does not honor
//baseCommand's properties
executor.execute(args);
}
I could then reuse these interfaces amongst my different functions and define objects that extend them to be passed into my functions without worrying about misspelling property names or passing in the wrong argument. Any ideas on how to implement this, or where I could utilize an existing implementation?
I don't see any other way to enforce this. It's one of the side effects of the dynamic nature of JavaScript. It's essentially a free-for-all, and with that freedom comes responsibility :-)
If you're in need of type checking you could have a look at typescript (it's not JavaScript) or google's closure compiler (javascript with comments).
Closure compiler uses comments to figure out what type is expected when you compile it. Looks like a lot of trouble but can be helpful in big projects.
There are other benefits that come with closure compiler as you will be forced to produce comments that are used in an IDE like netbeans, it minifies your code, removes unused code and flattens namespaces. So code organized in namespaces like myApp.myModule.myObject.myFunction will be flattened to minimize object look up.
Cons are that you need to use externs when you use libraries that are not compiler compatible like jQuery.
The way that this kind of thing is typically dealt with in javascript is to use defaults. Most of the time you simply want to provide a guarentee that certain members exist to prevent things like reference errors, but I think that you could use the principal to get what you want.
By using something like jQuery's extend method, we can guarentee that a parameter implements a set of defined defaults.
var defaults = {
prop1: 'exists',
prop2: function() { return 'foo'; }
};
function someCall(args) {
var options = $.extend({}, defaults, args);
// Do work with options... It is now guarentee'd to have members prop1 and prop2, defined by the caller if they exist, using defaults if not.
}
If you really want to throw errors at run time if a specific member wasn't provided, you could perhaps define a function that throws an error, and include it in your defaults. Thus, if a member was provided by the caller, it would overwrite the default, but if it was missed, it could either take on some default functionality or throw an error as you wish.