I am trying to understand how private methods are created using coffeescript. Following is an example code
class builders
constructor: ->
// private method
call = =>
#priv2Method()
// privileged method
privLedgeMethod: =>
call()
// privileged method
priv2Method: =>
console.log("got it")
Following is the generated JS code.
(function() {
var builders,
__bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
builders = (function() {
var call,
_this = this;
function builders() {
this.priv2Method = __bind(this.priv2Method, this);
this.privLedgeMethod = __bind(this.privLedgeMethod, this);
}
call = function() {
return builders.priv2Method();
};
builders.prototype.privLedgeMethod = function() {
return call();
};
builders.prototype.priv2Method = function() {
return console.log("got it");
};
return builders;
}).call(this);
}).call(this);
Note that I have used "fat arrow" for the function definition. There are couple of things that I didn't get from the code.
what's the use of _this variable
if you run this code as : (new builders()).privLedgeMethod() than inside the call method it doesn't find priv2Method method. Even though the builders object does show priv2Method as a property of it's prototype.
Hopefully someone could shed some light here.
Your call function is not a private method, there is no such thing in JavaScript so there is no such thing in CoffeeScript.
A more accurate description of call would be:
a function that is only visible inside the builders class.
Defining call with => creates something the behaves like a private class method. Consider this:
class C
p = => console.log(#)
m: -> p()
c = new C
c.m()
If you look in the console you'll see that # inside p is the class C itself, you'll see something in the console that looks like a function but that's all a CoffeeScript class is. That's why you see the var _this in the JavaScript. When CoffeeScript sees => and you're not defining a method, CoffeeScript uses the standard var _this = this trick to make sure the references come out right in the bound function.
Also note that your call is, more or less, a private class method so there is no builders instance and you can't call the instance method priv2Method without a builders instance.
Your privLedgeMethod method:
privLedgeMethod: =>
call()
will work fine because call is a function (not a method), the function happens to be bound to the class but it is still just a function. Hence the lack of an # prefix when you call(). If call was a proper class method:
#call: -> ...
then you'd call it as a class method in the usual way:
#constructor.call()
Here's a simple demo that might clarify things: http://jsfiddle.net/ambiguous/tQv4E/
Related
In Python, you can implement __call__() for a class such that calling an instance of the class itself executes the __call__() method.
class Example:
def __call__(self):
print("the __call__ method")
e = Example()
e() # "the __call__ method"
Do JavaScript classes have an equivalent method?
Edit
A summary answer incorporating the discussion here:
Python and JavaScript objects are not similar enough for a true equivalent (prototype vs. class based, self, vs. this)
The API can be achieved however using proxy or modifying the prototype – and maybe using bind?
One should generally not do this: it is too far removed from the JS's structure and from the normal use of of JavaScript, potentially creating confusion among other JS devs, your future-self, and could result in idiosyncratic side-effects.
I basically agree with #CertainPerformace that this isn't really something you would do in normal JS code. Having said that, proxies offer a lot of possibilities and you can create something that is surprisingly close to (on the surface) to Python's __call__().
For example:
class F extends Function{
constructor(someID, arr, n){
super()
this.id = someID
this.arr = arr
this.n = n
return new Proxy(this, {
apply(target, thisArg, argumentsList) {
return target.__call__(...argumentsList);
}
})
}
__call__(a){ // simple mult functions
return a * this.n
}
*[Symbol.iterator](){ // make it iterable for demo purposes
yield *this.arr.map(this) // call itself in a map!
}
}
let f = new F("FrankenFunction", [1, 2, 3, 4], 5)
// access instance variable
console.log("id:", f.id)
// call it
console.log("calling with 100: ", f(100))
// use the iterator
// get multiples of calling this on arr
console.log([...f])
// change the __call__ function to power instead
F.prototype.__call__ = function(a){
return a ** this.n
}
// change n to get squares:
f.n = 2
// call it again with new __call__
console.log("calling with 10:", f(10)) // 10**2
console.log([...f]) // or iterate
I really not sure if any of this is a good idea, but it's an interesting experiment.
The only way to do this would be for the constructor to explicitly return a function, which can be called. (In Javascript, if you don't explicitly return inside a constructor, the newly created instance gets returned - but such an instance will be a plain object, not a function.)
class Example {
constructor() {
return function() {
console.log('function running');
}
}
}
const e = new Example();
e();
But this would be really weird to do, and would not allow you to reference any of the properties on the prototype, or anything like that. Better to avoid it, or to make a plain function that returns a function:
const example = () => () => console.log('function running');
const e = example();
e();
You can get this done, but in a rather weird way.
There isn't anything like __call__(), __add__() or __sub__() in JavaScript - JavaScript does not support operator overloading.
However if you really want to make objects callable, you can do it by giving a function a different prototype:
function createCallableObject(cls, fn) {
// wrap the original function to avoid modifying it
const wrapper = (...args) => fn(...args)
// set the prototype of the wrapped function so we can call class methods
Object.setPrototypeOf(wrapper, cls.prototype)
return wrapper
}
class Example {
method() { console.log('This is an instance of class Example') }
}
function example() { console.log('This is a function') }
const obj = createCallableObject(Example, example)
obj() // 'This is a function'
obj.method() // 'This is an instance of class Example'
console.log(obj.constructor) // 'class Example { ... }'
I'm working on building a collection of prototype helper methods inside a wrapper. However for ease of use, I'd like to be able to call the object as both a new instance and single global instance under the same call.
For example, with jQuery, you can call both "$" and "$()" which can be used differently http://learn.jquery.com/using-jquery-core/dollar-object-vs-function/:
So given the bellow as simple example, how could I do something similar?
(function () {
var myWrapper = function (foo) {
return new helper(foo);
};
var helper = function (foo) {
this[0] = foo;
return this;
}
helper.prototype = {
putVar: function(foo) {
this[0] = foo;
}
}
if(!window.$) {
window.$ = myWrapper;
}
})();
// create an new instace;
var instance = $("bar");
console.log(instance);
// call a prototype method
instance.putVar("foo");
console.log(instance);
// call a prototype method using the same call without new instance
// this doesnt work :(
$.putVar("foo");
// however this will work
window.myLib = $("foo");
myLib.putVar("bar");
http://jsfiddle.net/2ywsunb4/
If you want to call $.putVar, you should define putVar like this:
myWrapper.putVar = function (foo) {
console.log('Work');
}
In your code, the instance and myLib are the same thing, they are both instances created by you.
If you want to call both $.putVar and $(...).putVar, you should add the code I show you above. That means you have to define two putVar functions, one is used like a 'instance' method, while the other one is used like a 'static' method.
If you search through jQuery source code, you will see two each functions defined. That's why you can all both $.each(...) and $(...).each for different usages.
I've been reading SO posts all day and I haven't come up with anything that is working for me.
I have a JS object
function MyObject(a,b){
this.member_a = a;
this.member_b = b;
function operation1(){
$('#someDiv1').text(this.a);
}
function operation2(){
$('#someDiv1').text(this.b);
}
MyObject.prototype.PublicFunction1 = function(){
//There is an ajax call here
//success
operation1();
//failure
operation2();
}
}
Roughly like that. That's the pattern I'm at right now. It's in an external JS file. My page creates a MyObject(a,b) and the breakpoints show that member_a and member_b are both initialized correctly. After some other magic happens from my page callsMyObject.PublicFunction1();, the ajax executes and I enter operation1() or operation2() but when I am inside of those member_a and member_b are both undefined and I don't understand why. I'm losing the scope or something. I've had the private function and the prototypes outside the object body declaration, combinations of both. How can I call a private function from an object's prototype to work on the object's data?
I've also tried
ClassBody{
vars
private function
}
prototype{
private function call
}
and have been reading this
operation1 and operation2 do not have a context and are thus executed in the global context (where this == window).
If you want to give them a context, but keep them private, then use apply:
operation1.apply(this);
operation2.apply(this);
Further reading on the apply method https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply
EDIT
#FelixKing is correct - your code should more appropriately be written like this (using the Module Pattern):
//encapsulating scope
var MyObject = (function() {
function operation1(){
$('#someDiv1').text(this.a);
}
function operation2(){
$('#someDiv1').text(this.b);
}
var MyObject = function(a,b) {
this.member_a = a;
this.member_b = b;
};
MyObject.prototype.PublicFunction1 = function(){
//There is an ajax call here
//success
operation1.apply(this);
//failure
operation2.apply(this);
}
return MyObject;
}());
I have built a tool to allow you to put private methods onto the prototype chain. This way you'll save on memory allocation when creating multiple instances.
https://github.com/TremayneChrist/ProtectJS
Example:
var MyObject = (function () {
// Create the object
function MyObject() {}
// Add methods to the prototype
MyObject.prototype = {
// This is our public method
public: function () {
console.log('PUBLIC method has been called');
},
// This is our private method, using (_)
_private: function () {
console.log('PRIVATE method has been called');
}
}
return protect(MyObject);
})();
// Create an instance of the object
var mo = new MyObject();
// Call its methods
mo.public(); // Pass
mo._private(); // Fail
Here's a sample of a simple Javascript class with a public and private method (fiddle: http://jsfiddle.net/gY4mh/).
function Example() {
function privateFunction() {
// "this" is window when called.
console.log(this);
}
this.publicFunction = function() {
privateFunction();
}
}
ex = new Example;
ex.publicFunction();
Calling the private function from the public one results in "this" being the window object. How should I ensure my private methods are called with the class context and not window? Would this be undesirable?
Using closure. Basically any variable declared in function, remains available to functions inside that function :
var Example = (function() {
function Example() {
var self = this; // variable in function Example
function privateFunction() {
// The variable self is available to this function even after Example returns.
console.log(self);
}
self.publicFunction = function() {
privateFunction();
}
}
return Example;
})();
ex = new Example;
ex.publicFunction();
Another approach is to use "apply" to explicitly set what the methods "this" should be bound to.
function Test() {
this.name = 'test';
this.logName = function() {
console.log(this.name);
}
}
var foo = {name: 'foo'};
var test = new Test();
test.logName()
// => test
test.logName.apply(foo, null);
// => foo
Yet another approach is to use "call":
function Test() {
this.name = 'test';
this.logName = function() {
console.log(this.name);
}
}
var foo = {name: 'foo'};
var test = new Test();
test.logName()
// => test
test.logName.call(foo, null);
// => foo
both "apply" and "call" take the object that you want to bind "this" to as the first argument and an array of arguments to pass in to the method you are calling as the second arg.
It is worth understanding how the value of this in javascript is determined in addition to just having someone tell you a code fix. In javascript, this is determined the following ways:
If you call a function via an object property as in object.method(), then this will be set to the object inside the method.
If you call a function directly without any object reference such as function(), then this will be set to either the global object (window in a browser) or in strict mode, it will be set to undefined.
If you create a new object with the new operator, then the constructor function for that object will be called with the value of this set to the newly created object instance. You can think of this as the same as item 1 above, the object is created and then the constructor method on it is called.
If you call a function with .call() or .apply() as in function.call(xxx), then you can determine exactly what this is set to by what argument you pass to .call() or .apply(). You can read more about .call() here and .apply() here on MDN.
If you use function.bind(xxx) this creates a small stub function that makes sure your function is called with the desired value of this. Internally, this likely just uses .apply(), but it's a shortcut for when you want a single callback function that will have the right value of this when it's called (when you aren't the direct caller of the function).
In a callback function, the caller of the callback function is responsible for determining the desired value of this. For example, in an event handler callback function, the browser generally sets this to be the DOM object that is handling the event.
There's a nice summary of these various methods here on MDN.
So, in your case, you are making a normal function call when you call privateFunction(). So, as expected the value of this is set as in option 2 above.
If you want to explictly set it to the current value of this in your method, then you can do so like this:
var Example = (function() {
function Example() {
function privateFunction() {
// "this" is window when called.
console.log(this);
}
this.publicFunction = function() {
privateFunction.call(this);
}
}
return Example;
})();
ex = new Example;
ex.publicFunction();
Other methods such as using a closure and defined var that = this are best used for the case of callback functions when you are not the caller of the function and thus can't use 1-4. There is no reason to do it that way in your particular case. I would say that using .call() is a better practice. Then, your function can actually use this and can behave like a private method which appears to be the behavior you seek.
I guess most used way to get this done is by simply caching (storing) the value of this in a local context variable
function Example() {
var that = this;
// ...
function privateFunction() {
console.log(that);
}
this.publicFunction = function() {
privateFunction();
}
}
a more convenient way is to invoke Function.prototype.bind to bind a context to a function (forever). However, the only restriction here is that this requires a ES5-ready browser and bound functions are slightly slower.
var privateFunction = function() {
console.log(this);
}.bind(this);
I would say the proper way is to use prototyping since it was after all how Javascript was designed. So:
var Example = function(){
this.prop = 'whatever';
}
Example.prototype.fn_1 = function(){
console.log(this.prop);
return this
}
Example.prototype.fn_2 = function(){
this.prop = 'not whatever';
return this
}
var e = new Example();
e.fn_1() //whatever
e.fn_2().fn_1() //not whatever
Here's a fiddle http://jsfiddle.net/BFm2V/
If you're not using EcmaScript5, I'd recommend using Underscore's (or LoDash's) bind function.
In addition to the other answers given here, if you don't have an ES5-ready browser, you can create your own "permanently-bound function" quite simply with code like so:
function boundFn(thisobj, fn) {
return function() {
fn.apply(thisobj, arguments);
};
}
Then use it like this:
var Example = (function() {
function Example() {
var privateFunction = boundFn(this, function() {
// "this" inside here is the same "this" that was passed to boundFn.
console.log(this);
});
this.publicFunction = function() {
privateFunction();
}
}
return Example;
}()); // I prefer this order of parentheses
Voilà -- this is magically the outer context's this instead of the inner one!
You can even get ES5-like functionality if it's missing in your browser like so (this does nothing if you already have it):
if (!Function.prototype.bind) {
Function.prototype.bind = function (thisobj) {
var that = this;
return function() {
that.apply(thisobj, arguments);
};
}:
}
Then use var yourFunction = function() {}.bind(thisobj); exactly the same way.
ES5-like code that is fully compliant (as possible), checking parameter types and so on, can be found at mozilla Function.prototype.bind. There are some differences that could trip you up if you're doing a few different advanced things with functions, so read up on it at the link if you want to go that route.
I would say assigning self to this is a common technique:
function Example() {
var self = this;
function privateFunction() {
console.log(self);
}
self.publicFunction = function() {
privateFunction();
};
}
Using apply (as others have suggested) also works, though it's a bit more complex in my opinion.
It might be beyond the scope of this question, but I would also recommend considering a different approach to JavaScript where you actually don't use the this keyword at all. A former colleague of mine at ThoughtWorks, Pete Hodgson, wrote a really helpful article, Class-less JavaScript, explaining one way to do this.
How is it that jQuery can do $("#foo").addClass("bar") and $.ajax()?
I'm creating a micro javascript framework and want to create a new instance of an object, such as $("#hello"). With this object there are associated methods, such as addClass, css, etc, just like with jQuery. So I could do something like
$("#foo").addClass("remove").css("color", "red");
I have been successful in creating this. However, when I want to call a method from this object, such as $.ajax, the constructor function is overwritten, and I can call $.ajax, but not $("#foo").
Basically, how can jQuery do both?
$ = function(arg) { console.log("$ function called with " + arg); }
$.ajax = function(arg) {console.log("$.ajax called with " + arg);}
$('foo');
$.ajax('bar');
http://jsfiddle.net/ac7nx/
I don't think there's any magic here. $ is just a name for the global function. Just keep in mind that in javascript, functions are first class objects that can have their own properties, including sub-functions, which is what $.ajax is.
Since you mentioned the constructor function, I should note that there are no OO objects being used here, just regular functions (no new keyword), so constructor functions don't play into this. If you are using the new keyword, that is probably where you are getting confused. If you want $('#foo') to return a new object, then inside the $ function's code you should create a new object using new and return that, which is what jQuery does, but the $ function itself is not a constructor and should not be called with new. Or in the case of something like $('#someID'), inside that function jQuery is getting an element object from the DOM and then returning that object, but still $ is just a regular function whose return value is an object, not a constructor function.
OK, the $ function is not only a function but an object, like all functions. So it can have methods. That's all that ajax is, a method of the $ function. So we can start off by doing this:
$ = function(obj) {
// some code
};
$.ajax = function (arg1, arg2) {
// some ajax-y code
};
So far so good. Now, what on earth do we put in the $ function? Well it has to return an object and that object has to have some nice methods defined on it. So we'll need a constructor function (to give us new objects) and a prototype (to provide the nifty methods for those objects).
$ = function(obj) {
var myConstructor = function (obj) {
this.wrappedObj = obj;
};
myConstructor.prototype = {
niftyMethod: function () {
// do something with this.wrappedObj
return this; // so we can chain method calls
},
anotherNiftyMethod: function (options) {
// do something with this.wrappedObj and options
return this;
}
};
return new myConstructor(obj);
};
So there we have it. We can do this:
var mySnazzObject = $("whatever");
mySnazzObject.niftyMethod().anotherNiftyMethod(true);
And we can do this:
$.ajax("overthere.html", data);
Obviously jQuery does a heck of a lot more than that, and it does it in some really impressive ways, but that's the general idea.
UPDATE: AS #Raynos was kind enough to observe without supplying a constructive answer, my original code would create the prototype ad infinitum. So we make use of an anonymous autoexecuting function to declare the constructor and prototype separately:
(function () {
var myConstructor = function (obj) {
this.wrappedObj = obj;
};
myConstructor.prototype = {
niftyMethod: function () {
// do something with this.wrappedObj
return this; // so we can chain method calls
},
anotherNiftyMethod: function (options) {
// do something with this.wrappedObj and options
return this;
}
};
var $ = function(obj) {
return new myConstructor(obj);
};
$.ajax = function (arg1, arg2) {
// some ajax-y code
};
window.$ = $;
}());
Two different things:
$.ajax is a prototyped function of jQuery called ajax.
While $('foo') is a call of the jQuery function and depending on the type of foo it reacts in different ways. At http://api.jquery.com/jQuery you can see that jQuery (almost the same as $) can react on three different types: selectors, html or callbacks.
You can imitate the magic jQuery behaviour as following:
//define
var _$ = {};
var $ = function(selector) {
_$.html = function(args) {
alert("Doing the method 'html' with arguments " + args
+ " for selector " + selector);
return _$; //needed for pipeline
};
return _$;
};
$.ajax = function(args){alert("Doing the method 'ajax' "); }
//and try
$("selector1").html("args1").html("args2");
$.ajax("args2");