I'm playing with JS "oop" (or should i say "pop" - prototype oriented programming?) and tried to do something like static method and functions. I can call function from prototype object but that isn't exactly static function, is it?
I have that code:
function a(val){
this.val = val;
console.log(this);
this.foo = function(){
console.log('hi');
}
}
And i want to call the foo function without creating a new object. Is something like this is possible? How can i do it?
If you're trying to mimic static functions, you'll need to add the methods after you create the "class":
function A(val) {
this.val = val;
}
A.foo = function() {
console.log('hi');
}
Then you can call A.foo(); without having to use the prototype. It's a lot like the way Object.keys works. This makes it a property of the function, rather than a property of the instance, like static functions.
As written, you cannot call foo without instantiating a.
The best you can do is hide the instantiation of the object like so:
var a = {
foo: function() {
console.log('hi');
}
};
As regard to simulating static functions, this is the closest you can come:
function A(val) {
this.val = val;
console.log(this);
}
A.prototype.foo = function() { console.log('hi'); };
You do not need to instantiate A to call foo at this point.
A.prototype.foo(); // 'hi'
Related
I'm trying to get my head around Javascript OO, using the IIFE module pattern to mimic a class:
var MyClass = (function() {
// Constructor
return function() {
return {
foo: 'foo'
}
}
}());
I'm passing arguments with something like:
var MyClass = (function() {
// Constructor
return function(arg) {
return {
foo: function() {
return 'foo'+arg
}
}
}
}());
To mimic classical inheritance I am using the pattern suggested here:
function inherit(base, child, obj) {
child.prototype = Object.create(base.prototype);
child.prototype.constructor = child;
obj&&Object.keys(obj).forEach(function(key){
child.prototype[key] = obj[key];
})
}
var Base = (function() {
var init = function() {};
init.prototype = {
foo: function() {
return "foo";
}
};
return init;
}());
var Child = (function() {
var init = function() {
Base.call(this);
};
inherit(Base, init, {
bar: function() {
return 'bar';
}
});
return init;
}());
So far so good. My only problem is in understanding how to pass parameters to my class constructor when I'm doing inheritance in the above way. I like the fact that in the 'pure' IIFE module I can simply refer to the constructor parameter in any functions defined within it, so that they become closures. But how do I access constructor params when I'm adding these subsequent functions using the constructor variable, as in the inheritance example above? I suppose I could do something like:
var init = function(arg) {
this.theArg = arg;
};
Then I can access it within anything subsequent:
init.prototype = {
foo: function() {
return "foo"+this.theArg;
}
};
And for the child:
var init = function(arg) {
Base.call(this, arg);
};
This makes arg available to the outside world, so to make it read-only I suppose a getter would work:
var init = function(arg) {
var theArg = arg;
this.getArg = function() { return theArg };
};
On the face of it I can't see anything wrong with that, and I can't think of a better alternative. Is there one? Am I missing something obvious?
I can't think of a better alternative. Is there one?
No. Not in your example.
I like the fact that in the 'pure' IIFE module I can simply refer to the constructor parameter in any functions defined within it, so that they become closures.
You can access args in each function because, in your first example, you are defining foo on each object instance separately. Therefore each definition of foo has a separate closure containing the args passed when it was defined.
This is also only possible, because foo is defined within the scope containing your args.
But how do I access constructor params when I'm adding these subsequent functions ... in the inheritance example above?
By using the classical inheritance patten you found, you are now defining the foo function on the constructor prototype. This means that only a single foo definition exists which is inherited by all instances created using your constructor. So foo can not be made specific to each instance anymore.
As you have figured, it also means foo is no longer defined inside the scope containing args and has no direct access.
You are therefore correct by assigning args to this.thisArgs which allows foo to access thisArgs on each instance. You have made foo a general case function that can handle any instance it is applied to.
Passing arguments to the IIFE constructor: The IIFE itself is not the constructor, it simply builds the constructor object. The IIFE's scope has long since been returned by the time the constructor itself is invoked.
Am I missing something obvious?
Yes. Javascript is a prototypical language. It was never meant to be like "classical" languages. Just let it be Javascript. :)
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
My question is rather weird, it has to do with something i have seen in jQuery but so far i have been unable to recreate it.
in jQuery you can go like this
jQuery('div').append
or
jQuery.ajax
the application i am making would need a similar syntax, i notice if you use new like
var that=new function(){
}
you can call the function with just that, without the (), but in some cases i would need it.
The reason for this is some functions i have need to select a dom element just like jQuery so.
that('[data-something="this"]').setEvent('click',functin(){})
and some automatically do it so:
that.loadIt('this','[data-something="that"]')
the reason for this is that the dom elements are loaded externally and pushed, then the script waits for it to be ready before continuing. and doing it this way, to me anyway seems like the most cleanest way to get this functionality (i am coding a full javascript framework so i avoid libraries to keep the scripts fast)
Functions are objects.
Just get rid of new, and add properties directly to that.
var that = function() {
// do some work
}
that.loadit = function() {
// do other work
}
Since you're trying to achieve something like jQuery does, then have that call a constructor.
;(function(global) {
// function to be publicly exposed
var that = function(foo, bar) {
return new MyLibrary(foo, bar);
}
// publicly expose the function
global.that = that;
// use the function as a namespace for utilities
that.loadit = function() {
// do other work
}
// The actual constructor function, like the internal jQuery constructor
MyLibrary(foo, bar) {
// constructor function
}
// Prototypal inheritance of objects created from the constructor
MyLibrary.prototype.setEvent = function() {
// do some work
return this; // allows for method chaining
};
MyLibrary.prototype.otherMethod = function() {
// do something else
return this; // allows for method chaining
};
})(this);
Functions are objects and can have properties, just like other objects can. So, you can add a property to a function like this:
function myFunc(){}
myFunc.someFunc = function(){}
If you use new myFunc the resulting object won't have someFunc as it's not part of the prototype.
So, you can make something like this:
function myFunc(){
// This lets you do "myFunc()" instead of "new myFunc()"
if (!(this instanceof myFunc)) {
return new myFunc();
}
else{
this.val = 0;
this.setVal = function(x){
this.val = x;
// for function chaining
return this;
}
this.getVal = function(){
return this.val;
}
}
}
// This function is not part of the prototype
myFunc.test = function(){
alert('hi');
}
// Some tests
var obj = myFunc();
obj.setVal(12).getVal(); // 12
myFunc.test();
obj.test(); // Error: 'test' is not a function
myFunc.getVal(); // Error: 'getVal' is not a function
$.fn.loadIt=function(var1,var2) {
// $(this) is automatically passed
// do stuff
}
call it like this
$('#element').loadIt('a variable','another variable');
I am creating an AJAX API for a web service and I want to be able to call jQuery-like accessors.
jQuery seems to be able to execute 'jQuery' as a function, but also use it to directly access the object that is the result of the function EG:
jQuery();
jQuery.each({});
This is the trick that I can't seem to pull off:
myAPI('foo'); //output: 'foo'
myAPI('foo').changeBar(); //output: 'foo' 1
myAPI.changeBar(); //Error: not a function
I have seen the answers to similar questions, which are helpful, but don't really answer my question.
#8734115 - Really interesting, but you can't access the methods that were set by f.prototype.
#2953314 - Uses Multiple operations to create object instead of a single function.
here is my code:
(function(window) {
var h = function(foo) {
// The h object is actually just the init constructor 'enhanced'
return new h.fn.init(foo);
};
/**
* Methods defined at protoype.
*/
h.fn = h.prototype = {
constructor: h,
init: function(foo) {
console.log(foo);
return this;
},
splice : function () {},
length : 0,
bar : 0,
changeBar : function() {
this.bar++;
return this.bar;
}
};
h.fn.init.prototype = h.fn;
//Publish
window.myAPI =h;
}( window));
I'm sure I'm missing something simple :(
What jQuery is doing there is using jQuery as both a function and as a pseudo-namespace. That is, you can call jQuery: var divs = jQuery("div"); and you can use properties on it, e.g.: jQuery.each(...);.
This is possible because in JavaScript, functions are first-class objects, and so you can add arbitrary properties to them:
function foo() {
alert("Foo!");
}
foo.bar = function() {
alert("Bar!");
};
foo(); // "Foo!"
foo.bar(); // "Bar!"
That's literally all there is to it.
Within the call to bar, this will be the foo function (because this is determined entirely by how a function is called, not where it's defined). jQuery doesn't use this to refer to itself (usually it uses this to refer to DOM elements, sometimes to other things like array elements; when referring to itself, since it's a single thing, it just uses jQuery).
Now, you might want to ensure that your functions have proper names (whereas the function I assigned to bar above is anonymous — the property has a name, but the function does not). In that case, you might get into the module pattern:
var foo = (function() {
function foo() {
alert("Foo!");
}
function foo_bar() {
alert("Bar!");
}
foo.bar = foo_bar;
return foo;
})();
foo(); // "Foo!"
foo.bar(); // "Bar!"
That pattern also has the advantage that you can have private data and functions held within the scoping function (the big anonymous function that wraps everything else) that only your code can use.
var foo = (function() {
function foo() {
reallyPrivate("Foo!");
}
function foo_bar() {
reallyPrivate("Bar!");
}
function reallyPrivate(msg) {
alert(msg);
}
foo.bar = foo_bar;
return foo;
})();
foo(); // "Foo!"
foo.bar(); // "Bar!"
reallyPrivate("Hi"); // Error, `reallyPrivate` is undefined outside of the scoping function
In your code, you're assigning things to the prototype property of the function. That only comes into play when the function is called as a constructor function (e.g., via new). When you do that, the object created by new receives the function's prototype property as its underlying prototype. But that's a completely different thing, unrelated to what jQuery does where it's both a function and a pseudo-namespace.
You do not need any of that weirdness, to use stuff like $.each
you just attach functions to the function object instead
of the prototype object:
function Constructor() {
if (!(this instanceof Constructor)) {
return new Constructor();
}
}
Constructor.prototype = {
each: function() {
return "instance method";
}
};
Constructor.each = function() {
return "static method";
};
var a = Constructor();
a.each(); //"instance method"
Constructor.each(); //"static method"
I have a class definition that is part of a class definition.
var someObject = {
someClass: function() {
this.someMethod = function() {
alert('hello');
};
}
}
I have been told that I should use prototype to add methods, as it then only needs to be created once for all instances of the object. The problem is that it seems I need to add the prototyped method after the constructor function is defined, like this...
var someObject = {
someClass: function() {
}
}
someObject.someClass.prototype.someMethod = function() {
alert('hello');
};
Ideally however I would like to define the prototyped methods within the constructor function like this...
var someObject = {
someClass: function() {
this.prototype.someMethod = function() {
alert('hello');
};
}
}
This causes an error however stating that prototype is null or not an object. Is there a way to accomplish what I would like, or is this not possible?
You can make it work by using arguments.callee or - if you don't overwrite the .prototype property of your constructor function - this.constructor instead of plain this, ie
var someObject = {
someClass: function() {
// this.constructor.prototype should work as well
arguments.callee.prototype.someMethod = function() {
alert('hello');
};
}
};
However, putting the function expression back into the constructor defeats the whole purpose of the exercise - it doesn't matter that you store the reference to the function object in the prototype instead of the instance, you're still creating a new one on each constructor invocation!
One possible solution is using an anonymous constructor instead of an object literal, which gets you an additional scope for free:
var someObject = new (function() {
function someClass() {}
someClass.prototype.someMethod = function() {
alert('hello');
};
this.someClass = someClass;
});
See Paul's answer for an equivalent solution using object literals and a wrapper function, which might be more familiar.
Yes, you can't access prototype property inside the constructor of the class, but you can do a little bit different code notation, so probably it helps to you:
var someObject = {
someClass: (function wrapper() {
var ctor = function(){};
ctor.prototype.someMethod = function(){
alert('hello');
};
return ctor;
})()
}