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');
Related
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);
I would like to known if there is a native way of doing this :
Object.prototype.chain = function(f) { return f.call(this) }
function fun1() {
doSomethingWithObject(this)
return this
}
function fun2() {
doSomethingElse(this)
return this
}
someObject
.method1('something')
.method2()
.chain(checkSomething() ? fun1 : fun2)
.method3()
But I do not feel like changing the prototype of Object. Is there a way to do this without modifying the prototype of Objects or the other constructors that I use (and am not the developer of)
Edits :
I feel I do not explain very well, so let' add some details :
What I would like to do is to use some APIs I do not define. someObject is defined like the following, with chainable methods :
var someObject = {
method1: function(val) {
// do something
return this
},
method2: function() {
// do something
return this
},
method3: function() {
// do something
return this
}
}
Now imagine I cannot change this code, because this object is from a library, and so I don't want to. Then, imagine that I would like to chain methods and some custom functions (see my first snippet) for many more different objects. The simplest thing to do is to attach a chain method to Object.prototype.
But I think that it could result in conflicts in the future. I am looking for a way to do the same thing without touching the prototype.
I'm surprised there are no answers to this to be honest.
There are many ways to natively introduce chaining. I like to use the revealing module pattern.
So I create a basic model (Go ahead and chuck this in your chrome of firefox console)
var Dog = function(name) {
var self = this;
this.name = name;
var core = {
getName:function(){
return self.name;
}
};
this.movement = function(){ //this function will be exposed including its returned functions for chaining
console.log(self.name + " is getting restless... ");
var jump = function(){
console.log(self.name + " jumps around ");
return this //returns the movement scope
};
var run = function(){
console.log(self.name + " has decided to run");
return this //returns the movement scope
};
return {
jump:jump,
run:run
};
}
console.log("A Pup has been born, we shall call him... " + name);
return{
movement:self.movement //only .movement is exposed to the outside world
};
}
Now create a new dog using var p = new Dog("doggyName");
now, you can chain functions. Try:
p.movement().jump().run().jump().run();
You should get the console logged text that corresponds with each function.
By returning the scope of this after executing your movement function you expose the additional functions that are returned in that scope (see the comments in the code). These can then be chained onto the end of your current function provided they are in the same scope. This allows you to scope specific parts of your code. For example with this dog, all movement is scoped to self.movement, you could have all eating scoped to self.eat and so on
Read up on the revealing module pattern. Though this is not the only way to do it.
The wrapper is something that will wrap any object to make it compatible with "chaining" and will add another chain method that will allow you to plug external functions and still get the chaining.
Check this example:
function myObj() {
this.state = {
a: 1
};
this.method1 = function () {
console.log("1");
}
this.method2 = function () {
console.log("2");
}
this.method3 = function () {
console.log("3");
}
this.method4 = function () {
console.log(this.state);
}
}
function objectChainWrapper(obj) {
this.chain = function (fn) {
fn.call(obj);
return this;
}
for (var prop in obj) {
if (obj.hasOwnProperty(prop) && typeof obj[prop] == 'function') {
this[prop] = (function (methodName) {
return function () {
obj[methodName].call(obj);
return this;
}
}(prop))
}
}
}
var obj = new myObj();
var wrapper = new objectChainWrapper(obj);
var chainMethod = function(){ console.log('chain') };
var chainMethodState = function(){ console.log(this.state) };
wrapper.method1().method2().chain(chainMethodState).method3().chain(chainMethod).method4();
JSFIDDLE.
To "plug" an unbound function into the object's method chain you can assign it to a property and call that:
function fn() {
document.write('hi ');
return this;
}
someObj = {
meth1: function() {
document.write('meth1 ');
return this;
},
meth2: function() {
document.write('meth2 ');
return this;
}
}
someObj
.meth1()
[someObj._=fn, '_']()
.meth2()
This doesn't look very pretty if you ask me. A more readable option is to add the chain method on the fly, like:
function chainable(obj) {
obj.chain = function(fn) {
return fn.call(this);
}
return obj;
}
chainable(someObj).meth1().chain(fn).meth2()
I am trying to create an object that gets returned without the new keyword in javascript?
My code structure so far;
myLib.func = (function() {
"use strict";
function func() {
this._init();
};
func.prototype._init = function() {
this.someVar = 5;
};
Return func;
})();
This obviously only works when using the new keyword;
new myLib.func();
How can I make it so that I can just do;
var func = myLib.func();
But it would still return an object that is exactly the same as the first example?
What I have tried
myLib.func = (function() {
"use strict";
function func() {
if (window === this) {
return new myLib.func();
} else {
this._init();
}
};
func.prototype._init = function() {
this.someVar = 5;
};
Return func;
})();
This does not work I learned from an example on slide 25 of John Resig's tips on building a library, http://ejohn.org/blog/building-a-javascript-library/
I know there are already existing frameworks, but rolling my own will make me learn alot, and as you can see that isn't alot at the moment!
In strict mode, the this will be undefined by default. You can account for this feature by adding it to your condition:
if (window === this || undefined === this) {
return new myLib.func();
} else {
this._init();
}
or by checking whether the current object is an instance of the constructor (using instanceof):
if (this instanceof func) {
this._init();
} else {
return new func();
}
(PS. You've got a typo in your code; JavaScript is case-sensitive, so you should use return instead of Return at the end)
If myLib.func is your class name, you cannot call it like var func = myLib.func();.
However, you could wrap this latter into another function, such that you get
var factory = function(){
var func = myLib.func();
return func;
}
If you don't really need public / private methods, you can use object literal
var myLib = {};
myLib.func = {
_init: function() {
this.someVar = 5;
},
doSomething: function(a, b) {
return a + b;
}
};
This will be a singleton object, usually it's enough for javascript applciation. If you need many instances of an object, you'll be forced to put a "new" keyword somewhere.
We have some Foo object
var Foo = function() {
this.bar = function(bazinga) {
if (bazinga) {return this;}
else {return false;}
}
this.show = function() {
alert('bar');
}
};
So it allows us to do some foo.bar().bar().bar().bar(); chain.
But if in the middle of chain, bar() will return false, the next bar() attempts will cause error that undefined has no method bar() whitch is ofc thing.
So how to make all chain return false without errors whan any of its 'rings' return false?
FIDDLE
You will have to change the return type of bar. I suggest to create a sort of null object for that purpose and add a finalization method at the end of the chain which returns false for the null object:
var Foo = function() {
var nullFoo = function() {
this.finalize = function() { return false; }
this.bar = function() { return this; }
}
this.finalize = function() { return this; }
this.bar = function(bazinga) {
if (bazinga) {return this;}
else {return new nullFoo();}
}
this.show = function() {
alert('bar');
}
};
foo.bar().bar().bar().bar().finalize();
For your fiddle example I did not use the finalize method but instead gave the null object a show method. Otherwise you still would have false.show() at the end:
Fiddle
Method chains can be a bit dangerous. It is all to do with dependencies.
When using method chaining it is a good idea to keep an eye on the objects that each part of the chain returns.
If they are not all the same, e.g they aren't all strings, then it is a good idea to break down the chain into separate statements. Or perhaps extract them into a separate function.
Lets say you have some chain such as
somearray.pop().getATagWrapper().getTag().setSrc("http://www.stackoverflow.com")
The implicit dependencies for the chain are Array, Object1, TagWrapper, Tag, String
Now the the function you have written is now coupled to all of these objects and any change to these can potentially wreck havoc with your code.
Where as if we look at
someString.trim().substr(12).trim().concat("with awesome")
All deals with only the String object.
For more info see Law of Demeter
Is this what you want: http://jsfiddle.net/c24w/MXgzx/5/
JavaScript:
var Foo = function() {
var returnValue = true;
this.bar = function(bazinga) {
returnValue = returnValue && bazinga;
return this;
}
this.show = function() {
alert('bar');
return returnValue;
}
};
I've been building a small JS framework for use at my job, and I'd like to employ Douglas Crockford's prototypical inheritance patterns. I think I get the general idea of how the prototype object works, but what isn't clear is the way in which I would use this pattern beyond the simplest example.
I'll flesh it out to the point that I understand it.
(function () {
'use strict';
var Vehicles = {};
Vehicles.Vehicle = function () {
this.go = function () {
//go forwards
};
this.stop = function () {
//stop
};
};
Vehicles.Airplane = Object.create(Vehicles.Vehicle());
}());
So now my Vehicles.Airplane object can go() and stop(), but I want more. I want to add takeOff() and land() methods to this object. I could just use ugly dot notation afterwards:
Vehicles.Airplane.takeOff = function () {
//take off stuff
}
But that seems wrong, especially if I were to add many methods or properties. The question asked at here seems to be very similar to mine, but the answer doesn't quite ring true for me. The answer suggests that I should build an object literal before using Object.create, and that I should pass that object literal into the create method. In the example code given, however, it looks like their new object inherits nothing at all now.
What I'm hoping for is some syntax similar to:
Vehicles.Airplane = Object.create(Vehicles.Vehicle({
this.takeOff = function () {
//takeOff stuff
};
this.land = function () {
//land stuff
};
}));
I know this syntax will break terribly with Object.create right now, because of course I'm passing Vehicle.Vehicle a function rather than an object literal. That's beside the point. I'm wondering in what way I should build new properties into an object that inherits from another without having to list them out one at a time with dot notation after the fact.
EDIT:
Bergi, after some anguished thought on the topic, I think I really want to go with what you described as the "Classical Pattern". Here is my first stab at it (now with actual code snippets rather than mocked up hypotheticals - You even get to see my crappy method stubs):
CS.Button = function (o) {
o = o || {};
function init(self) {
self.domNode = dce('a');
self.text = o.text || '';
self.displayType = 'inline-block';
self.disabled = o.disabled || false;
self.domNode.appendChild(ctn(self.text));
if (o.handler) {
self.addListener('click', function () {
o.handler(self);
});
}
}
this.setText = function (newText) {
if (this.domNode.firstChild) {
this.domNode.removeChild(this.domNode.firstChild);
}
this.domNode.appendChild(ctn(newText));
};
init(this);
};
CS.Button.prototype = Object.create(CS.Displayable.prototype, {
constructor: {value: CS.Button, configurable: true}
});
CS.Displayable = function (o) { // o = CS Object
o = o || {};
var f = Object.create(new CS.Element(o));
function init(self) {
if (!self.domAnchor) {
self.domAnchor = self.domNode;
}
if (self.renderTo) {
self.renderTo.appendChild(self.domAnchor);
}
}
//Public Methods
this.addClass = function (newClass) {
if (typeof newClass === 'string') {
this.domNode.className += ' ' + newClass;
}
};
this.addListener = function (event, func, capture) {
if (this.domNode.addEventListener) {
this.domNode.addEventListener(event, func, capture);
} else if (this.domNode.attachEvent) {
this.domNode.attachEvent('on' + event, func);
}
};
this.blur = function () {
this.domNode.blur();
};
this.disable = function () {
this.disabled = true;
};
this.enable = function () {
this.disabled = false;
};
this.focus = function () {
this.domNode.focus();
};
this.getHeight = function () {
return this.domNode.offsetHeight;
};
this.getWidth = function () {
return this.domNode.offsetWidth;
};
this.hide = function () {
this.domNode.style.display = 'none';
};
this.isDisabled = function () {
return this.disabled;
};
this.removeClass = function (classToRemove) {
var classArray = this.domNode.className.split(' ');
classArray.splice(classArray.indexOf(classToRemove), 1);
this.domNode.className = classArray.join(' ');
};
this.removeListener = function () {
//Remove DOM element listener
};
this.show = function () {
this.domNode.style.display = this.displayType;
};
init(this);
};
CS.Displayable.prototype = Object.create(CS.Element.prototype, {
constructor: {value: CS.Displayable, configurable: true}
});
I should be quite clear and say that it's not quite working yet, but mostly I'd like your opinion on whether I'm even on the right track. You mentioned "instance-specific properties and methods" in a comment in your example. Does that mean that my this.setText method and others are wrongly placed, and won't be available to descendant items on the prototype chain?
Also, when used, it seems that the order of declaration now matters (I can't access CS.Displayable.prototype, because (I think) CS.Button is listed first, and CS.Displayable is undefined at the time that I'm trying to reference it). Is that something I'll just have to man up and deal with (put things in order of ancestry in the code rather than my OCD alphabetical order) or is there something I'm overlooking there as well?
Vehicles.Airplane = Object.create(Vehicles.Vehicle());
That line is wrong. You seem to want to use new Vehicles.Vehicle - never call a constructor without new!
Still, I'm not sure which pattern you want to use. Two are coming to my mind:
Classical Pattern
You are using constructor functions just as in standard JS. Inheritance is done by inheriting the prototype objects from each other, and applying the parent constructor on child instances. Your code should then look like this:
Vehicles.Vehicle = function () {
// instance-specific properties and methods,
// initialising
}
Vehicles.Vehicle.prototype.go = function () {
//go forwards
};
Vehicles.Vehicle.prototype.stop = function () {
//stop
};
Vehicles.Airplane = function() {
// Vehicles.Vehicle.apply(this, arguments);
// not needed here as "Vehicle" is empty
// maybe airplane-spefic instance initialisation
}
Vehicles.Airplane.prototype = Object.create(Vehicles.Vehicle.prototype, {
constructor: {value:Vehicles.Airplane, configurable:true}
}); // inheriting from Vehicle prototype, and overwriting constructor property
Vehicles.Airplane.prototype.takeOff = function () {
//take off stuff
};
// usage:
var airplane = new Vehicles.Airplace(params);
Pure Prototypical Pattern
You are using plain objects instead of constructor functions - no initialisation. To create instances, and to set up inheritance, only Object.create is used. It is like having only the prototype objects, and empty constructors. instancof does not work here. The code would look like this:
Vehicles.Vehicle = {
go: function () {
//go forwards
},
stop: function () {
//stop
}
}; // just an object literal
Vehicles.Airplane = Object.create(Vehicles.Vehicle); // a new object inheriting the go & stop methods
Vehicles.Airplane.takeOff = function () {
//take off stuff
};
// usage:
var airplane = Object.create(Vehicles.Airplane);
airplane.prop = params; // maybe also an "init" function, but that seems weird to me
You got Object.create wrong. The first argument should be an object (maybe that's why people suggested you pass a literal).
In your first example, you're actually passing undefined:
Vehicles.Airplane = Object.create(Vehicles.Vehicle()); // the function call will
// return undefined
The following would work, but it's not very Crockford-ish:
Vehicles.Airplane = Object.create(new Vehicles.Vehicle());
The way I believe Crockford would do it (or, at least, wouldn't complain of):
var Vehicles = {};
Vehicles.Vehicle = {
go : function() {
// go stuff
},
stop : function() {
// go stuff
}
};
Vehicles.Airplane = Object.create(Vehicles.Vehicle, {
takeOff : {
value : function() {
// take-off stuff
}
},
land : {
value: function() {
// land stuff
}
}
});
Note that Vehicles.Vehicle is just a literal, which will be used as the prototype for other objects. When we call Object.create, we pass Vehicles.Vehicle as the prototype, and takeOff and land will be own properties of Vehicles.Airplane. You may then call Object.create again, passing Vehicles.Airplane as the prototype, if you want to create e.g. a Boeing.
The own properties passed as the second parameter are packed in an object that contains a representation of their property descriptors. The outer keys are the names of your properties/methods, and each one points to another object containing the actual implementation as the value. You may also include other keys like enumerable; if you don't they'll take the default values. You can read more about descriptors on the MDN page about Object.defineProperty.