Resume from an error - javascript

Before I get yelled at for trying something so reckless, let me tell you that I wouldn't do this in real life and it's an academic question.
Suppose I'm writing a library and I want my object to be able to make up methods as they are needed.
For example if you wanted to call a .slice() method, and I didn't have one then the window.onerror handler would fire it for me
Anyway I played around with this here
window.onerror = function(e) {
var method = /'(.*)'$/.exec(e)[1];
console.log(method); // slice
return Array.prototype[method].call(this, arguments); // not even almost gonna work
};
var myLib = function(a, b, c) {
if (this == window) return new myLib(a, b, c);
this[1] = a; this[2] = b; this[3] = c;
return this;
};
var obj = myLib(1,2,3);
console.log(obj.slice(1));
Also (maybe I should start a new question) can I change my constructor to take an unspecified amount of args?
var myLib = function(a, b, c) {
if (this == window) return new myLib.apply(/* what goes here? */, arguments);
this[1] = a; this[2] = b; this[3] = c;
return this;
};
BTW I know I can load my objects with
['slice', 'push', '...'].forEach(function() { myLib.prototype[this] = [][this]; });
That's not what I'm looking for

As you were asking an academic question, I suppose browser compatibility is not an issue. If it's indeed not, I'd like to introduce harmony proxies for this. onerror is not a very good practice as it's just a event raised if somewhere an error occurs. It should, if ever, only be used as a last resort. (I know you said you don't use it anyway, but onerror is just not very developer-friendly.)
Basically, proxies enable you to intercept most of the fundamental operations in JavaScript - most notably getting any property which is useful here. In this case, you could intercept the process of getting .slice.
Note that proxies are "black holes" by default. They do not correspond to any object (e.g. setting a property on a proxy just calls the set trap (interceptor); the actual storing you have to do yourself). But there is a "forwarding handler" available that routes everything through to a normal object (or an instance of course), so that the proxy behaves as a normal object. By extending the handler (in this case, the get part), you can quite easily route Array.prototype methods through as follows.
So, whenever any property (with name name) is being fetched, the code path is as follows:
Try returning inst[name].
Otherwise, try returning a function which applies Array.prototype[name] on the instance with the given arguments to this function.
Otherwise, just return undefined.
If you want to play around with proxies, you can use a recent version of V8, for example in a nightly build of Chromium (make sure to run as chrome --js-flags="--harmony"). Again, proxies are not available for "normal" usage because they're relatively new, change a lot of the fundamental parts of JavaScript and are in fact not officially specified yet (still drafts).
This is a simple diagram of how it goes like (inst is actually the proxy which the instance has been wrapped into). Note that it only illustrates getting a property; all other operations are simply passed through by the proxy because of the unmodified forwarding handler.
The proxy code could be as follows:
function Test(a, b, c) {
this[0] = a;
this[1] = b;
this[2] = c;
this.length = 3; // needed for .slice to work
}
Test.prototype.foo = "bar";
Test = (function(old) { // replace function with another function
// that returns an interceptor proxy instead
// of the actual instance
return function() {
var bind = Function.prototype.bind,
slice = Array.prototype.slice,
args = slice.call(arguments),
// to pass all arguments along with a new call:
inst = new(bind.apply(old, [null].concat(args))),
// ^ is ignored because of `new`
// which forces `this`
handler = new Proxy.Handler(inst); // create a forwarding handler
// for the instance
handler.get = function(receiver, name) { // overwrite `get` handler
if(name in inst) { // just return a property on the instance
return inst[name];
}
if(name in Array.prototype) { // otherwise try returning a function
// that calls the appropriate method
// on the instance
return function() {
return Array.prototype[name].apply(inst, arguments);
};
}
};
return Proxy.create(handler, Test.prototype);
};
})(Test);
var test = new Test(123, 456, 789),
sliced = test.slice(1);
console.log(sliced); // [456, 789]
console.log("2" in test); // true
console.log("2" in sliced); // false
console.log(test instanceof Test); // true
// (due to second argument to Proxy.create)
console.log(test.foo); // "bar"
The forwarding handler is available at the official harmony wiki.
Proxy.Handler = function(target) {
this.target = target;
};
Proxy.Handler.prototype = {
// Object.getOwnPropertyDescriptor(proxy, name) -> pd | undefined
getOwnPropertyDescriptor: function(name) {
var desc = Object.getOwnPropertyDescriptor(this.target, name);
if (desc !== undefined) { desc.configurable = true; }
return desc;
},
// Object.getPropertyDescriptor(proxy, name) -> pd | undefined
getPropertyDescriptor: function(name) {
var desc = Object.getPropertyDescriptor(this.target, name);
if (desc !== undefined) { desc.configurable = true; }
return desc;
},
// Object.getOwnPropertyNames(proxy) -> [ string ]
getOwnPropertyNames: function() {
return Object.getOwnPropertyNames(this.target);
},
// Object.getPropertyNames(proxy) -> [ string ]
getPropertyNames: function() {
return Object.getPropertyNames(this.target);
},
// Object.defineProperty(proxy, name, pd) -> undefined
defineProperty: function(name, desc) {
return Object.defineProperty(this.target, name, desc);
},
// delete proxy[name] -> boolean
delete: function(name) { return delete this.target[name]; },
// Object.{freeze|seal|preventExtensions}(proxy) -> proxy
fix: function() {
// As long as target is not frozen, the proxy won't allow itself to be fixed
if (!Object.isFrozen(this.target)) {
return undefined;
}
var props = {};
Object.getOwnPropertyNames(this.target).forEach(function(name) {
props[name] = Object.getOwnPropertyDescriptor(this.target, name);
}.bind(this));
return props;
},
// == derived traps ==
// name in proxy -> boolean
has: function(name) { return name in this.target; },
// ({}).hasOwnProperty.call(proxy, name) -> boolean
hasOwn: function(name) { return ({}).hasOwnProperty.call(this.target, name); },
// proxy[name] -> any
get: function(receiver, name) { return this.target[name]; },
// proxy[name] = value
set: function(receiver, name, value) {
this.target[name] = value;
return true;
},
// for (var name in proxy) { ... }
enumerate: function() {
var result = [];
for (var name in this.target) { result.push(name); };
return result;
},
// Object.keys(proxy) -> [ string ]
keys: function() { return Object.keys(this.target); }
};

Related

Creating jQuery prototyped collection

I need to initialize some kind of a prototype on already existing jQuery elements collection. The main problem is that the prototype should be accessible only inside of that collection and on elements produced by built-in jQuery functions like .find() on that collection or on some children objects inside of that collection, for example:
var $a = $('a');
$a.__proto__.foo/*some magic over here*/ = function(){ alert('foo!'); };
$a.foo(); //should show alert('foo!')
$a.find('b').foo(); //should produce the same action
$('a').foo(); //should produce an error (method not found)
If using $a.__proto__ like in example above, the jQuery.prototype is accessed, so all the new elements in outside of that jQuery-collection (for example, $('a')) are granting an access to .foo() method. That behaviour is unacceptable on a problem statement.
Is that actually possible?
Okay, here's the thing, I have a rather complex ES6 solution, so I won't be able to explain it in great depth, but if you have some particular questions, go ahead.
var wrap = (function wrapper() {
var store = {};
function wrap(fn) {
return new Proxy(fn, {
apply(target, thisArg, argumentsList) {
var result = Reflect.apply(target, thisArg, argumentsList);
// `jQuery(...)` returns a "rich" object that always contain `.length`
if (result.length > 0) {
result = new Proxy(result, {
get(target, propertyKey, receiver) {
var value = Reflect.get(target, propertyKey, receiver);
if (Object.keys(store).includes(propertyKey)) {
value = store[propertyKey];
}
return value;
},
set(target, propertyKey, value, receiver) {
// TODO: use `Reflect.set(), somehow`
// return Reflect.set(store, propertyKey, value, receiver);
return (store[propertyKey] = value);
},
});
}
return result;
}
});
}
return wrap;
})();
var $ = wrap(jQuery);
$.prototype.find = wrap(jQuery.prototype.find); // TODO: implement recursively in `wrap()`
var x = $('div');
var xx = x.find('div');
var xxx = x.find('divvv');
xx.foo = 123;
console.log(x.foo); // 123
console.log(xx.foo); // 123
console.log(xxx.foo); // undefined

Method chaining and/or value caching

Say i have a constructor, and some instance methods, like
function MyClass(name) {
this.name = name || '';
}
MyClass.prototype = {
constructor: MyClass,
isEmptyName: function() {
return this.name === '';
}
}
Now i can write
var myClass = new MyClass('Ben');
myClass.isEmptyName();
which would return false. Now if i make another method what would also return a Boolean
MyClass.prototype = {
constructor: MyClass,
isEmptyName: function() {
return this.name === '';
}
longerThan: function(len) {
return this.name.length > len;
}
}
i would like to chain these methods like this (somehow, thats my question :) )
myClass.isEmptyName().and.longerThan(2);
Just omit now the '.and.' part. I want the upper statement to finally return a value
false && true -> false
Or a more realistic sample:
myClass.notEmptyName().and.longerThan(4);
To summarize my problem i would say, i want my methods return a boolean value if they are called 'directly' myClass.notEmptyName() should return true, but work like i wrote in the samples, otherwise.
Other libraries do this somehow, but i can't guess how, npm's should is a good example:
user.should.have.property('pets').with.lengthOf(4);
user.pets.should.be.instanceof(Array).and.have.lengthOf(4);
Thanks
That's not possible. A method can't return either a boolean or be chainable depending on how it's used later on, because it doesn't know how it will be used later on.
You can chain methods that validate the object in different ways, but you need to get the result at the end if you want to use it in an expression, either by reading a property or calling a method:
function MyClass(name) {
this.name = name;
this.and = this;
}
MyClass.prototype = {
value: true,
isNotEmpty: function() {
this.value = this.value && this.name.length > 0; return this;
},
isLongerThan: function(len) {
this.value = this.value && this.name.length > len; return this;
},
evaluate: function() {
return this.value;
}
};
console.log(new MyClass('Adam').isLongerThan(2).evaluate());
console.log(new MyClass('Bob').isNotEmpty().and.isLongerThan(3).evaluate());
Demo: http://jsfiddle.net/Guffa/62e8dLwL/
Edit:
To allow evaluation more than once, you would reset the value in the evaluate method:
evaluate: function() {
var v = this.value;
this.value = true;
return v;
}
Sure, you can do that. We will define a new intermediate status object, called ChainResult, which remembers the underlying object, the current value, and a pending operation (a function to use to combine the next test). We give this object a valueOf method, so that when JS tries to evaluate it as a primitive, it "looks" like it has a value. To make this work, it turns out that ChainResult actually needs to be a function, and so we hang the necessary properties off the function.
function ChainResult(obj, val) {
function x() { }
x.obj = obj;
x.val = val;
x.op = null;
// the `valueOf` function spits out the current value when the object is evaluated
x.valueOf = function() { return this.val; };
// the test functions combine the results with the current value
// using the current operation as set by a preceding `and` or `or`
x.isEmptyName = function() {
x.val = x.op(x.val, x.obj._isEmptyName());
return this;
};
x.isLongerThan = function(len) {
x.val = x.op(x.val, x.obj._isLongerThan(len));
return this;
};
// we implement `and` and `or` via getters which set the operation
// on the ChainResult object, and return `this` so we can keep chaining
Object.defineProperties(x, {
and: {
get: function() { x.op = function(a,b) { return a && b; }; return x; }
},
or: {
get: function() { x.op = function(a,b) { return a || b; }; return x; }
}
});
return x;
}
The MyClass definition needs a bit of tweaking:
function MyClass(name) {
this.name = name || '';
}
MyClass.prototype = {
constructor: MyClass,
// we implement the testers as pseudo-private functions
_isEmptyName: function() { return this.name === ''; },
_isLongerThan: function(len) { return this.name.length > len; },
// when the public tester functions are invoked directly on the object
// (when they are the first link in the chain), we construct and return a
// ChainResult object with the initial value set correctly
isEmptyName: function() { return ChainResult(this, this._isEmptyName()); },
isLongerThan: function(len) { return ChainResult(this, this._isLongerThan(len)) }
};
Flow:
new MyClass('Bob') // create MyClass object
.isEmptyName() // create ChainResult object with value `false`
.or // remember `or` operation in ChainResult object
.isLongerThan(2) // update value of ChainResult object
; // JS tries to convert to scalar, calls valueOf
// true
This needs to be bullet-proofed and tightened up, but you get the idea.
i want my methods return a boolean value if they are called 'directly' myClass.notEmptyName() should return true
Your methods are always called directly on the instance, and would always need to return a primitive boolean value. By that, the context (myClass) is lost and you cannot have an and method (or property getter) on the result.
I would recommend you to have a look at functional programming, partial application and currying, which helps a lot with fluent interfaces like this. Given
function and(fn1, fn2) {
return function(val) {
return fn1(val) && fn2(val);
};
}
function or(fn1, fn2) {
return function(val) {
return fn1(val) || fn2(val);
};
}
function hasEmptyName: function(val) {
return val.name === '';
}
function hasNameLongerThan: function(len) {
return function(val) {
return val.name.length > len;
};
}
you could write
and(hasEmptyName, hasNameLongerThan(2))(myClass);
Making these functions methods of anything is complicated however. Maybe something like this:
function method(name) {
var args = Array.prototype.slice.call(arguments, 1);
return function(instance) {
return instance[name].apply(instance, args);
};
}
Function.prototype.and = function (fn2) {
var fn1 = this;
return function(val) {
return fn1(val) && fn2(val);
};
}
Function.prototype.or = function (fn2) {
var fn1 = this;
return function(val) {
return fn1(val) || fn2(val);
};
}
Object.defineProperty(Object.prototype, "test", function(pred) {
return pred(this);
});
Now you could write
myClass.test(method("notEmptyName").and(method("longerThan", 4)));
This is an answer (better call it to outcome) for my own question:
Finally i came out with another solution based on the responses in this thread (Thanks guys!) because the original problem can not be solved, since the javascript runtime can't find out wether to return a value, or return itself (the object) when chained. Explanation is messy, sorry :(
Check out my lib, where i have methods like:
check(value).isString() etc..
Originally i wanted to chain these like check(value).isString().and.not.empty() but this, in this way, can not be done. (Challenge me)
Finally i created tokens for chaining, so instead of
check(value).isString().and.not.empty()
I can write
check(value).isstring.and.not.empty.test()
Not nice, but still something.
check.js
For review, visit checkjs on my github repo.
Note: README is outdated.
If you use promises, you could write functions that return values and can be chained. Mind you, these are all asynchronous.
Find this JS plugin: Kris Owal's Q, or if you like to use a JS library, they usually contain deferred objects and promises.

jQuery logger plugin

I'm working on a jQuery plugin that allows you to log any javascript class or object.
The idea is to override each function inside the object, or prototype of a function.
(function($)
{
"use strict";
$.log = function(object, logger)
{
if (!$.isFunction(logger))
{
logger = function(name, args)
{
console.log(name + "(" + $.makeArray(args).join(", ") + ")");
};
}
var s = $.isFunction(object) ? object.prototype : object;
for (name in s)
{
var fn = s[name];
if ($.isFunction(fn))
{
s[name] = (function(name, fn)
{
return function()
{
logger(name, arguments);
return fn.apply(this, arguments);
};
})(name, fn);
}
}
};
})(jQuery);
This seems to work for logging individual plugins. For example $.log($.ui.tabs); logs all the function calls inside the tabs prototype.
But when I want to log all of jQuery $.log($); it's giving me some reference error.
I can't figure out why I'm getting this error. I'm under the impression it has something to do with either this or the arguments being passed, but I'm not sure.
Edit: Now I think about It some more it might also be caused because the overridden function always returns.
I created a fiddle to demo the problem: http://jsfiddle.net/Sj6xN/4/
EDIT:
This is the code i ended up with, so far working perfectly:
(function($)
{
"use strict";
var Logger = function(options)
{
this.options = $.extend(this.defaults, options);
};
Logger.prototype = {
defaults:
{
inherited: false,
deep: false,
logWriter: function(name, args)
{
console.log("CALL: " + name + "(" + $.makeArray(args).join(", ") + ")");
}
},
augment: function(object)
{
var self = this;
// Make sure this object is not already augmented
if (object.__isAugmented__)
{
return;
}
// Set 'isAugmented' to prevent recursion
object.__isAugmented__ = true;
// Loop through the object
for (var name in object)
{
var originalFunction = object[name];
// If it's a function and the function is not inherited or 'inherited' is enabled augment it
if ($.isFunction(originalFunction) && (object.hasOwnProperty(name) || self.options.inherited))
{
// Wrap in self executing function so references to 'name' and 'orginalFunction' are maintained
object[name] = (function(name, originalFunction)
{
// If the function has a prototype and 'deep' is enabled augment that as well
if (self.options.deep && originalFunction.prototype)
{
self.augment(originalFunction.prototype);
}
var augmentedFunction = function()
{
// Execute log writer
self.options.logWriter(name, arguments);
// Call original function
return originalFunction.apply(this, arguments);
};
// Inherit prototype of original function
augmentedFunction.prototype = originalFunction.prototype;
// Return the augmented function
return augmentedFunction;
})(name, originalFunction);
}
// If it's a plain object and 'deep' is enabled augment that as well
else if (self.options.deep && $.isPlainObject(originalFunction))
{
self.augment(originalFunction);
}
}
}
};
$.log = function(object, options)
{
var logger = new Logger(options);
// If the object is a function use it's prototype, otherwise assume a plain object
object = $.isFunction(object) ? object.prototype : object;
// Augment
logger.augment(object);
};
})(jQuery);
Can be used like this:
$.log(<object or function> [,
{
inherited: <bool>,
deep: <bool>,
logWriter: <function(name, args)>
}]);
Well look closely to the error.
Uncaught ReferenceError: name is not defined
Means you haven't defined name and since you are in strict mode, you can't use a variable without defining it(normally if you do it, it'll be a global variable, but not in strict mode). So if you write a var name before it you won't get this error anymore.
Though there is another error for not having tab method. The other error says tabs is not a method of the object which is because when you wrap the function, you didn't inherit the prototype, so when the function is called with new, it doesn't have prototype functions(tabs is one of them).
Here's the fixed code : http://jsfiddle.net/Sj6xN/8/

Using extend to add an object to a prototype

I get an error whenever I try to add the object BaseNet to IPv4Address.prototype. The error:
TypeError: Cannot read property 'ipInt' of undefined
just doesn't make sense. It's behaving like the getter is actually being executed when I copy the object to the prototype. Is there a way to copy this and not get an error like this?
var _s = require('underscore')
var BaseNet = {
get network(){
return new IPv4Address((this.ipInt & this.netmask.ipInt) >>> 0);
},
}
function IPv4Address (address){
this.ipInt = address
this.netmask = {}
this.netmask.ipInt = 255
}
_s.extend(IPv4Address.prototype,BaseNet)
//also fails with generic extend:
function extend(destination, source) {
for (var k in source) {
if (source.hasOwnProperty(k)) {
destination[k] = source[k];
}
}
return destination;
}
extend(IPv4Address.prototype,BaseNet)
First Edit
Maybe this is an answer to my own question. Resig had a post on this and this "modififed" extend method seems to do what I'm looking for. Can someone explain? It seems that it's a scoping problem, but i don't understand why it was behaving like someone was actually calling the getter during the extend operation.
http://ejohn.org/blog/javascript-getters-and-setters/
function extend(a,b) {
for ( var i in b ) {
var g = b.__lookupGetter__(i), s = b.__lookupSetter__(i);
if ( g || s ) {
if ( g )
a.__defineGetter__(i, g);
if ( s )
a.__defineSetter__(i, s);
} else
a[i] = b[i];
}
return a;
}
Second Edit
So I did some more experimenting and I came up with two other ways that seems to work. One uses the extend method posted earlier and another uses the defineProperties method form the ECMA spec pointed out in the comments. Is one better than the other? It seems that the "mixin" style works better for what i'm looking for, but the Object.create way also works.
var BaseNet = {}
Object.defineProperties(BaseNet, {
"network":{
enumerable:true,
get: function (){return new IPv4Address((this.ipInt & this.netmask.ipInt) >>> 0)}
}
});
function IPv4Address (address){
this.ipInt = address
this.netmask = {}
this.netmask.ipInt = 255
}
IPv4Address.prototype = Object.create(BaseNet)
var ip = new IPv4Address(12345)
ip.netmask
Alternatively, you can also do:
var BaseNet = {}
Object.defineProperties(BaseNet, {
"network":{
enumerable:true,
get: function (){return new IPv4Address((this.ipInt & this.netmask.ipInt) >>> 0)}
}
});
function IPv4Address (address){
this.ipInt = address
this.netmask = {}
this.netmask.ipInt = 255
}
extend(IPv4Address.prototype,BaseNet)
var ip = new IPv4Address(12345)
ip.netmask
It does not work because whilst extending, the getter is executed, at which point this.netmask is not an instance at all (there are no instances created), but in fact undefined, so accessing this.netmask.ipInt throws an error (you cannot access anything from undefined or null, it throws an error in any case).
Have a look at the underlying _.extend code:
_.extend = function(obj) {
each(slice.call(arguments, 1), function(source) {
for (var prop in source) {
// source[prop] is fetched, so getter is executed
if (source[prop] !== void 0) obj[prop] = source[prop];
}
});
return obj;
};
You might instead want to iterate yourself and copy the getter "untouched" with a for in loop and Object.defineProperty.
As for your edit: Those __xxx__ functions are an ugly way to get the getter/setter function without executing it. Normally, passing a function works like someFunction without parentheses. A getter, however, would automatically get executed if you access it with someGetter.
The function you posted copies getters/setters without executing them:
function extend(a,b) {
for ( var i in b ) { // iterate over all properties
// get getter and setter functions
var g = b.__lookupGetter__(i), s = b.__lookupSetter__(i);
if ( g || s ) { // if there is a getter or setter
if ( g )
a.__defineGetter__(i, g); // copy getter to new object
if ( s )
a.__defineSetter__(i, s); // copy setter to new object
} else // if there is not getter nor setter
a[i] = b[i]; // just copy the value; nothing special
}
return a; // return the altered object
}

How to dynamically set a function/object name in Javascript as it is displayed in Chrome

This is something which has been bugging me with the Google Chrome debugger and I was wondering if there was a way to solve it.
I'm working on a large Javascript application, using a lot of object oriented JS (using the Joose framework), and when I debug my code, all my classes are given a non-sensical initial display value. To see what I mean, try this in the Chrome console:
var F = function () {};
var myObj = new F();
console.log(myObj);
The output should be a single line which you can expand to see all the properties of myObj, but the first thing you see is just ▶ F.
My issue is that because of my OO framework, every single object instantiated gets the same 'name'. The code which it looks is responsible for this is like so:
getMutableCopy : function (object) {
var f = function () {};
f.prototype = object;
return new f();
}
Which means that in the debugger, the initial view is always ▶ f.
Now, I really don't want to be changing anything about how Joose instantiates objects (getMutableCopy...?), but if there was something I could add to this so that I could provide my own name, that would be great.
Some things that I've looked at, but couldn't get anywhere with:
> function foo {}
> foo.name
"foo"
> foo.name = "bar"
"bar"
> foo.name
"foo" // <-- looks like it is read only
Object.defineProperty(fn, "name", { value: "New Name" });
Will do the trick and is the most performant solution. No eval either.
I've been playing around with this for the last 3 hours and finally got it at least somewhat elegant using new Function as suggested on other threads:
/**
* JavaScript Rename Function
* #author Nate Ferrero
* #license Public Domain
* #date Apr 5th, 2014
*/
var renameFunction = function (name, fn) {
return (new Function("return function (call) { return function " + name +
" () { return call(this, arguments) }; };")())(Function.apply.bind(fn));
};
/**
* Test Code
*/
var cls = renameFunction('Book', function (title) {
this.title = title;
});
new cls('One Flew to Kill a Mockingbird');
If you run the above code, you should see the following output to your console:
Book {title: "One Flew to Kill a Mockingbird"}
Combine usage of computed property name to dynamically name a property, and inferred function naming to give our anonymous function that computed property name:
const name = "aDynamicName"
const tmp = {
[name]: function(){
return 42
}
}
const myFunction= tmp[name]
console.log(myFunction) //=> [Function: aDynamicName]
console.log(myFunction.name) //=> 'aDynamicName'
One could use whatever they want for 'name' here, to create a function with whatever name they want.
If this isn't clear, let's break down the two pieces of this technique separately:
Computed Property Names
const name = "myProperty"
const o = {
[name]: 42
}
console.log(o) //=> { myProperty: 42 }
We can see that the property name assigned on o was myProperty, by way of computed property naming. The []'s here cause JS to lookup the value inside the bracket, and to use that for the property name.
Inferred Function Naming
const o = {
myFunction: function(){ return 42 }
}
console.log(o.myFunction) //=> [Function: myFunction]
console.log(o.myFunction.name) //=> 'myFunction'
Here we use inferred function naming. The language looks at the name of wherever the function is being assigned to, & gives the function that inferred name.
We can combine these two techniques, as shown in the beginning. We create an anonymous function, which gets it's name via inferred function naming, from a computed property name, which is the dynamic name we wanted to create. Then we have to extract the newly created function from the object it is embedded inside of.
Example Using Stack Trace
Naming a supplied anonymous function
// Check the error stack trace to see the given name
function runAnonFnWithName(newName, fn) {
const hack = { [newName]: fn };
hack[newName]();
}
runAnonFnWithName("MyNewFunctionName", () => {
throw new Error("Fire!");
});
Although it is ugly, you could cheat via eval():
function copy(parent, name){
name = typeof name==='undefined'?'Foobar':name;
var f = eval('function '+name+'(){};'+name);
f.prototype = parent;
return new f();
}
var parent = {a:50};
var child = copy(parent, 'MyName');
console.log(child); // Shows 'MyName' in Chrome console.
Beware: You can only use names which would be valid as function names!
Addendum: To avoid evaling on every object instantiation, use a cache:
function Cache(fallback){
var cache = {};
this.get = function(id){
if (!cache.hasOwnProperty(id)){
cache[id] = fallback.apply(null, Array.prototype.slice.call(arguments, 1));
}
return cache[id];
}
}
var copy = (function(){
var cache = new Cache(createPrototypedFunction);
function createPrototypedFunction(parent, name){
var f = eval('function '+name+'(){};'+name);
f.prototype = parent;
return f;
}
return function(parent, name){
return new (cache.get(name, parent, typeof name==='undefined'?'Foobar':name));
};
})();
This won't totally solve your problem, but I would suggest overriding the toString method on the class's prototype. For instance:
my_class = function () {}
my_class.prototype.toString = function () { return 'Name of Class'; }
You'll still see the original class name if you enter an instance of my_class directly in the console (I don't think it's possible to do anything about this), but you'll get the nice name in error messages, which I find very helpful. For instance:
a = new my_class()
a.does_not_exist()
Will give the error message: "TypeError: Object Name of Class has no method 'does_not_exist'"
If you want to dynamically create a named function. You can use new Function to create your named function.
function getMutableCopy(fnName,proto) {
var f = new Function(`function ${fnName}(){}; return ${fnName}`)()
f.prototype = proto;
return new f();
}
getMutableCopy("bar",{})
// ▶ bar{}
Similar to #Piercey4 answer, but I had to set the name for the instance as well:
function generateConstructor(newName) {
function F() {
// This is important:
this.name = newName;
};
Object.defineProperty(F, 'name', {
value: newName,
writable: false
});
return F;
}
const MyFunc = generateConstructor('MyFunc');
const instance = new MyFunc();
console.log(MyFunc.name); // prints 'MyFunc'
console.log(instance.name); // prints 'MyFunc'
normally you use window[name] like
var name ="bar";
window["foo"+name] = "bam!";
foobar; // "bam!"
which would lead you to a function like:
function getmc (object, name) {
window[name] = function () {};
window[name].prototype = object;
return new window[name]();
}
but then
foo = function(){};
foobar = getmc(foo, "bar");
foobar; // ▶ window
foobar.name; // foo
x = new bar; x.name; // foo .. not even nija'ing the parameter works
and since you can't eval a return statement (eval("return new name()");), I think you're stuck
I think this is the best way to dynamically set the name of a function :
Function.prototype.setName = function (newName) {
Object.defineProperty(this,'name', {
get : function () {
return newName;
}
});
}
Now you just need to call the setName method
function foo () { }
foo.name; // returns 'foo'
foo.setName('bar');
foo.name; // returns 'bar'
foo.name = 'something else';
foo.name; // returns 'bar'
foo.setName({bar : 123});
foo.name; // returns {bar : 123}
Based on the answer of #josh, this prints in a console REPL, shows in console.log and shows in the debugger tooltip:
var fn = function() {
return 1917;
};
fn.oldToString = fn.toString;
fn.toString = function() {
return "That fine function I wrote recently: " + this.oldToString();
};
var that = fn;
console.log(that);
Inclusion of fn.oldToString() is a magic which makes it work. If I exclude it, nothing works any more.
With ECMAScript2015 (ES2015, ES6) language specification, it is possible to dynamically set a function name without the use of slow and unsafe eval function and without Object.defineProperty method which both corrupts function object and does not work in some crucial aspects anyway.
See, for example, this nameAndSelfBind function that is able to both name anonymous functions and renaming named functions, as well as binding their own bodies to themselves as this and storing references to processed functions to be used in an outer scope (JSFiddle):
(function()
{
// an optional constant to store references to all named and bound functions:
const arrayOfFormerlyAnonymousFunctions = [],
removeEventListenerAfterDelay = 3000; // an auxiliary variable for setTimeout
// this function both names argument function and makes it self-aware,
// binding it to itself; useful e.g. for event listeners which then will be able
// self-remove from within an anonymous functions they use as callbacks:
function nameAndSelfBind(functionToNameAndSelfBind,
name = 'namedAndBoundFunction', // optional
outerScopeReference) // optional
{
const functionAsObject = {
[name]()
{
return binder(...arguments);
}
},
namedAndBoundFunction = functionAsObject[name];
// if no arbitrary-naming functionality is required, then the constants above are
// not needed, and the following function should be just "var namedAndBoundFunction = ":
var binder = function()
{
return functionToNameAndSelfBind.bind(namedAndBoundFunction, ...arguments)();
}
// this optional functionality allows to assign the function to a outer scope variable
// if can not be done otherwise; useful for example for the ability to remove event
// listeners from the outer scope:
if (typeof outerScopeReference !== 'undefined')
{
if (outerScopeReference instanceof Array)
{
outerScopeReference.push(namedAndBoundFunction);
}
else
{
outerScopeReference = namedAndBoundFunction;
}
}
return namedAndBoundFunction;
}
// removeEventListener callback can not remove the listener if the callback is an anonymous
// function, but thanks to the nameAndSelfBind function it is now possible; this listener
// removes itself right after the first time being triggered:
document.addEventListener("visibilitychange", nameAndSelfBind(function(e)
{
e.target.removeEventListener('visibilitychange', this, false);
console.log('\nEvent listener 1 triggered:', e, '\nthis: ', this,
'\n\nremoveEventListener 1 was called; if "this" value was correct, "'
+ e.type + '"" event will not listened to any more');
}, undefined, arrayOfFormerlyAnonymousFunctions), false);
// to prove that deanonymized functions -- even when they have the same 'namedAndBoundFunction'
// name -- belong to different scopes and hence removing one does not mean removing another,
// a different event listener is added:
document.addEventListener("visibilitychange", nameAndSelfBind(function(e)
{
console.log('\nEvent listener 2 triggered:', e, '\nthis: ', this);
}, undefined, arrayOfFormerlyAnonymousFunctions), false);
// to check that arrayOfFormerlyAnonymousFunctions constant does keep a valid reference to
// formerly anonymous callback function of one of the event listeners, an attempt to remove
// it is made:
setTimeout(function(delay)
{
document.removeEventListener('visibilitychange',
arrayOfFormerlyAnonymousFunctions[arrayOfFormerlyAnonymousFunctions.length - 1],
false);
console.log('\nAfter ' + delay + 'ms, an event listener 2 was removed; if reference in '
+ 'arrayOfFormerlyAnonymousFunctions value was correct, the event will not '
+ 'be listened to any more', arrayOfFormerlyAnonymousFunctions);
}, removeEventListenerAfterDelay, removeEventListenerAfterDelay);
})();
I have not seen anyone mention the use of ES6 Proxies. Which in my opinion solve this problem beautifully. So here it is.
function shadow(object, secondObject) {
return new Proxy(object, {
get(target, prop, receiver) {
if (secondObject.hasOwnProperty(prop)) return secondObject[prop];
return Reflect.get(...arguments);
}
})
}
let t=function namedFunction(a,b,c){return a+b+c;}
console.log(t.name)//read only property
let f=shadow(t,{name:"addition"})
console.log(f.name)

Categories