Create my own console.log version using closures | Javascript - javascript

I want to create my own version of console.log, using a 'curry' function and including the possibility to show or not the logs. ( inspired for SecretsOfTheJavascriptNinja )
So I implemented this function using closures:
Function.prototype.conditionalCurry = function() {
var fn = this;
var args = Array.prototype.slice.call(arguments);
var show = args[0];
args = args.slice(1, args.length);
return function() {
if (show) {
return fn.apply(this, args.concat(
Array.prototype.slice.call(arguments)));
}
else return;
};
};
When I tested the code using node in the terminal it works:
var newLog = console.log.conditionalCurry(true, 'Always visible|' );
newLog('log visible'); // gives on the console: Always visible| log visible
newLog = console.log.conditionalCurry(false, 'never visible|' );
newLog('log visible'); // gives nothing, it works!
But when I test the code on Chrome 48.0.2564.109 and in firefox 44.0.2 arises a problem, and I think is the same in both cases.
As far as I can see, somehow the context 'this' passed to the function conditionalCurry when I use node in terminal, is an anonymous function, but in the case of the browsers 'this' appear as a function called 'log', and I believe this is breaking the code.
Any idea how can I implement this functionality in browsers?
Fiddle
Thanks in advance!!!
With Bergi's solution, now it works: Fiddle

I would be weary about modifying the prototype of existing language features such as Function. It seems you really want instances of these logging tools back, so you may as well define that instance and use it in the sort of "classical" sense.
For example, let's create a "class" (just to use the language agnostic term) called "Logger". Instances of logger will be able to be configurable to show a prefixed message, conditionally send messages to the log, and also use the log in a semi traditional manner.
function Logger(display,prefix){
this.display = display == [][0] ? true : display;
this.prefix = prefix;
}
Logger.prototype.log = function(){
if(this.display){
[].splice.apply(arguments,[0,0,this.prefix])
console.log.apply(console,arguments);
}else{
console.log(this.prefix);
}
return this;
};
Logger.prototype.show = function(truthy){
this.display = !!truthy;
return this;
};
Logger.prototype.note = function(msg){
this.prefix = msg;
return this;
};
var newLog = new Logger(true,'always |');
newLog.log('show me');//always | show me
newLog.note('never show').show(false);
newLog.log('show me now');//never show

This is a problem of execution context. Since you attached the curry function to the 'function prototype', the 'this' keyword will reference the function that you are calling it on. In this case, the 'log' in console.log.
I wouldn't modify prototypes, just make a curry helper function :)
function curry(fn, ctx) {
var slice = Array.prototype.slice.call;
return function f() {
var x = slice(arguments);
// If we have enough arguments, call the function
// You can get the arity with the 'length' property
if (x.length >= fn.length) return fn.apply(ctx, x);
// If not, collect more arguments
return function () {
var y = slice(arguments);
return f.apply(null, x.concat(y));
}
}
}
// Trivial example
var curriedSum = curry(function(a, b, c) {
return a + b + c;
});
// this now works
curriedSum(1, 2, 3);
curriedSum(1)(2, 3);
curriedsum(1)(2)(3);
// for your use case
var curriedLog = curry(function(show, prefix, msg) {
if (! show) return;
return console.log(prefix + msg);
});
// Use like so
var neverLogs = curriedLog(false);
neverLog('Some Prefix|', 'some message') // never logs
var logWithPrefix = curriedLog(true, 'This is my prefix|');
logWithPrefix('wassup') // logs 'this is my prefix| wassup'
If you need to force the 'this' keyword to be a specific object, pass that object as the second parameter.
curry(fn, context);

Related

API: Making constructor functions chainable without parenthesis in JavaScript

So I am building out some chainable functions that does simple type checking. Currently, I have to call my functions like this:
Proceed().if('someString').is.a('string');
But what I really want is for my API to look like this:
proceed.if('someString').is.a('string');
Notice that in the second code sample, the opening and closing parenthesis are missing from the first function call.
As you can see from the code below, I have already figured out how to get the is and a to work, but I can't seem to find a way to remove the parenthesis from the Proceed() function.
Here is the code sample that works:
function Proceed() {
if (!(this instanceof Proceed)) {
return new Proceed();
}
this.target = "";
}
Proceed.prototype.if = function (target) {
this.target = target;
return this;
}
Proceed.prototype.a = function (type) {
console.log(this.target + ' === ' +type, typeof this.target === type);
};
Object.defineProperty(Proceed.prototype, 'is', {
get: function () {
return this;
}
});
Proceed().if('someString').is.a('string'); // true
Proceed().if('someString').is.a('function'); // false
// Everything Above this line Works!
And now, my attempt to remove the parenthesis from Proceed() looks like this:
Object.defineProperty(Proceed.prototype, 'proceed', {
set: function(){},
get: function(){
return Proceed(this);
},
configurable: true
});
proceed.if('someString').is.a('string'); // ReferenceError
proceed.if('someString').is.a('function'); // ReferenceError
The error I get from here is this:
Uncaught ReferenceError: proceed is not defined
If I swap out Proceed.prototype with Object.prototype then I can get it to work, but that means I have extended a native object, which can be problematic.
So does anyone know of a way I can pull this off without dangerously extending the native object? What am I doing wrong here?
Here is a jsFiddle with the above code samples.
Any help is appreciated.
UPDATE #1 This code is being designed as a node module, so there won't be any access to the browser's window object.
You need to start with a instance-valued variable:
var proceed = new Proceed();
And to make it chainable, you should return new instances from your methods instead of mutating the "static" proceed object:
function Proceed(target) {
this.target = arguments.length ? target : "";
}
Proceed.prototype.if = function (target) {
return new Proceed(target);
}
Or in general, you need to make proceed.if a factory that returns Proceed instances, regardless whether proceed already is one or just a normal object.
(jsfiddle demo)
Change:
Object.defineProperty(Proceed.prototype, 'proceed', {
to
Object.defineProperty(window, 'proceed', {
updated fiddle: https://jsfiddle.net/Lw29zyf1/4/
So you will have a "proceed" variable in the window scope.
Please see if the below code is of any help
var proto = Object.create(null);
proto.a = function(type) {
console.log(this.target + ' === ' + type, typeof this.target === type);
};
Object.defineProperty(proto, 'is', {
get: function() {
return this;
}
});
proto.if = function(target) {
this.target = target;
return this;
};
var proceed = Object.create(proto);
proceed.if('someString').is.a('string');

In JavaScript, inject my function into a library without changing function's properties

I need to inject my own function instead of the existing one, while keeping all of the original properties. So given this code:
var lib = {
func: function(foo) { return 1+foo; }
};
lib.func.bar = 10;
lib.func.bar2 = function() { return this(this.bar); };
// current behavior: lib.func.bar2() returns 11
// needed behavior: lib.func.bar2() to return 12, without changing bar2()
I need to replace the original func with my own version, while keeping all of its existing functionality intact:
var newFunc = function(foo) { return foo + 2; }
// copy the old lib.func.* to the newFunc
$.each(lib.func, function(k,v) { newFunc[k] = v; });
lib.func = newFunc;
The above seems to work, since when i call lib.func.bar2(), the result is 12, but this seems overly complex and potentially might cause other issues. What is the right way? Is there a way to bind the lib.func context to the newFunc somehow? or some other way?
You should assign the properties to the new function, not the old one:
$.each(obj.func, function(k,v) { newFunc[k] = v; });
If you want to alter what a function does when called, without creating a new different function, that would require altering the [[Code]] internal property, but most implementations don't expose it.
However, you can try this approach:
function code() {
return 123; /* Whatever */
}
function f() {
return code.apply(this, arguments); /* Redirect call to `code` */
}
f(); // 123
code = function() {
return 'abc'; /* Whatever else */
};
f(); // 'abc

Javascript Sandbox Pattern example implementation

On Page 101 of Stoyan Stefanov's great book "JavaScript Patterns" he explains the sandbox pattern.
I liked his book much but I really missed some real life examples here and then to better understand what he talks about.
Like the sandbox pattern!
I'm looking for a real life working implementation, like a copy&paste starting point, just a simple example that will work to fully understand it.
Is there any?
I've simplified Stoyan's example in an attempt to make it easier to understand what's going on. I've also commented it more thoroughly.
/*First define the modules of the sandbox. These will be defined
as properties on the constructor function because this is a
convenient place to keep them.*/
Sandbox.modules = {};
Sandbox.modules.returnNumbers = function(MYAPP) {
MYAPP.return100 = function() {return 100;};
};
Sandbox.modules.returnLetters = function(MYAPP) {
MYAPP.returnABC = function() {return "ABC";};
};
function Sandbox() {
/* Because Sandbox is a constructor, an new object is automatically
created. Because we're in the constructor, we refer to this new object
as 'this'.
A constructor would typically be used as part of an assignment, e.g.
myObject = new Sandbox().
However, it's also legitimate javascript to use a constructor without
the assignment by just writing new Sandbox() with no assignment. The
constructor does return an object, it's just that it doesn't get
assigned to anything so is discarded.
We're going to add functionality (methods) to the 'this' object, but
rather than returning it, we will pass it to the callback function, so
the methods can be used immediately.
*/
var args = Array.prototype.slice.call(arguments); //Put the arguments
//of the call to the Sandbox constructor in an array called args.
var callback = args.pop(); //The last argument is the callback
var requiredmodules = args; //The remaining arguments are the require
// modules
//For each of the modules in 'requiredmodules', add the module's
//methods to 'this'
for (i=0; i< requiredmodules.length; i++) {
Sandbox.modules[requiredmodules[i]](this);
}
//'this' now has methods returnNumbers and returnLetters
//Call the callback. In the example below, 'this' will be called
//MYAPP, which within the callback will have all the methods from
//the required modules.
callback(this);
}
//Finally here is an example of usage
new Sandbox('returnNumbers', 'returnLetters', function (MYAPP) {
console.log(MYAPP.return100());
console.log(MYAPP.returnABC());
});
Stoyan Stefanov mentions in the same chapter that YUI version 3 implements the Sandbox pattern. The YUI add method (API) registers modules and the use method (API) loads the specified ones in the sandbox instance. There are links to the source js file in the API documentation.
Virtually all YUI code examples use this pattern to work with the YUI library. Defining a module is rarely needed - YUI has many core ones and there is a page for custom modules added by the community.
So I tried and came up with this solution:
function Sandbox() {
// turning arguments into an array
var args = Array.prototype.slice.call(arguments),
// the last argument is the callback
callback = args.pop(),
// modules can be passed as an array or as individual parameters
modules = (args[0] && "string" === typeof args[0]) ? args : args[0],
i;
// make sure the function is called
// as a constructor
if (!(this instanceof Sandbox)) {
return new Sandbox(modules, callback);
}
// add properties to 'this' as needed:
this.a = 1;
this.b = 2;
// now add modules to the core 'this' object
// no modules or "*" both mean "use all modules"
if (!modules || '*' === modules) {
modules = [];
for (i in Sandbox.modules) {
if (Sandbox.modules.hasOwnProperty(i)) {
modules.push(i);
}
}
}
// initialize the required modules
for (i = 0; i < modules.length; i += 1) {
Sandbox.modules[modules[i]](this);
}
// call the callback
callback(this);
// any prototype properties as needed
Sandbox.prototype = {
name: "Sandbox",
version: "1.0",
getName: function() {
return this.name;
}
}
};
Sandbox.modules = {};
Sandbox.modules.color = function (box) {
// private
var initialColor = $('#main').css('color');
// set a red color
box.setMainRed = function() {
$('#main').css('color','red');
return false;
},
// get the current color
box.getInitialColor = function () {
return initialColor;
};
}
// another module
Sandbox.modules.style = function (box) {
// set a red color
box.setStyle = function() {
$('#main').css('font-style','italic');
return false;
};
}
// page ready
$.ready(
Sandbox(['color', 'style'], function (box) {
console.log(box);
box.setMainRed();
box.setStyle();
console.log('try access initialColor: ', box.initialColor);
console.log('get initial color: ', box.getInitialColor());
})
);
But I am really unsure weather this is what I should be doing.
Especially adding the "modules" is somewhat confusing. Also earlier in the book he uses the namespace-pattern for this task, but not here. Why? Can't you do it here too?
But I failed to combine these two patterns.
Namespace pattern example inspired by the book:
var APP = APP || {};
// namespace function
APP.namespace = function (nsString) {
var parts = nsString.split('.'),
parent = APP,
i;
// strip redundant leading global
if ("APP" === parts[0]) {
parts = parts.slice(1);
}
for (i = 0; i < parts.length; i += 1) {
// create a property if it doesn't exist
if ("undefined" === typeof parent[parts[i]]) {
parent[parts[i]] = {};
}
parent = parent[parts[i]];
}
return parent;
}
// constructors
APP.namespace('modules.Color');
// immediate function
APP.modules.Color = (function () {
var currentColor = $('#main').css('color'),
// set a red color
setMainRed = function() {
$('#main').css('color','red');
return false;
},
// get the current color
getCurrentColor = function () {
return currentColor;
};
// revealing module pattern
return {
setMainRed: setMainRed,
getCurrentColor: getCurrentColor
};
}());
var doSomething = function () {
var color = APP.modules.Color;
color.setMainRed();
console.log(color.currentColor);
console.log(color.getCurrentColor());
return false;
}
// page ready
$.ready(
doSomething()
);
Here is example with detailed comments:
(function(){
/* function constructor */
function Sandbox(){
//Change arguments to array, as you know 'arguments' are not a true JS array
//Array.prototype.slice will provide shallow copy of 'arguments'
var args = Array.prototype.slice.call(arguments),
//remove last element from array and return it to caller
//our last argument is callback
callback = args.pop(),
//We can pass modules as strings or as array
//if first element is a string, take all arguemnts
//otherwise take one element (array)
modules = (args[0] && typeof args[0] === "string") ? args : args[0],
modulesLength = modules.length,
i;
//handle calling function constructor without 'new' keyword
if(!(this instanceof Sandbox)){
//Invoke me again!
return new Sandbox(modules, callback);
}
//we can add properties to 'this'
this.someProp = "Initialized property";
//Initialize all required modules
for(i = 0; i < modulesLength ; i++){
//pass reference to 'this' for each required module and invoke it
//'this' is poiting to new object which was created
//after calling new Sandbox()
Sandbox.modules[modules[i]](this);
}
//Invoke callback and pass 'this'
//now 'this' cotains all methods and properties
//attached in modules functions
callback(this);
};
//We can optionally create Sandbox methods
Sandbox.prototype = {
version: "1.0.1",
createdAt: new Date()
};
/* function as a first class object - saving all modules*/
Sandbox.modules = {};
/*Create $http,$scope and $ajax modules */
/*We need box object to add new functionality*/
/*We are creating new methods by attatching them to box obect*/
/*box is a reference to 'this' called as initializator from function constructor*/
Sandbox.modules.$http = function(box){
box.get = function(){
console.log("$http.get");
};
box.post = function(){
console.log("$http.post");
};
box.prop = "I'm $http property";
};
Sandbox.modules.$scope = function(box){
box.inject = function(param1, param2){
console.log("$scope.inject: " + param1 + " " + param2);
};
box.destroy = function(o){
console.log("$scope.destroy: " + o + " has been destroyed!");
};
};
Sandbox.modules.$ajax = function(box){
box.call = function(){
console.log("$ajax.call");
};
};
//Sandbox without calling 'new' was handled in function constructor
//We are requesting for 2 modules: $scope and $http
//callback function is our new playground
//box object has $scope and $http methods and properties inside, we are ready to go!
Sandbox(["$scope", '$http'], function(box){
console.log(box); //contains methods from $scope and $http
console.log(box.inject("John", "Doe"));
console.log(box.post());
//we can event nest our playgrounds
Sandbox(["$ajax"], function(box){
console.log(box); //contains only $ajax methods and properties
console.log(box.call());
//we can't invoke $scope or $http functions here
});
//we can't invoke $ajax functions here
});
})();
Link to JSFiddle: http://jsfiddle.net/Lodse4hj/

How do I compute a variable in JavaScript if and only if it is used?

This is what I'm doing right now.
var foo = function() {
var x = someComplicatedComputationThatMayTakeMoreTime();
this.foo = function() { return x; };
return x;
}
It works but only if foo is called as a function like so
foo();
But what if I want to call it as a normal variable with a value? I could modify the code to be
var foo = function() {
var x = someComplicatedComputationThatMayTakeMoreTime();
this.foo = x;
return x;
}
That would allow me to only call it once as a function and after that as a regular variable. But it's still not what I want. Plus it gets complicated if it accidentally gets called as a function again, returning an error.
Is this even possible in JavaScript?
BTW, this is for a Chrome/Firefox extension, so IE compatibility does not matter.
Ended up using toString because getters don't allow me to redefine the whole attribute, a function must be associated with it. And toString has cleaner syntax.
How about using toString?
var foo = function() {
function someComplicatedComputationThatMayTakeMoreTime() {
//your calculations
}
return {
toString: function() {
return someComplicatedComputationThatMayTakeMoreTime();
}
}
}
More about Object-to-Primitive Conversions in JavaScript
EDIT based on comment. Use a singleton (I think it's called):
myObject.prop = (function(){
function someComplicatedComputationThatMayTakeMoreTime() {
//your calculations
}
return {
toString: function() {
return someComplicatedComputationThatMayTakeMoreTime();
}
}
})()
If only Internet Explorer didn't exist, you could use getters and setters as described by John Resig in this blog article:
John Resig: JavaScript Getters and Setters
... They allow you to bind special functions to an object that look like normal object properties, but actually execute hidden functions instead.
Using a function is your best option for now, however the new JavaScript standard (ECMAScript 5th Ed.) which is being implemented now by all major browser vendors, gives you a method to create accessor properties, where you can define a property with a get and set functions that will be internally called, without worrying to treat this properties as functions, e.g.:
var obj = {};
Object.defineProperty(obj, 'foo', {
get: function () { // getter logic
return 'foo!';
},
set: function (value) {
// setter logic
}
});
obj.foo; // "foo!", no function call
This new standard will take some time to be implemented for all browsers, (the IE9 preview version really disappointed me), and I wouldn't recommend you to use it for production, unless you have total control on the environment where your application will be used.
What I think you want is a lazily instantiated variable, which can be implemented like this.
var myProperty = null;
function getMyProperty() {
return (myProperty = myProperty || builder());
}
This is not practical on the web because IE does not support it, but you can look at
https://developer.mozilla.org/en/defineGetter for examples how to do this.
There are a couple ways to do it, here is one example:
var data = {};
data.__defineGetter__("prop",
(function () {
var value = null;
return function () {
if (null == value) {
value = getYourValueHere();
}
return value;
};
})());
and now you can use it like:
var a = data.prop;
var b = data.prop;
I would recommend a variation on ChaosPandion's answer, but with a closure.
var myProperty = (function () {
var innerProperty = null;
return function() {
return (innerProperty = innerProperty || someComplicatedComputationThatMayTakeMoreTime());
};
})();
and then use myProperty() every time you need to access the variable.
You could define a JavaScript getter. From the Apple JavaScript Coding Guidelines:
myObject.__defineGetter__( "myGetter", function() { return this.myVariable; } );
var someVariable = myObject.myGetter;
See John Resig's post, JavaScript Getters and Setters, and the Defining Getters and Setters page at the Mozilla Developer Centre for more information.
I would use explicit lazy evaluation. Here's my implementation of it based on Scheme's take:
var delay, lazy, force, promise, promiseForced, promiseRunning;
(function () {
var getValue = function () {
return this.value;
};
var RUNNING = {};
var DelayThunk = function (nullaryFunc) {
this.value = nullaryFunc;
};
DelayThunk.prototype.toString = function () {
return "[object Promise]";
};
DelayThunk.prototype.force = function () {
if (promiseRunning (this)) {
throw new Error ("Circular forcing of a promise.");
}
var nullaryFunc = this.value;
this.value = RUNNING;
this.value = nullaryFunc ();
this.force = getValue;
return this.value;
};
var LazyThunk = function (nullaryFunc) {
DelayThunk.call (this, nullaryFunc);
};
LazyThunk.prototype = new DelayThunk (null);
LazyThunk.prototype.constructor = LazyThunk;
LazyThunk.prototype.force = function () {
var result = DelayThunk.prototype.force.call (this);
while (result instanceof LazyThunk) {
result = DelayThunk.prototype.force.call (result);
}
return force (result);
};
delay = function (nullaryFunc) {
return new DelayThunk (nullaryFunc);
};
lazy = function (nullaryFunc) {
return new LazyThunk (nullaryFunc);
};
force = function (expr) {
if (promise (expr)) {
return expr.force ();
}
return expr;
};
promise = function (expr) {
return expr instanceof DelayThunk;
};
promiseForced = function (expr) {
return expr.force === getValue || !promise (expr);
};
promiseRunning = function (expr) {
return expr.value === RUNNING || !promise (expr);
};
}) ();
Example Syntax:
var x = lazy (function () { return expression; });
var y = force (x);
var z = delay (function () { return expression; });
var w = force (z);
Note values are stored once evaluated, so repeated forcing will not do extra computations.
Example usage:
function makeThunk (x, y, z) {
return lazy (function () {
// lots of work done here
});
}
var thunk = makeThunk (arg1, arg2, arg3);
if (condition) {
output (force (thunk));
output (force (thunk)); // no extra work done; no extra side effects either
}
You can use the javascript Proxy class for creating such functionality.
var object = {};
var handler = {
resolvers: {},
get ( target, property, proxy ) {
if ( ! target.hasOwnProperty( property ) && this.resolvers.hasOwnProperty( property ) ) {
// execute the getter for the property;
target[ property ] = this.resolvers[ property ]();
}
return target[ property ];
},
set ( target, property, value, receiver ) {
// if the value is function set as a resolver
if ( typeof value === 'function' ) {
this.resolvers[property] = value;
// otherwise set value to target
} else {
target.property = value;
}
},
has ( target, property, receiver ) {
//true when proxy handler has either a resolver or target has a value;
return this.resolvers.hasOwnProperty( property ) || target.hasOwnProperty( property );
}
};
var lazyObject = new Proxy( object, handler );
Now you can use it like this:
'exampleField' in lazyObject; //returns false
lazyObject.exampleField = function(){ return 'my value' }; // add a resolver function
'exampleField' in lazyObject; //returns true
lazyObject.exampleField; //executes your resolver function and returns 'my value'
This example is to demonstrate the working. You can change after your needs.
Here is a fiddle with a demonstration

Removing the need for "new"

A nasty gotcha in javascript is forgetting to call new on a function meant to be instantiated, leading to this being bound to a different object (usually the global) instead of a fresh one. One workaround I read about is to check for it explicitly in the function-constructor using the following idiom:
function SomeConstructor(x, y, ...) {
// check if `this` is created with new
if ( !(this instanceof arguments.callee) )
return new SomeConstructor(x, y, ...);
// normal initialization code follows
Now new SomeConstructor(...) and SomeConstructor(...) are equivalent.
I'd like to simplify this by creating a wrapper function factory(fn) that does the first two lines and then delegates to the wrapped function fn. This would be used like:
SomeConstructor = factory(function (x, y, ...) {
// normal initialization code follows
})
My first attempt was:
function factory(fn) {
return function() {
if ( !(this instanceof arguments.callee) ) {
return new arguments.callee.apply(this, arguments);
}
fn.apply(this, arguments);
}
}
but it fails with "Function.prototype.apply called on incompatible [object Object]". The second attempt was:
function factory(fn) {
return function() {
if ( !(this instanceof arguments.callee) ) {
var tmp = new arguments.callee();
arguments.callee.apply(tmp, arguments);
return tmp;
}
fn.apply(this, arguments);
}
}
This sort of works but it may call the wrapped function twice: once with no arguments (to create a new instance) and once with the passed arguments for the actual initialization. Apparently this is fragile and inefficient but I can't figure out a way to do it with a single call. Is this possible ?
EDIT: Based on bobince's approach, here's a similar one that does the trick:
function factory(init) {
var run_init = true;
function constr() {
if ( !(this instanceof constr) ) {
run_init = false;
var tmp = new constr();
run_init = true;
init.apply(tmp, arguments);
return tmp;
}
if (run_init)
init.apply(this, arguments);
}
return constr;
}
As for whether this is something that should be encouraged or not, that's debatable. I come from a Python background and I think of new as just noise (Java) or wart (Javascript), but I may be missing something.
This simply encourages a bad-habit shortcut that relies far too heavily on the implementation of the class to "fix" the calling code.
If this is a problem, don't just let it slide, throw an error message.
You can pass a unique value into the constructor for the first call (with new) that signifies you don't want the initialiser called yet:
var _NOINIT= {};
function factory(init) {
function constr() {
if (!(this instanceof constr)) {
var inst= new constr(_NOINIT);
init.apply(inst, arguments);
return inst;
}
if (arguments[0]!==_NOINIT)
init.apply(this, arguments);
}
return constr;
}
Note I've used a named inline function for the constructor because arguments.callee will be going away in ECMAScript Fifth Edition's ‘strict’ mode.
However if you're using a class factory, I suggest making the initialiser function a member of the class, rather than being passed in. That way, you can subclass a base class and have the subclass inherit the initialiser, which is normal behaviour in class-based languages. eg.:
Function.prototype.makeSubclass= function() {
function constr() {
var that= this;
if (!(this instanceof constr))
that= new constr(_NOINIT);
if (arguments[0]!==_NOINIT && '_init' in that)
that._init.apply(that, arguments);
return that;
}
if (this!==Object)
constr.prototype= new this(_NOINIT);
return constr;
};
var Shape= Object.makeSubclass();
Shape.prototype._init= function(x, y) {
this.x= x;
this.y= y;
};
var Point= Shape.makeSubclass();
// inherits initialiser(x, y), as no need for anything else in there
var Circle= Shape.makeSubclass()
Circle.prototype._init= function(x, y, r) {
Shape.prototype._init.call(this, x, y);
this.r= r;
};
Of course you don't have to put that into the Function prototype... it's a matter of taste, really. As is allowing constructors without new.
Personally I prefer to throw an error rather than silently make it work, to try to discourage bare-constructor-calling, since this is a mistake elsewhere and may make the code slightly less clear.
Your "factory" function could be written in this way:
function factory(fn, /* arg1, arg2, ..., argn */) {
var obj = new fn(), // Instantiate using 'new' to preserve the prototype chain
args = Array.prototype.slice.call(arguments, 1); // remove fn argument
fn.apply(obj, args); // apply the constructor again, with the right arguments
return obj;
}
// Test usage:
function SomeConstructor (foo, bar) {
this.foo = foo;
this.bar = bar;
}
SomeConstructor.prototype.test = true;
var o = factory(SomeConstructor, 'foo', 'bar');
// will return: Object foo=foo bar=bar test=true, and
o instanceof SomeConstructor; // true
However, the new operator is not bad, I would not encourage you to avoid it, I would recommend you to stick with a proper naming convention, constructor functions identifiers in PascalCase, all other identifiers in camelCase, and also I highly recommend you to use JsLint it will help you to detect that kind of mistakes early.
I dislike your mixing of arguments.callee and the function's identifier. Also, you are dumbing down the original problem. You should have used apply to begin with so as not to make the helper (factory) function seem even better than it really is.
What should have been done to begin with:
function SomeConstructor(x, y, ...) {
// check if `this` is created with new
if ( !(this instanceof arguments.callee) )
return new arguments.callee.apply (this, arguments);
// normal initialization code follows
Another issue with factory is that it defeats the function's length property.
while 'new' is a good thing, and I don't endorse trying to do away with language features, check out this code I played with a while ago: (note, this is not a complete solution for you, but rather something to build into your code)
function proxy(obj)
{
var usingNew = true;
var obj_proxy = function()
{
if (usingNew)
this.constructor_new.apply(this, arguments);
};
obj_proxy.prototype = obj.prototype;
obj_proxy.prototype.constructor_new = obj.prototype.constructor;
obj_proxy.createInstance = function()
{
usingNew = false;
var instance = new obj_proxy();
instance.constructor_new.apply(instance, arguments);
usingNew = true;
return instance;
}
return obj_proxy;
}
to test it out, create a function foo like this:
function foo(a, b) { this.a = a; }
and test it:
var foo1 = proxy(foo);
var test1 = new foo1(1);
alert(test1 instanceof foo);
var test2 = foo1.createInstance(2);
alert(test2 instanceof foo);
EDIT: removed some code not relevant for this.
If you are interested in dealing with the inability to use apply with new, one could use
Function.prototype.New = (function () {
var fs = [];
return function () {
var f = fs [arguments.length];
if (f) {
return f.apply (this, arguments);
}
var argStrs = [];
for (var i = 0; i < arguments.length; ++i) {
argStrs.push ("a[" + i + "]");
}
f = new Function ("var a=arguments;return new this(" + argStrs.join () + ");");
if (arguments.length < 100) {
fs [arguments.length] = f;
}
return f.apply (this, arguments);
};
}) ();
Example:
var foo = Foo.New.apply (null, argArray);
Here is some broilerplate code I've come up with as a code-template for object factory in AngularjS. I've used a Car/CarFactory as an example to illustrate. Makes for simple implementation code in the controller.
<script>
angular.module('app', [])
.factory('CarFactory', function() {
/**
* BroilerPlate Object Instance Factory Definition / Example
*/
this.Car = function(color) {
// initialize instance properties
angular.extend(this, {
color : null,
numberOfDoors : null,
hasFancyRadio : null,
hasLeatherSeats : null
});
// generic setter (with optional default value)
this.set = function(key, value, defaultValue, allowUndefined) {
// by default,
if (typeof allowUndefined === 'undefined') {
// we don't allow setter to accept "undefined" as a value
allowUndefined = false;
}
// if we do not allow undefined values, and..
if (!allowUndefined) {
// if an undefined value was passed in
if (value === undefined) {
// and a default value was specified
if (defaultValue !== undefined) {
// use the specified default value
value = defaultValue;
} else {
// otherwise use the class.prototype.defaults value
value = this.defaults[key];
} // end if/else
} // end if
} // end if
// update
this[key] = value;
// return reference to this object (fluent)
return this;
}; // end this.set()
}; // end this.Car class definition
// instance properties default values
this.Car.prototype.defaults = {
color: 'yellow',
numberOfDoors: 2,
hasLeatherSeats: null
};
// instance factory method / constructor
this.Car.prototype.instance = function(params) {
return new
this.constructor()
.set('color', params.color)
.set('numberOfDoors', params.numberOfDoors)
.set('hasFancyRadio', params.hasFancyRadio)
.set('hasLeatherSeats', params.hasLeatherSeats)
;
};
return new this.Car();
}) // end Factory Definition
.controller('testCtrl', function($scope, CarFactory) {
window.testCtrl = $scope;
// first car, is red, uses class default for:
// numberOfDoors, and hasLeatherSeats
$scope.car1 = CarFactory
.instance({
color: 'red'
})
;
// second car, is blue, has 3 doors,
// uses class default for hasLeatherSeats
$scope.car2 = CarFactory
.instance({
color: 'blue',
numberOfDoors: 3
})
;
// third car, has 4 doors, uses class default for
// color and hasLeatherSeats
$scope.car3 = CarFactory
.instance({
numberOfDoors: 4
})
;
// sets an undefined variable for 'hasFancyRadio',
// explicitly defines "true" as default when value is undefined
$scope.hasFancyRadio = undefined;
$scope.car3.set('hasFancyRadio', $scope.hasFancyRadio, true);
// fourth car, purple, 4 doors,
// uses class default for hasLeatherSeats
$scope.car4 = CarFactory
.instance({
color: 'purple'
numberOfDoors: 4
});
// and then explicitly sets hasLeatherSeats to undefined
$scope.hasLeatherSeats = undefined;
$scope.car4.hasLeatherSeats.set('hasLeatherSeats', $scope.hasLeatherSeats, undefined, true);
// in console, type window.testCtrl to see the resulting objects
});
</script>
the only thing that worked for me involves dumbing down the implementation. it's ugly but works (both with and without operator new):
var new_ = function (cls)
{
var constructors = [
function ()
{
return new cls();
}
, function ($0)
{
return new cls($0);
}
, function ($0, $1)
{
return new cls($0, $1);
}
, function ($0, $1, $2)
{
return new cls($0, $1, $2);
}
, // up to a chosen limit
];
return function ()
{
return constructors[arguments.length].apply(
this
, arguments
);
}
}
edit to react to comments
I have way-below-average tolerance to repetitive code, and this code hurt to write, but the functions in constructors need to be separate if arguments.length is to mean something in the real constructor function. consider a variant with a single new wrapper:
var new_ = function (cls)
{
// arbitrary limit
var constructor = function ($0, $1, $2)
{
return new cls($0, $1, $2);
};
return function ()
{
return constructor.apply(
this
, arguments
);
}
}
var gen = new_(function ()
{
print(
arguments.length
+ " "
+ Array.prototype.toSource.call(arguments)
);
});
gen("foo") // 3 ["foo", undefined, undefined]
gen("foo", "bar") // 3 ["foo", "bar", undefined]
gen("foo", "bar", "baz") // 3 ["foo", "bar", "baz"]
the parameter list can be arbitrarily wide, but arguments.length doesn't lie only in the special case.
I've been using this solution with the upper limit of 10 arguments for a few years, and I don't remember ever running into the limit. the risk that it'll ever happen is rather low: everybody knows that functions with many parameters are a no-no, and javascript has a better interface for the desired functionality: packing parameters into objects.
so, the only limit is the width of the parameter list, and this limit seems to be purely theoretical. other than that, it supports completely arbitrary constructors, so I'd say it's very general.

Categories

Resources