I'm trying to use jQuery.fn.extend to override jQuery.fn.init with my own implementation which is going to behave differently from the original implementation but is going to need to call the original implementation at certain places to do the real work.
So, the very first thing would be to actually "proxy" for the original implementation in the new one. That's what I'm attempting to do and I'm observing what seems like awkward behaviour (this seems like one of those JS things).
You can see my latest attempt here. So the problem is. It is only supposed to apply the border to the .parent .child. Not both .parent and .child. That's what it seems to be doing right now. If you remove my jQuery.fn.extend call. You can see the original, correct behavior.
So, the question is what am I doing wrong? Is this the right approach to proxying any JS function and specially the jQuery init function? Is there a better way?
Extra
I saw this old question and the answer there refers to jQuery.sub which has been moved to the jQuery Migrate plugin now. Should I try to use that? Is it any more likely to work than what I'm trying right now? Do I really need to?
Note: you really shouldn't be doing this, as internal jQuery code also calls the constructor, which could bring about very unusual issues if you aren't extremely careful.
Your problem is that you aren't calling oldInit as a constructor - rather as a function, which doesn't really work because anything set inside jQuery.fn.init will go on jQuery.fn, rather than a new jQuery object.
Why doesn't just setting the ThisBinding to {} work then?
Although this can seem somewhat intuitive after learning about the way the "new" operator works, this doesn't actually do the same thing. Why?
function Foo() { this.bar(); }
Foo.prototype.bar = function () { alert(1); };
new Foo; // 1 is alerted
Foo.apply({}); // TypeError: Object #<Object> has no method 'bar'
When new is used, it also gives the instance's __proto__ the constructor's prototype object. When you create an object literal, this is the standard Object.prototype, not the intended prototype object.
What to do then?
If you are attempting to override jQuery.fn.init, you need to use something like this:
var oldInit = jQuery.fn.init;
jQuery.fn.extend(
{ init: function ()
{ return oldInit.apply(new oldInit, Array.prototype.slice.call(arguments));
}
});
How does this work?
By calling new oldInit with no arguments, we'll just get back an empty object with __proto__ set to jQuery.fn, exactly what we want. Then, we'll supply the arguments to oldInit, and any arguments will go straight to this empty new object, just like the original.
This works because when you call it, your actual ThisBinding (the value of this inside the function call) will already be the new jQuery object, because you are meant to refer to this to add new properties to an instance of the jQuery.fn.init constructor.
Your original code was the following:
var oldInit = jQuery.fn.init;
jQuery.fn.extend({
init: function () {
return oldInit.apply(jQuery.fn, Array.prototype.slice.call(arguments));
}
});
That would make your constructor think that the new function is meant to be jQuery.fn, which is probably not what you had intended. If you had a standard function, that would work (as that is what the ThisBinding is meant to be), however since the "new" operator changes the ThisBinding, this no longer means the same thing as before.
Related
I've been using JavaScript for a few years now in web design/development, and everything I know has been self-taught (I was a design major and hobbyist front-ender turned full web developer in pursuit of my career). With that background, I discovered something that I want to learn more about and have no idea what it is called, how it works, or that it may even be something extremely simple.
I've tried to search for more information about this (to prevent myself from needing to ask this), but it's difficult when I'm not sure what it's called that I'm even looking for...
I noticed in a few JavaScript libraries that I use that a variable name can be both a function and an object. For example, the jQuery library uses the name "jQuery". When logged using typeof jQuery it is a function, and typeof jQuery() is an object. What is interesting to me is that initial thought would suggest that jQuery() would be a function but it's actually an object.
//jQuery:
ƒ (a,b){return new r.fn.init(a,b)}
//jQuery():
r.fn.init {} //Expanding this reveals functions inside of __proto__ (which is obviously a clue, but I need more info)
When I try to do something like this I end up overwriting the name (as I would expect):
//Declare an object:
var planetEarth = {
'sky': 'blue',
'grass': 'green'
}
//Now overwrite the object as a function:
function planetEarth(){
console.log('inside of a function now');
}
This is understandable within the realm of JavaScript, so my question is how does a library like jQuery pull off having both at the same time?
Ultimately (using the above example) I would like to be able to do something like this:
planetEarth().sky; //"blue"
So my roundabout question is simply what is this called?
And a follow-up of where can I learn the basics of accomplishing this?
I've found resources on object-oriented JavaScript and classes, as well as prototypes, but I'm not sure if either (or both) of those concepts are what this is. All of the articles I've found aren't starting at the beginning and seem to always jump into unnecessarily complex examples. I'm not looking to make a giant library- I just want to get some more experience at the very basic level. Again, this could be embarrassingly simple and I've just never come across the concept before, so I appreciate being pointed in the right direction, thanks.
Every JavaScript function is also an object, just as every array is also an object. I don't think there is a special name for this, it's just how the language works.
You may be confusing two different things: what a function returns when you call it, vs. the function itself. Take your example:
planetEarth().sky; // "blue"
This code does not rely on the function planetEarth being an object and having any properties. You're calling the function, so to make this code work the function would need to return an object.
Because a function is an object, you can also set properties directly on the function itself. The code below uses both of these features. This version of planetEarth() returns an object with sky and grass properties, so it works as in your example: you call the function to get an object with those properties.
The code also sets an exists property directly on the function, and you can access that property without calling the function:
function planetEarth() {
// Return an object when this function is called
return {
sky: 'blue',
grass: 'green'
}
}
// Set a property directly on the function itself
planetEarth.exists = true;
// Test accessing the function's property
console.log( planetEarth.exists );
// Test accessing a property in the object that the function returns when called
console.log( planetEarth().sky );
jQuery makes use of both of these facilities. jQuery is a function that you can call. It returns a value commonly called a "jQuery object". That object has properties and methods, such as jQuery('#foo').show(). It also has "array-like" properties that you can access as you would any array, e.g. jQuery('#foo').length and jQuery('#foo')[0]. The jQuery function adds those properties to the value it returns.
At the same time, the jQuery library adds other properties and methods to the jQuery function itself. You access without calling the function, e.g. jQuery.ajax({...}).
A couple of other notes to help you understand the jQuery code. First, download the uncompressed version of jQuery. It looks like you are studying the compressed version, which has shortened names that won't make any sense.
Also, if you are wondering what jQuery.fn is, it is simply an alias for jQuery.prototype. Whenever you see jQuery.fn you can mentally substitute jQuery.prototype; learn how JavaScript prototypes work and you will understand jQuery.fn.
And now you may wonder why jQuery.fn exists at all. Why not use jQuery.prototype directly like other JavaScript code that uses prototypical inheritance? In fact, you could, and it would work the same as jQuery.fn. But the very first version of jQuery, back in 2006, didn't work this way. jQuery.fn was its own separate object, and every time you called the jQuery function, it copied all of the methods and properties from this object into the value it returned.
As you can guess, this was rather inefficient, so jQuery was changed to use .prototype so it could return an object that inherits all the methods such as .show() and .hide() instead of copying them all one by one. At the same time, jQuery.fn was kept around as an alias for jQuery.prototype to avoid breaking existing code.
This is a silent answer...
function f() {
return {};
}
console.log(typeof f, typeof f());
This is how jQuery does it. f is a function but when it gets called it returns an object.
Interesting part: function is also an object. f instanceof Function and f instanceof Object are both valid. So, you can call a variable as a function and / or assign some properties because it is also an object.
f.test = 123;
First-Class Objects
In Javascript, functions are first-class objects. This means that functions are just another kind of object. You can put a function in a variable, you can return a function, you can make an array of functions, and all that. Functions are objects.
Consider a slight change in your attempt:
//Declare a function:
function planetEarth(){
console.log('inside of a function now');
}
// Now add fields to it, since it is also an object
planetEarth.sky = 'blue';
planetEarth.grass = 'green';
// Test stuff
planetEarth(); // works
console.log(planetEarth.sky, planetEarth.grass); // works
You mention that you would like to use planetEarth().sky, but observe that while planetEarth is a function (and an object, as I said), planetEarth() is the result of calling planetEarth with no parameters. Therefore, whether you can or can't do planetEarth().sky does not depend on planetEarth as an object having the sky field, but rather depends on whatever you return from planetEarth having that field.
Bonus: did you know that functions can be declared very much like "normal" variables? See below:
// Both lines of code below are identical:
function myFunc() { ... }
var myFunc = function() { ... };
Perhaps looking at the code above will help you clear the confusion. The function is myFunc. myFunc() is simply the act of calling that function. If typeof myFunc() gives function, it is just a coincidence that the object that myFunc returned happened to also be a function.
jQuery is a function. Properties can be assigned to a defined function.
function planetEarth(options){
console.log('inside of a function now', options);
return window["planetEarth"];
}
var planetEarthOptions = {
'sky': 'blue',
'grass': 'green'
}
for (let prop in planetEarthOptions) {
planetEarth[prop] = planetEarthOptions[prop];
}
window["planetEarth"] = planetEarth;
planetEarth("selector");
console.log(planetEarth.sky);
console.log(planetEarth().grass);
So this is a somewhat weird problem that may not be possible but I was wondering if it is possible to get a variable linkage of the function that created an instance from javascript.
for example, i'd like to be able to do something like this:
function MyObject(){
console.log(this) // traces out the instance
console.log(MyObject) // traces out the function
console.log(this.parentClass) //Would like this to trace out the function as well. (or a similar call like it)
}
var test = new MyObject();
Is this at all possible? Thanks!!
It's possible if the prototype objects on the functions haven't been replaced, or if the code replacing them keeps the linkage as it is by default.
The constructor property is (by default and convention) the property you're looking for:
var test = new MyObject();
console.log(test.constructor === MyObject); // "true"
Note all of the caveats above, though. For instance, this is perfectly valid (and not particularly atypical) code:
function MyObject() {
}
MyObject.prototype = {
foo: function() { /* ... */ },
bar: function() { /* ... */ }
};
This is a common pattern, although one I don't recommend. Instead of adding to the object referred to by the MyObject.prototype property (an object that was set up by the JavaScript engine when creating MyObject), that code is creating an entirely new object and assigning it to the prototype property, making it refer to the new object. If that has been done, then the constructor property has been broken, and you can't use it the way you want to use it.
The constructor property on the default prototype object on functions is a funny thing about JavaScript. It's defined quite clearly in the specification that it will be there (it's §13.2 if anyone wants to read up), but as far as I'm aware nothing in the JavaScript engine actually uses it. (It isn't, for instance, used for instanceof.)
I by prelude mean this object: http://gkz.github.com/prelude-ls/
Seems not good idea to bind prelude to window.__proto__, though.
I'm curious and confused about why this happend, and my code in Chrome:
window.__proto__ = prelude;
window.onload = function(){
console.log('loaded');
}
__proto__ is a non-standard reference to the prototype of the window object (https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/proto).
In your first line you are overwriting the entire prototype of window. This means that everything else previously written to the prototype object gets list, including native functions that could be responsible for invoking window.onload.
You should typically never overwrite the prototype object, but instead extend it or simply add new properties directly to objects like window.
I would like to create function objects (yes, all functions are objects) with some control over the prototypal inheritance, that is, I would like one function to inherit from another.
I can make objects that have prototypal inheritance, and know to set the prototype property of a function performing as a constructor to initialize the [[prototype]] property of the object.
However, when creating a function, I must use the function operator or the Function constructor. I could try to swizzle Function.prototype, but (1) don't know if that is writable, and (2) that just seems quite dangerous [still, I should try doing that].
btw: I only care to do this for V8 within node.JS, so if there are means that work only for that environment, that would be acceptable.
For the record, I have seen this:
Is it possible to create a function with another prototype than Function.prototype?
In V8 (and most other browsers/engines except IE) you can change an object's prototype by setting the __prototype__ __proto__ attribute. Setting the prototype attribute will instead change the prototype that is used to create an object if the function is invoked as a constructor function. This should not be what you want.
Afaik there currently is no standard conform way to directly "subclass" a function (or array for that matter). There's only Object.create, but there is no Function.create or Array.create.
EDIT: I just realized that function objects do not have the __prototype__ attribute and changing / setting it will not turn an object into a function.
I believe though that I just recently watched a talk by Brendan Eich (the creator of JavaScript) in which he talked about Function and Array equivalents of Object.create. And infact, googling for "Function.create brendan eich" reveals the following blog post by Eich in which he talks about his wish to implement Function.create and Array.create.
EDIT 2: Ok, I screwed up. Pretty much. The non-standard attribute is of course __proto__ and not __prototype__. Setting __proto__ works fine for functions with some restrictions, which are:
To be able to call aFunction you must initialize it with an actual function, eg:
var aFunction = function () {};
This is important. Calling a function does not access the prototype chain, so if you define aFunction as an object and simply set the __proto__ attribute, you will not able to call aFunction.
You can now assign any other function to aFunction.__proto__ and reading any members (including methods) will correctly delegate to the prototype chain if the porperty is not found on aFunction itself.
Calling aFunction() will always invoke the function that was originally declared when aFunction was defined and will never invoke aFunction's prototype function. So the body of the function is not subject to inheritence.
Sorry for screwing up first with the name of the attribute. Hope this helps you nevertheless.
I came up with a solution that solves my needs. It is not cross-browser, but can be used cross-browser. My most important use case is as a module for node.JS. In that case, the mechanism of setting __proto__ works just fine, in which case I can call methods on the base function object
f.method(args...);
and it executed by code in the "super" function. Because the method is invoked by the method invocation pattern, "this" is set to the base function object, and so the proper properties are accessed even though the method resides in the "super."
Now for the in-Browser case: when I use my library client-side, I provide a proxy mechanism. Alas, code intended for the browser must be written differently. The invocation is:
f.proxy(methodName, args...);
The proxy method in the base function object is:
f.proxy = function (methodName) {
var p = this.constructor.prototype;
return p.proxy(this, methodName, arguments);
};
The proxy in the "super" object's prototype is:
proxy: function (instance, methodName) {
var args = Array.prototype.splice.apply(arguments, [2]),
method = this[methodName];
return (method) ? method.apply(instance, args) : undefined;
}
I probably should have named this "forward" or some such, but "proxy" is good enough.
Perhaps this mechanism might be useful to someone...
I think I understand what you're trying to do. In short, there's no way to really do it natively. You'd have to access the Function constructor, which for function expressions and definitions (i.e. anything using the 'function' keyword), isn't possible as far as I can tell. You could overwrite Function and just always use new Function([args], string) but I doubt you (or any JS programmer) want to do that.
Your best bet would probably be to send your function expressions to another function that returns the function object with your custom methods dynamically added:
wrapFunc = function(f){
f.customFunc = someCustomFunc;
return f;
}
var myNewFunc = wrapFunc(
function(){
//do something
}
);
myNewFunc.customFunc();
If we look at the latest jQuery source at http://code.jquery.com/jquery-latest.js we see the following:
var jQuery = function( selector, context ) {
// The jQuery object is actually just the init constructor 'enhanced'
return new jQuery.fn.init( selector, context );
}
My understanding of the new keyword in Javascript is essentially JavaScript passes the function an empty object {} and the function sets stuff on it via this.blah.
Also from my understanding new differs from .call/.apply etc.. in that the return object also has the prototype set to that of the function. So the return value should have a prototype that the same as jQuery.prototype.init.prototype (or jQuery.fn.init.prototype). However from what I see its prototype is set to jQuery.prototype thus all the commands available to work on the set.
Why is this? What am I missing in my understanding?
If you look deeper into jQuery's code, you'll notice this line:
// Give the init function the jQuery prototype for later instantiation
jQuery.fn.init.prototype = jQuery.fn;
This is for readability/structure purposes so the constructor can have its own method.
There's no real "magic" being done here, just standard JavaScript, albeit in a slightly less commonly used way, perhaps. It's useful in jQuery's case since the library is pretty lengthy and this adds good structure/readability to it.
In that source file, search for the string "Give the init function the jQuery prototype for later instantiation" :-)
The code sets the prototype reference of jQuery.fn.init to jQuery.prototype (which is the same as jQuery.fn I think).