jQuery runs on approximately 50% of all websites and is the de facto standard for DOM manipulation. Most front end developers use it, but how well do they understand what's happening when they use the API? How does a library take on the problems it solves? What are the reasons behind some of the techniques it uses? Observing the code under the hood of a library/framework is a fantastic way to become a better developer and thereby user of that library/framework. Better understanding of design patterns in some of the most popular libraries can open opportunities to contribute and be a better open source citizen.
10. ALL ARRAY BELONG TO US
function Collection(args) {
this.push.apply(this, args);
}
var omit = ['length', 'constructor', 'toString', 'toLocalString'];
Object.getOwnPropertyNames(arrProto).forEach(function (fn) {
if (omit.indexOf(fn) > -1) return;
Collection.prototype[fn] = function () {
var args = __slice.call(arguments);
return arrProto[fn].apply(this, args);
};
});
var c = new Collection([1,2,3]);
log(c.length); // 3
log(c.unshift(0)); // [0, 1, 2, 3]
12. ON
on: function (name, fn, ctx) {
this._events = this._events || {};
var events = this._events[name] || (this._events[name] = []);
events.push({ fn: fn, ctx: ctx || this });
return this;
}
13. TRIGGER
trigger: function (name) {
if (!this._events) return this;
var args = __slice.call(arguments, 1);
var events = this._events[name];
if (events) {
events.forEach(function (ev) {
ev.fn.apply(ev.ctx, args);
});
}
return this;
}
14. LETS SEE IN OUR API
Object.getOwnPropertyNames(arrProto).forEach(function (fn) {
if (omit.indexOf(fn) > -1) return;
Collection.prototype[fn] = function () {
var args = __slice.call(arguments);
var ret = arrProto[fn].apply(this, args);
this.trigger(fn, ret);
return ret;
};
});
15. TOJSON
Collection.prototype.toJSON = function () {
return this.map(function (val) {
return val;
});
};
var c = new Collection(1,2,3);
// Without method
JSON.stringify(c); // '{"0":1,"1":2,"2":3}' - will also contain other props...
// With method
JSON.stringify(c); // '[1,2,3]'
16. UNDERSCORE
Collection.zip = function () {
var args = __slice.call(arguments);
var lengths = args.map(function (c) { return c.length; });
var length = Math.max.apply(Math, lengths);
var results = new Array(length);
var map = function (c) { return c[i]; };
for (var i = 0; i < length; i += 1) {
results[i] = args.map(map);
}
return results;
}
var c = new Collection([1,2]);
var c2 = new Collection([3,4]);
console.log(Collection.zip(c, c2)); // [[1,3],[2,4]];
17. ANGULAR
http://jsperf.com/apply-vs-call-vs-invoke
// performant apply
var pApply = function (args, fn, ctx) {
switch (args.length) {
case 0: return ctx[fn]();
case 1: return ctx[fn](args[0]);
case 2: return ctx[fn](args[0], args[1]);
case 3: return ctx[fn](args[0], args[1], args[2]);
case 4: return ctx[fn](args[0], args[1], args[2], args[3]);
case 5: return ctx[fn](args[0], args[1], args[2], args[3], args[4]);
...
case 10: return ctx[fn](args[0], args[1], args[2], args[3], args[4], args[5
default: return ctx[fn].apply(ctx, args);
}
};
18. LET'S SEE IT IN THE API
function Collection(args) {
// Accept different types
args = this._toArray(args) || [];
pApply(args, 'push', this);
}
19. MOCHA
Collection.prototype.repr = function () {
var self = this;
// override toJSON for repr purposes
this.toJSON = null;
var repr = JSON.stringify(this, function (key, val) {
if (self === val || !isNaN(parseInt(key, 10))) {
return val;
}
return void 0;
});
// reset
delete this.toJSON;
return repr;
}