Harmony proxy, detect whether property was accessed or called - javascript

Is there a way using Proxy to detect if a property was executed, or was it just accessed?
'use strict';
require('harmony-reflect');
var Stub = {
method: function (a) {
console.log('q' + a + this.q);
}
};
var ProxiedLibrary = {
get: function (target, name, receiver) {
if (name in target) {
return target[name];
}
if (MAGIC_EXPRESSION) {
return function() {
return 'Return from nonexistent function!';
};
}
return 'Property ' + name + ' is drunk and not available at the moment';
}
};
var Library = new Proxy(Stub, ProxiedLibrary);
console.log(Library.nonexistent); //Everything is cool
console.log(Library.nonexistent()); //TypeError we don't want
I pretty much want to emulate php's __call and __get, preferably separaterly. try...catch block is not an option.
Thank you

Is there a way using Proxy to detect if a property was executed, or was it just accessed?
No, as JavaScript does not distinguish between attributes and methods. It's all just properties that are accessed, and their value can be called if it's a function.
You would need to return a function (so that it can be called) but also mimics a string, maybe by tampering with the .valueOf()/.toString()/##toPrimitive methods of that function object.

I'm just a beginner with proxies, but as far as I know, the proxy cannot do what you need. It just gives you back the property you're looking for, it can't know how you're going to use it.

Related

How to detect function creation js

I am trying to detect when a function is created, preferable through a constructor. Functions are a type of object, right? So it makes sense that when you create a new one, it calls a constructor. Is there a way to override this, for example, something like this
var old = Function.constructor;
Function.constructor = () => {
alert('new function created!');
old();
};
function asdf() {}
var k = new Function();
If this is not possible, is there a way to get all currently defined functions? I am trying to trigger a piece of code on each function run.
You can't detect function creation.
Functions are a type of object, right?
Yes.
So it makes sense that when you create a new one, it calls a constructor.
No. Or - maybe, but that constructor is internal. Just like the construction of objects from array literals, object literals, regex literals, definition of a function directly creates a native object.
Is there a way to override this?
No. You'd need to hook into the JS engine itself for that.
If this is not possible, is there a way to get all currently defined functions?
No. At best, you could try the debugging API of the JS engine and get a heap snapshot, that should contain all function objects.
I am trying to trigger a piece of code on each function run.
Let me guess, that piece of code is a function itself?
Was able to get a semi-working attempt at this. It reads only global functions but it can add code to both the front and beginning of the function. Any tips on how to improve this, as I use classes a lot when I code?
Thanks to Barmar for the idea of looping through window properties, but since you can't access local functions and class functions, this may be the closest way to do this
<script>
function prepend(name) {
console.time(name);
}
function postpend(name) {
console.timeEnd(name);
}
var filter = ['prepend', 'postpend', 'caches'];
function laggyFunction() {
var l = 0;
while (l<1000) {l++}
}
var functions = [];
for (var property in window) {
try {
if (!filter.includes(property)) { // security error on accessing cache in stackoverflow editor along with maximum call stack size exceeded if prepend and postpend are included
if (typeof window[property] === 'function') {
window[property].original = window[property];
window[property].name = property;
window[property] = function() {
prepend(this.name);
console.log(this.original);
this.original.apply(null, arguments);
postpend(this.name);
}.bind(window[property]);
functions.push(property);
}
}
} catch(e) {
console.warn(`Couldn't access property: `+property+' | '+e);
}
}
document.write(functions); // functions the prepend and postpend are applied to
laggyFunction(); // test performance of the function
</script>

understanding chained functions, how to return jquery-like the element itself, how avoid "... is not a function"

Completely experimental and only for learning I am trying to reinvent the wheel, a wheel called jquery. The original purpose of doing that was to understand chainable functions.
So I wrote a function which is doing this, and very well as far as I can see, like so: (a bit shortened)
var $ = function(s) {
var el = document.querySelectorAll(s);
var myobj = {
addClass(className) {
el.forEach(elem => {elem.classList.add(className)});
return this;
},
toggleClass(className) {
el.forEach(elem => {elem.classList.toggle(className)});
return this;
},
removeClass(className) {
el.forEach(elem => {elem.classList.remove(className)});
return this;
},
html(html) {
if (typeof html == 'string') {
el.forEach(elem => {elem.innerHTML = html});
return this;
} else {
return el[0].innerHTML;
}
}
};
return myobj;
};
(this is really a working example) Now I can use jquery-like syntax for manipulate classes and html of an element (or a node list of elements) like so: $('#test').html('new html content'); or chained like $('a').html('my new link').addClass('newclassname');, which is really cool.
For those who are interested, here's the complete script snapshot of today including css() and attr(), and including the missing jquery function id().
My questions are:
[RESOLVED] How can I get this function to return jquery-like just the element itself like var elm = $('div#test');, now I am getting back the entire object.
[RESOLVED] (plz. see comments below) How can I write a fallback function, to prevent getting error message like $(...).animate is not a function, if I am using a method which is currently not covered in this function, like $('div#test').addClass('newclass').animate({top:'2.3em',background:'red'});, since animate() isn't implemented at the moment. In php there is a __get() function, is there something similar in javascript ?
Please don't ask me for why I am doing this all, as I mentioned it's for learning and understanding
Thanks.
How can I get this function to return jquery-like just the element itself like var elm = $('div#test');, now I am getting back the entire object.
jQuery always gives you back an entire jQuery object. If you’re referring to how the methods you’ve defined (addClass, …) don’t appear with the object in the browser console, that’s achieved by sharing those properties with a prototype instead of creating a new set of functions for every object. ES6 classes are a convenient way to make constructors that allow you to define a prototype with methods and create objects that inherit from it:
class MyElementCollection {
constructor(el) {
this.el = el;
}
addClass(className) {
this.el.forEach(elem => {elem.classList.add(className)});
return this;
}
// ⋮
}
var $ = function(s) {
var el = document.querySelectorAll(s);
return new MyElementCollection(el);
};
Then if you want it to show up like an array on Firefox and have elements accessible by index $('div#test')[0] like jQuery does, you assign the elements to those indexes and give your collection a length:
class MyElementCollection {
constructor(el) {
el.forEach((elem, i) => {
this[i] = elem;
});
this.length = el.length;
}
each(action) {
for (let i = 0; i < this.length; i++) {
action(this[i]);
}
}
addClass(className) {
this.each(elem => {elem.classList.add(className)});
return this;
}
// ⋮
}
Then if you want it to show up like an array on Chrome, you do the weird hack (still applicable in 2019) of defining a splice method, even if it doesn’t do the same thing that it does on arrays… or do anything at all:
class MyElementCollection {
// ⋮
splice() {
throw new Error('Not implemented');
}
}
How can I write a fallback function, to prevent getting error message like $(...).animate is not a function, if I am using a method which is currently not covered in this function, like $('div#test').addClass('newclass').animate({top:'2.3em',background:'red'});, since animate() isn't implemented at the moment. In php there is a __get() function, is there something similar in javascript ?
You can do this with proxies in JavaScript, but I wouldn’t recommend it – it’s not very clean. (Your object will pretend to have all properties instead of just ones existing in jQuery that you haven’t implemented, and there won’t be a list of them to find on the prototype. Proxies also come with performance and understandability penalties.) Instead, consider assigning all the unimplemented stuff the same implementation:
class MyElementCollection {
// ⋮
}
const notImplemented = () => {
throw new Error('Not implemented');
};
['animate', …].forEach(name => {
MyElementCollection.prototype[name] = notImplemented;
});
Have a look into the github repo of jquery. The relevant files are these:
https://github.com/jquery/jquery/blob/master/src/core.js
https://github.com/jquery/jquery/blob/master/src/core/init.js
jQuery does it's stuff in a more complicated way than I will do here, so this is just a basic explanation.
The jQuery function (and the $ alias of it) call new jQuery.fn.init(selector, root) in their function bodies. This creates a new jQuery collection (that's the term they use in the documentation) from the jQuery.prototype. That prototype has all the usual jQuery methods attached to it (like on, find, animate, etc.).
They do this:
function jQuery (selector, root) {
return new jQuery.fn.init(selector, root);
}
jQuery.fn = jQuery.prototype = { /* skipped */ }
jQuery.fn.init = function (selector, root) { /* skipped */ }
jQuery.fn.init.prototype = jQuery.fn;
The key here is the last line, where they make the jQuery.prototype also being the prototype of jQuery.fn.init (don't get confused by fn, it's just another alias they use so they don't have to type prototype all the time).
When you scan the jQuery.fn.init constructor function, you'll notice they store all matched DOM elements by their index into the new collection. Simplified:
jQuery.fn.init = function (selector, root) {
let elms = Array.from(root.querySelectorAll(selector));
elms.forEach((el, index) => {
this[index] = el;
}, this);
}
They do alot more stuff under the hood, but this basically explains what a jQuery collection is: A custom type of object which partially looks like a NodeList but with a custom prototype which has all the jQuery methods attached to it.
This means, your first question is based on the wrong assumption: Using $('div#test') will return a jQuery collection with a single element. You'll never get the "raw" DOM node from jQuery.
As for your second question: You can use proxies for that.

JavaScript Console

I would like to setup a console in the web browser using form fields. It needs to behave much like nodejs's repl (command line). In fact, I will be using the same API in both.
This falls-short because the properties in context are only available under this. Can you please suggest a tweak to get this going? It is ideal if I can keep context unchanged, I use this object to loop (via Object.keys(context)` and set properties on nodejs' repl context.
var context = {
debug: 'I am debug'
}
function evalInContext(js) {
return function() { return eval(js); }.call(context)
}
//This does not need to work, but it
//confirms that the context is under 'this'
evalInContext('console.log(this.debug)') //prints 'I am debug'
//This really needs to work:
try{
evalInContext('console.log(debug)')
}catch(e){
//not good: ReferenceError: debug is not defined
console.log(e)
}
evalInContext('var a=2')
try{
evalInContext('console.log(a)')
}catch(e){
//not good: ReferenceError: a is not defined
console.log(e)
}
http://jsfiddle.net/hy8hewq4/
This in no way removes the dangers of eval, however you can create some String from an Object which evals as a var statement that you do in your function before evaling the other piece of code.
In my example below it is very important that the keys of variables are valid identifier names and note the values are turned into JSON to protect them from tampering across invocations, if you want functions etc you'll have to implement them your own way
var variables = {
variables: undefined, // shadow self
debug: 'foo'
};
function evalWithVariables(code) {
var key, variable_string = '';
for (key in variables) {
variable_string += ', ' + key + ' = ' + JSON.stringify(variables[key]);
}
if (variable_string.length) eval('var' + variable_string.slice(1));
return eval(code);
}
evalWithVariables('console.log(debug)'); // logs foo
You may wish to combine this with another abstraction to achieve your own this (as you're currently doing)
You may also wish to put this definition inside an IIFE that just returns your custom eval function to protect the references to your variables Object, etc

Immediately Invoked Function Expression without any private variable nor return value?

I was reading through the javascript design patterns book and came across the code below, while reading "The Command Pattern". I've been trying to understand why this code is wrapped around an anonymous function which is immediately invoked, especially since there is no other private variables to be closured with. How is this different from just declaring CarManager as an object literal?
(function(){
var CarManager = {
// request information
requestInfo: function( model, id ){
return 'The information for ' + model + ' with ID ' + id + ' is foobar';
},
// purchase the car
buyVehicle: function( model, id ){
return 'You have successfully purchased Item ' + id + ', a ' + model;
},
// arrange a viewing
arrangeViewing: function( model, id ){
return 'You have successfully booked a viewing of ' + model + ' ( ' + id + ' ) ';
}
};
})();
The example is slightly half-baked.
The goal is to be able to provide a public-interface.
Inside of that IIFE, you put together all of the pieces required for your interface.
You don't have to pass it back out through a return statement.
jQuery, for example, is built in the same way.
Instead of having a return statement:
var jQuery = (function () { return magic; }());
They manually set window's jQuery (and $) properties from within the function, like so:
(function () { window["jQuery"] = magic; }());
Moreover, what's actually happening is a little bit different, still.
They're passing the window object into the IIFE's window parameter.
It doesn't have to be window, then.
It could be any previously-defined object you're extending, in theory.
var myApp = {};
(function (window, document) { window["jQuery"] = public_interface; }(myApp, document));
myApp.jQuery("...");
When building libraries similar to jQuery, you can do similar.
If you typically namespace any large libraries/applications (a good idea), you can shorten the name, internally, to make life easier.
Instead of:
var NAMESPACED_AWESOME_APP_OF_DOOM = {};
NAMESPACED_AWESOME_APP_OF_DOOM.module1 = {};
You can do something like:
(function (namespace) {
var ns = {};
window[namespace] = ns;
ns.module1 = {};
ns.module2 = {};
ns.service1 = function () {};
}("NAMESPACED_AWESOME_APP_OF_DOOM"));
Then you can do all of your internal setup inside, and once your setup is complete, you can reference the application by name, in the global scope.
In other cases, you can create sandboxes for code, using closures in this way, with mediators to send messages back and forth between components.
Nicholas Zakas has a couple of good talks on this.
And lastly, sometimes you just want some work to get done, which is 100% irrelevant to the rest of your program, but still needs to get done, all the same (like doing compatibility-checks and tying the results to the DOM elements, a la Modernizr... ...or setting cookies that just need to be there, or firing off calls to analytics software... ...et cetera).
In this specific instance, the anonymous function only serves to keep CarManager from being accessible elsewhere, i.e. entering into the global scope.
Also specific to this code instance, its entirely pointless. CarManager is not usable by any other code.
Looking at the link you provided in the comments, http://addyosmani.com/resources/essentialjsdesignpatterns/book/#commandpatternjavascript
As far as I can tell, the entire section of code being documented, in addition to the CarManager.execute bit, will completely fail to work. Trying to define CarManager.execute = ... outside of the anonymous function will fail because CarManager doesn't exist.
I'm the OP. Based on the discussion above, and after trying to interpret what the author wanted to express, I'm coming to a conclusion that the following code might have been what the author wanted to express. At least for me this seems to make sense.
(function(){
var CarManager = {
requestInfo: function(model, id){
/* blah blah */
},
buyVehicle: function(model, id){
/* blah blah */
},
arrangeViewing: function( model, id ){
/* blah blah */
}
};
CarManager.execute = function(name){
return CarManager[name] && CarManager[name].apply(CarManager, Array.prototype.slice(arguments, 1));
}
return CarManager;
}());
Basically, I added the part where the anonymous function execution returns the CarManager object literal. Also, I placed the execute method before returning (Although I don't quite understand why it should be outside of the original CarManager object, other than potentially code readability) What do you think?
(By the way, the web version of the section of the book is here: http://addyosmani.com/resources/essentialjsdesignpatterns/book/#commandpatternjavascript )

Does Javascript have something like Ruby's method_missing feature?

In Ruby I think you can call a method that hasn't been defined and yet capture the name of the method called and do processing of this method at runtime.
Can Javascript do the same kind of thing ?
method_missing does not fit well with JavaScript for the same reason it does not exist in Python: in both languages, methods are just attributes that happen to be functions; and objects often have public attributes that are not callable. Contrast with Ruby, where the public interface of an object is 100% methods.
What is needed in JavaScript is a hook to catch access to missing attributes, whether they are methods or not. Python has it: see the __getattr__ special method.
The __noSuchMethod__ proposal by Mozilla introduced yet another inconsistency in a language riddled with them.
The way forward for JavaScript is the Proxy mechanism (also in ECMAscript Harmony), which is closer to the Python protocol for customizing attribute access than to Ruby's method_missing.
The ruby feature that you are explaining is called "method_missing" http://rubylearning.com/satishtalim/ruby_method_missing.htm.
It's a brand new feature that is present only in some browsers like Firefox (in the spider monkey Javascript engine). In SpiderMonkey it's called "__noSuchMethod__" https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/NoSuchMethod
Please read this article from Yehuda Katz http://yehudakatz.com/2008/08/18/method_missing-in-javascript/ for more details about the upcoming implementation.
Not at the moment, no. There is a proposal for ECMAScript Harmony, called proxies, which implements a similar (actually, much more powerful) feature, but ECMAScript Harmony isn't out yet and probably won't be for a couple of years.
You can use the Proxy class.
var myObj = {
someAttr: 'foo'
};
var p = new Proxy(myObj, {
get: function (target, methodOrAttributeName) {
// target is the first argument passed into new Proxy, aka. target is myObj
// First give the target a chance to handle it
if (Object.keys(target).indexOf(methodOrAttributeName) !== -1) {
return target[methodOrAttributeName];
}
// If the target did not have the method/attribute return whatever we want
// Explicitly handle certain cases
if (methodOrAttributeName === 'specialPants') {
return 'trousers';
}
// return our generic method_missing function
return function () {
// Use the special "arguments" object to access a variable number arguments
return 'For show, myObj.someAttr="' + target.someAttr + '" and "'
+ methodOrAttributeName + '" called with: ['
+ Array.prototype.slice.call(arguments).join(',') + ']';
}
}
});
console.log(p.specialPants);
// outputs: trousers
console.log(p.unknownMethod('hi', 'bye', 'ok'));
// outputs:
// For show, myObj.someAttr="foo" and "unknownMethod" called with: [hi,bye,ok]
About
You would use p in place of myObj.
You should be careful with get because it intercepts all attribute requests of p. So, p.specialPants() would result in an error because specialPants returns a string and not a function.
What's really going on with unknownMethod is equivalent to the following:
var unk = p.unkownMethod;
unk('hi', 'bye', 'ok');
This works because functions are objects in javascript.
Bonus
If you know the number of arguments you expect, you can declare them as normal in the returned function.
eg:
...
get: function (target, name) {
return function(expectedArg1, expectedArg2) {
...
I've created a library for javascript that let you use method_missing in javascript: https://github.com/ramadis/unmiss
It uses ES6 Proxies to work. Here is an example using ES6 Class inheritance. However you can also use decorators to achieve the same results.
import { MethodMissingClass } from 'unmiss'
class Example extends MethodMissingClass {
methodMissing(name, ...args) {
console.log(`Method ${name} was called with arguments: ${args.join(' ')}`);
}
}
const instance = new Example;
instance.what('is', 'this');
> Method what was called with arguments: is this
No, there is no metaprogramming capability in javascript directly analogous to ruby's method_missing hook. The interpreter simply raises an Error which the calling code can catch but cannot be detected by the object being accessed. There are some answers here about defining functions at run time, but that's not the same thing. You can do lots of metaprogramming, changing specific instances of objects, defining functions, doing functional things like memoizing and decorators. But there's no dynamic metaprogramming of missing functions as there is in ruby or python.
I came to this question because I was looking for a way to fall through to another object if the method wasn't present on the first object. It's not quite as flexible as what your asking - for instance if a method is missing from both then it will fail.
I was thinking of doing this for a little library I've got that helps configure extjs objects in a way that also makes them more testable. I had seperate calls to actually get hold of the objects for interaction and thought this might be a nice way of sticking those calls together by effectively returning an augmented type
I can think of two ways of doing this:
Prototypes
You can do this using prototypes - as stuff falls through to the prototype if it isn't on the actual object. It seems like this wouldn't work if the set of functions you want drop through to use the this keyword - obviously your object wont know or care about stuff that the other one knows about.
If its all your own code and you aren't using this and constructors ... which is a good idea for lots of reasons then you can do it like this:
var makeHorse = function () {
var neigh = "neigh";
return {
doTheNoise: function () {
return neigh + " is all im saying"
},
setNeigh: function (newNoise) {
neigh = newNoise;
}
}
};
var createSomething = function (fallThrough) {
var constructor = function () {};
constructor.prototype = fallThrough;
var instance = new constructor();
instance.someMethod = function () {
console.log("aaaaa");
};
instance.callTheOther = function () {
var theNoise = instance.doTheNoise();
console.log(theNoise);
};
return instance;
};
var firstHorse = makeHorse();
var secondHorse = makeHorse();
secondHorse.setNeigh("mooo");
var firstWrapper = createSomething(firstHorse);
var secondWrapper = createSomething(secondHorse);
var nothingWrapper = createSomething();
firstWrapper.someMethod();
firstWrapper.callTheOther();
console.log(firstWrapper.doTheNoise());
secondWrapper.someMethod();
secondWrapper.callTheOther();
console.log(secondWrapper.doTheNoise());
nothingWrapper.someMethod();
//this call fails as we dont have this method on the fall through object (which is undefined)
console.log(nothingWrapper.doTheNoise());
This doesn't work for my use case as the extjs guys have not only mistakenly used 'this' they've also built a whole crazy classical inheritance type system on the principal of using prototypes and 'this'.
This is actually the first time I've used prototypes/constructors and I was slightly baffled that you can't just set the prototype - you also have to use a constructor. There is a magic field in objects (at least in firefox) call __proto which is basically the real prototype. it seems the actual prototype field is only used at construction time... how confusing!
Copying methods
This method is probably more expensive but seems more elegant to me and will also work on code that is using this (eg so you can use it to wrap library objects). It will also work on stuff written using the functional/closure style aswell - I've just illustrated it with this/constructors to show it works with stuff like that.
Here's the mods:
//this is now a constructor
var MakeHorse = function () {
this.neigh = "neigh";
};
MakeHorse.prototype.doTheNoise = function () {
return this.neigh + " is all im saying"
};
MakeHorse.prototype.setNeigh = function (newNoise) {
this.neigh = newNoise;
};
var createSomething = function (fallThrough) {
var instance = {
someMethod : function () {
console.log("aaaaa");
},
callTheOther : function () {
//note this has had to change to directly call the fallThrough object
var theNoise = fallThrough.doTheNoise();
console.log(theNoise);
}
};
//copy stuff over but not if it already exists
for (var propertyName in fallThrough)
if (!instance.hasOwnProperty(propertyName))
instance[propertyName] = fallThrough[propertyName];
return instance;
};
var firstHorse = new MakeHorse();
var secondHorse = new MakeHorse();
secondHorse.setNeigh("mooo");
var firstWrapper = createSomething(firstHorse);
var secondWrapper = createSomething(secondHorse);
var nothingWrapper = createSomething();
firstWrapper.someMethod();
firstWrapper.callTheOther();
console.log(firstWrapper.doTheNoise());
secondWrapper.someMethod();
secondWrapper.callTheOther();
console.log(secondWrapper.doTheNoise());
nothingWrapper.someMethod();
//this call fails as we dont have this method on the fall through object (which is undefined)
console.log(nothingWrapper.doTheNoise());
I was actually anticipating having to use bind in there somewhere but it appears not to be necessary.
Not to my knowledge, but you can simulate it by initializing the function to null at first and then replacing the implementation later.
var foo = null;
var bar = function() { alert(foo()); } // Appear to use foo before definition
// ...
foo = function() { return "ABC"; } /* Define the function */
bar(); /* Alert box pops up with "ABC" */
This trick is similar to a C# trick for implementing recursive lambdas, as described here.
The only downside is that if you do use foo before it's defined, you'll get an error for trying to call null as though it were a function, rather than a more descriptive error message. But you would expect to get some error message for using a function before it's defined.

Categories