I have a Curl.js module defined as:
define("Portfolio", ["jquery"], function(jquery) {
var $ = jquery;
var Portfolio = (function() {
$("#output").text("Getting Stuff");
var name = "";
var holdings = [];
return {
"stupid": function() {
$.getScript("app/myScript.js", function(dog) {
//var dog = new Dog();
dog.speak();
}).fail(function(jqxhr, settings, exception) {
console.log(exception);
});
}
}
})();
return Portfolio;
});
/app/myScript.js looks like this:
(function() {
var breed = "Poodle";
return {
speak: function() {
alert("Woof");
}
};
})();
I am calling the "stupid" method from the console in Chrome as follows:
curl(['Portfolio'], function(Portfolio){
Portfolio.stupid();
});
And Chrome says:
Uncaught TypeError: Object (function() {
var breed = "Poodle";
return {
speak: function() {
alert("Woof");
}
};
})(); has no method 'speak'
In myScript.js i could use var Dog = (function(){....})(); which would work but I don't want to pollute the global namespace that way.
I could also use function Dog(){....} and in my getScript callback use new Dog().speak() but then Dog wont be a module in that I would have private properties, and it would also be bound to the global scope.
It seems that $.getScript() does not bind the returned value of the script called to any variable that is accessible form the callback function of getScript(); at least none that I could find. It does return data but that is only the text of the script called. I did find a cheat that I can take the returned script and run it through eval(). Oddly this is pretty much exactly what $.getScript() method itself does. Reworking the Stupid method to the following code:
"stupid": function() {
$.get("/app/myScript.js", function(Dog) {
var dog = new (eval(Dog));
console.log("Breed:" + dog.getBreed());
d.setBreed("Lab");
console.log("Breed: " + dog.getBreed());
});
}
This does work but it feels a bit hacky and wrong.
How can I use getScript() to call /app/myScript.js that is written as a module or anonymous self-executing function and then call one of the returned functions? getScript is supposed to execute the script which I thought, as I have it written, return an object bount to the getScript return object that I could then call? Obviously I am missing something here.
Why you no work Javascript?
if once you use curl.js maybe it is better to forget about JQuery.getScript()
read curl's wiki!
Related
I'm a javascript newbie and trying to understand how functions work. I found a similar question here but it doesn't really answer my question.
Taking the following piece of javascript as an example
var test = function(){
console.log("kick off");
var insideTest = "variable inside test";
var init = function(){
var insideInit ="variable inside init";
console.log("inside init");
}
return{
init:init
}
}
test().init();
The above code prints the following:
kick off
inside init
But if I remove
return{
init:init
}
it gives me an error saying
Uncaught TypeError: Cannot read property 'init' of undefined
Also, even though I'm calling init method using test().init() it doesn't print inside Init if the return statement is removed.
My question is why is it necessary to return init:init to execute init method.
EDIT:
To answer why my init function is inside the test() function here is the larger picture of what i'm trying to do.
var test = function() {
var init = function() {
var a = 0;
function1();
function2();
}
var function1() = function() {
//some code
}
var function1() = function() {
//some code
}
return {
init: init
}
}
Have added inline comments. Also the init inside test will have access to variable defined outside it(init) scope which is closure. test is returning an object to access it's inner function.This specific pattern is revealing module pattern
var test = function(){
console.log("kick off");
var insideTest = "variable inside test";
// Here init is a function which is private to test function
// Any function calling test will not have access to init unless it is 'exposed'
var init = function(){
var insideInit ="variable inside init";
console.log("inside init");
}
return{
init:init // exposing the private function
}
}
When you return, you're returning an Object with a single key of init, which you've assigned the "init" function that you defined within the test function. This allows you to return multiple functions if you'd like, so you can chain calls.
If you'd prefer a different way, you could just return the function without the curly braces, ie. return init;, and then assign the return of test() to a variable. var externalInitFnc = test();
Edit: Looking back it seems that you are fuzzy on the idea of scope in Javascript. When you defined init within the test function, it is only accessible within that function. Similar to how a private variable in a Java class is only available within that same class.
I am kind of a OO js noobie and am trying to create a new Javascript library the correct way but am struggling to understand how best to do this and make it work properly. I would like my library to self invoked so that an object ABC is instantiated as soon as the js library file is included in the html. However, then users should be able to call methods on that object in the html page.
I've tried a few different versions of the below code, but can't seem to get the prototype method for declaring methods to work properly. On my HTML page, I can call ABC.alert(); and it properly executes the alert message. However, when I try to call ABC.alert2(); which is using prototype inheritance which I understand is more memory efficient than just declaring methods right inside the object constructor, I can't seem to get that to work. I keep getting ABC.alert2 is not a function. I do see that my ABC object does have the proto property, but I can't seem to figure out how to define methods properly using prototype. Is it because I'm using a self invoked function? Should I not use a self invoked function and instead at the bottom of my library just create a new instance of my object manually with var ABC = new defineABC(); line?
jsbin link
Also copied below:
(function (window) {
'use strict';
function defineABC() {
var ABC = {};
ABC.alert = function() {
alert("hello this is working");
};
ABC.prototype.alert2 = function() {
alert("hello prototype is working inside constructor");
};
return ABC;
}
defineABC.prototype.alert3 = function() {
alert("hello prototype is working outside constructor");
};
if (typeof(ABC) === 'undefined') {
window.ABC = defineABC();
}
})(window);
ABC.alert(); //successful
ABC.alert2(); //fails
ABC.alert3(); //fails
You need to create an instance of your object to use the prototype methods.
var abc = new defineABC()
abc.alert2();
abc.alert3();
so u can define it in your code like this
if (typeof(ABC) === 'undefined') {
window.ABC = new defineABC();
}
You final code could look like this:
(function (window) {
'use strict';
function defineABC() {
}
defineABC.prototype.alert3 = function() {
alert("hello prototype is working outside constructor");
};
defineABC.prototype.alert = function() {
alert("hello this is working");
};
defineABC.prototype.alert2 = function() {
alert("hello prototype is working inside constructor");
};
if (typeof(ABC) === 'undefined') {
window.ABC = new defineABC();
}
})(window);
Since ABC is only created once per page, it's okay to declare methods right on the object.
It is also no need in factory function or IIFE. You colud instantiate an object as simple as that.
window.ABC = window.ABC || {
alert: function () {
alert("hello this is working");
}
};
I was wondering how does JQuery use "$" both as a function to return a new instance and an instance itself.
I guess that it's not exactly the case but I mean, we can use $(element).method and for exemple $.ajax without brackets (.ajax will still be a method).
EDIT :
I think that I misspoke. I know how objects work in JavaScript and my question was not about that.
JQuery allows us to use $ without the key word new. It has a function that returns a new instance automatically. So my question was about how it can use $ both as a function to instanciate a new object and an object itself.
Let's say we have
(function() {
var jQ = function (arg){
this.own = arg;
};
jQ.prototype = {
foo : function (){
alert("Foo");
},
bar : function (){
alert("Bar");
}
};
window.jQ = window.$ = jQ;
return jQ;
}());
In this exemple, i have to go througth the key word new if I want to use my object.
So how does JQuery do to avoid us this step ?
Function is an object in javascript: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function You can check this code:
var f = function () { alert(1); };
f.func1 = function () { alert(2); };
f.func2 = function () { alert(3); };
and you can call f(), f.func1() and so on...
It's not jQuery. In JavaScript functions are objects.
In the case of $(element).method you are passing a parameter element into the jQuery function, where with the $.ajaxcall you are calling the ajax function inside of the $ jQuery object. In both cases we are talking about the same piece of code, but we are using it two different ways.
Have a look at the raw source code for jQuery and that should help to visualize this a little: https://code.jquery.com/jquery-2.1.1.js
Note: the jQuery function that is used repeatedly is aliased at the bottom of the page.
Remember that in JavaScript, functions are objects. So, using the specific functions you called out in your question, you could create them like this:
var $ = function(selector) {
...
};
$.ajax = function(url) {
...
};
EDIT: To respond to your edited/clarified question, you don't have to use prototyping to make constructor functions in javascript. Remember, all a constructor is doing is returning an object - here's the equivalent of your prototyping code, but without having to use the new operator to instantiate the object:
(function() {
var jQ = function (arg){
return {
own: arg,
foo: function (){
alert("Foo");
},
bar: function (){
alert("Bar");
}
}
};
window.jQ = window.$ = jQ;
return jQ;
}());
I believe this style is actually preferred by Douglas Crockford because forgetting to use the new keyword won't throw an error but you'll get some very unexpected behavior.
JQuery allows us to use $ without the key word new. It has a function that returns a new instance automatically.
Nothing magical here. The jQuery function simply returns an instance of another constructor (source):
// Define a local copy of jQuery
jQuery = function( selector, context ) {
// The jQuery object is actually just the init constructor 'enhanced'
// Need init if jQuery is called (just allow error to be thrown if not included)
return new jQuery.fn.init( selector, context );
},
The only magic going on in the code (not shown in the example) is that jQuery.fn.init.prototype = jQuery.prototype. But jQuery.fn.init is a different function than jQuery.
Applied to your example:
var jQ = function (arg){
return new jQ.prototype.init(arg);
};
jQ.prototype = {
init: function(arg) {
this.own = arg;
},
// ...
};
jQ.prototype.init.prototype = jQ.prototype;
So instead of doing the standard copy & paste from the examples in Google's docs of Javascript with global variables, I want to encapsulate everything using prototypes.
My function starts with this:
define(function (require){
'use strict';
...
but the interesting point at which I am blocked now is:
GoogleDrive.prototype.loadAPI = function() {
window.gapi.load('picker', {'callback': this.onPickerApiLoad});
};
GoogleDrive.prototype.onPickerApiLoad = function() {
this.pickerApiLoaded = true;
this.createPicker();
};
That doesn't work because this inside the callback is not my GoogleDrive function any more, so I get:
Uncaught TypeError: Cannot set property 'pickerApiLoaded' of undefined
How would I connect that .onPickerApiLoad to the right scope?
UPDATE:
Prompted by #Jon's answer, I looked into Underscore's bind method source code and it uses call, so I tried to do the same, with a function that returns a version of my prototype function bound to the context I want:
GoogleDrive.prototype.loadAPI = function() {
var that = this;
googleAPI.load('picker', {'callback': function() { return that.onPickerApiLoad.call(that) }});
};
This seems to work (have to confirm), but not that elegant.
I bet you could get this to work using the bind function of Underscore or Lo-Dash (a drop-in Underscore replacement):
GoogleDrive.prototype.loadAPI = function() {
window.gapi.load('picker', {'callback': _.bind(this.onPickerApiLoad, this)});
};
I finally resorted to call and an anonymous function, using which I can pass my own context:
var that = this;
googleAPI.load('picker', { 'callback': function() {
return that.onPickerApiLoad.call(that)
} } );
So I came up with something sort of hackish to check and see if a function is called from within the object itself. Can someone give me some good reasons not to do something like this?
function Poo(){
this.myFunc=function(){
for(x in this)
{
if (this.myFunc.caller==this[x]) {
alert(this.myFunc.caller==this[x]);
return;}
}
alert(false);
}
this.myFunc2=function(){this.myFunc();}
}
var mine=new Poo();
mine.myFunc(); //calling directly not allowed prints false
mine.myFunc2(); //called from a member function allowed prints true
You can do whatever you want, however, I can show you a case where you method doesn't work:
function Poo(){
this.myFunc = function () {
for(x in this) {
if (this.myFunc.caller == this[x]) {
console.info('internal call, accepted');
return;
}
}
console.error('no external calls allowed');
};
this.myFunc3 = function () {
var self = this;
// this is a standard way of
// passing functions to callbacks
// (eg, ajax callbacks)
this.myFunc4(function() {
self.myFunc();
});
}
this.myFunc4 = function (callback) {
// do stuff...
callback();
};
}
var mine = new Poo();
mine.myFunc3();
myFunc3 is within the object, so I assume you would expect the call to myFunc in the callback it gives to myFunc4 (also in the object) to work. However, caller doesn't do well with anonymous functions.
Also, iterating through the entire instance methods and attributes while comparing functions is definitely not the "Object Oriented" way of doing it. Since you're trying to emulate private methods, I'm assuming that OO is what you're looking for.
Your method is not taking any advantage of the features JS offers, you're just (re)building existing functionality in an inelegant way. While it may be interesting for learning purposes, I wouldn't recommend using that mindset for shipping production code.
There's another question on stackover that has an answer that you may be interested in:
Why was the arguments.callee.caller property deprecated in JavaScript?
edit: small change on how I call myFunc from the callback, in the anonymous function this was not the instance.
I cant't give you a good reason not to do this, but a lot easier solution.
function Poo() {
var myFunc = function() {
alert('myfunc');
};
this.myFunc2 = function() {
myFunc();
}
}
var mine = new Poo();
var mine.myFunc(); // Won't work
var mine.myFunc2(); // Will work
Why not use something like the module pattern to hide the implementation of your "private" methods.
var poo = function(){
var my = {},
that = {};
my.poo = function() {
// stuff
};
that.foo = function(){
my.poo(); //works
// more stuff
};
return that;
};
poo.foo(); // works
poo.poo(); // error