
ライブラリの使用に関する問題はここでは考慮されません。これは既にHabréで既に行われていますが、例としてBackbone自体を含む簡単なjsサマリーチートシートがあります。
叙情的な余談
このようなことをするというアイデアは、基本、パターン、アルゴリズム、優れた実践に関する多くの文献を読んだ後に思いつきます。 多くの知識があるという感覚がありますが、それらを自分の目的に適切に適用することはできません。さらに、異なる本の資料は、時にはほとんど知覚できませんが、互いに矛盾します。 あいまいな疑いが苦しみ始めます。
それから、良いプログラマーは、結局のところ、上記のすべてを完全に使用するのではなく、いくつかのサブセットを使用し、気分が良いことを発見しました(他のすべては、必要に応じてディレクトリから取得または接続できます)。 次に、この「最適なサブセット」を計算する必要がありました。
専門家は、この質問に対する答えを正確に知っています。「最良かつ最も正しいサブセットは、私が使用するものです。」 しかし、メンターのいないファンは洗脳する必要があります。
ただし、時間を無駄にすることはできませんが、アンダースコア+バックボーンバンドルのソースコードで、「javascriptの基本」、「アルゴリズム」、「パターン」、および「ベストプラクティス」の必要最小限に調整された最小値を見つけることができるとすぐに信じてください。
彼らは、グラフ横断のためのダイクストラのアルゴリズムを追加するのを忘れたことに加えて、特にダグラス・クロックフォードにほとんど同意しません。
まだ私を信じている人のために、彼はBackboneからよりユニークなトリックをキャプチャする大要を作成し、それを知っている人とそれを掘る場所を理解している人のためにjavascriptを繰り返しましょう-それを知らない人のために決めました。 わからない人はすぐにバックボーンを開いてください-ソースに下線を引き、次のようなタスクを完了するときに読んでください。投稿の作者が見逃した瞬間に。」
モジュール作成
(function(root, factory) { // if (typeof define === 'function' && define.amd) { // , define amd, define(['underscore', 'jquery', 'exports'], function(_, $, exports) { // // - root.Backbone = factory(root, exports, _, $); }); // Node, jQuery } else if (typeof exports !== 'undefined') { var _ = require('underscore'); factory(root, exports, _); // } else { root.Backbone = factory(root, {}, root._, (root.jQuery || root.Zepto || root.ender || root.$)); } }(this, function(root, Backbone, _, $) { }));
工場構造
function(root, Backbone, _, $) { // , Backbone var previousBackbone = root.Backbone; // var array = []; var push = array.push; var slice = array.slice; var splice = array.splice; // Backbone.VERSION = '1.1.2'; // Backbone.$ = $; // , , Backbone.noConflict = function() { root.Backbone = previousBackbone; return this; }; // Backbone.emulateHTTP = false; Backbone.emulateJSON = false; // , // , , - var Events = Backbone.Events = { // , , obj.trigger(' '); on: function(name/* */, callback/**/, context/* , this */) { }, // , once: function(name, callback, context) { }, // off: function(name, callback, context) { }, // trigger: function(name) { }, // - stopListening: function(obj, name, callback) {}; // , - this var listenMethods = {listenTo: 'on', listenToOnce: 'once'}; // , Events.bind = Events.on; Events.unbind = Events.off; // Backbone _.extend(Backbone, Events); // . // - var Model = Backbone.Model = function(attributes, options) {}; // , _.extend(Model.prototype, Events/* */, {/* */}); // // , var Collection = Backbone.Collection = function(models, options) { }; // , , _.extend(Collection.prototype, Events, {}); // // - , jQuery var View = Backbone.View = function(options) {}; // _.extend(View.prototype, Events, {}); // / // - jQuery.ajax Backbone.sync = function(method, model, options) {}; // // , // history var Router = Backbone.Router = function(options) {}; _.extend(Router.prototype, Events, {}); // , // var History = Backbone.History = function() {}; _.extend(History.prototype, Events, {}); // C Backbone.history = new History; // return Backbone; }
プロトタイプチェーン
var extend = function(protoProps/* */, staticProps/* */) { var parent = this; // , var child; // , if (protoProps && _.has(protoProps, 'constructor')) { // child = protoProps.constructor; // } else { // child = function(){ return parent.apply(this, arguments); }; // this child } // child , _.extend(child, parent, staticProps); // , // : child.staticProp(); // child , var Surrogate = function(){ this.constructor = child; }; // Surrogate.prototype = parent.prototype; // parent child.prototype = new Surrogate; // parent // , : var x = new child; x.protoProp(); if (protoProps) _.extend(child.prototype, protoProps); // , , , child.__super__ = parent.prototype; return child; }; // Model.extend = Collection.extend = Router.extend = View.extend = History.extend = extend;
これ、引数、プロトタイプ、コンストラクターで作業する
// var events = this._events[name] || (this._events[name] = []); // this - Events // , _.extend(Model.prototype, Events); // , (function(root, factory) {}(this, function(root, Backbone, _, $) {})) // // , once: function(name, callback, context) { var self = this; // , var once = _.once(function() { self.off(name, once); // , callback.apply(this, arguments); }); once._callback = callback; // , return this.on(name, once, context); // - }, trigger: function(name) { var args = slice.call(arguments, 1); // if (events) triggerEvents(events, args); // , , if (allEvents) triggerEvents(allEvents, arguments); // , , return this; } // var triggerEvents = function(events, args) { var ev, i = -1, l = events.length, a1 = args[0], a2 = args[1], a3 = args[2]; switch (args.length) { case 0: while (++i < l) (ev = events[i]).callback.call(ev.ctx); return; // this ev.ctx case 1: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1); return; case 2: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2); return; case 3: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2, a3); return; // , : ev.ctx.callback(args[0], args[1], args[2] ..) default: while (++i < l) (ev = events[i]).callback.apply(ev.ctx, args); return; } }; // , , , this.initialize.apply(this, arguments); has: function(attr) { // return this.get(attr) != null; // } // , , if (!diff) return this.hasChanged() ? _.clone(this.changed) : false; fetch: function(options) { options = options ? _.clone(options) : {}; // if (options.parse === void 0) options.parse = true; // - var model = this; // , var success = options.success; // options.success = function(resp) { // , , , if (!model.set(model.parse(resp, options), options)) return false; if (success) success(model, resp, options); // , model.trigger('sync', model, resp, options); }; wrapError(this, options); // error , return this.sync('read', this, options); // }, // return new this.constructor(this.attributes); // var modelMethods = ['keys', 'values', 'pairs', 'invert', 'pick', 'omit']; _.each(modelMethods, function(method) { Model.prototype[method] = function() { // var args = slice.call(arguments); // args.unshift(this.attributes); // c return _[method].apply(_, args); // Underscore }; }); attrs instanceof Model // // this[first ? 'find' : 'filter'](function(model) { for (var key in attrs) { if (attrs[key] !== model.get(key)) return false; } return true; }); this.models.sort(_.bind(this.comparator, this)/* , comparator.call(this)*/);
正規表現の例
// () var eventSplitter = /\s+/; // // var names = name.split(eventSplitter) // , // , id c base.replace(/([^\/])$/, '$1/') + encodeURIComponent(this.id) // _routeToRegExp: function(route) { route = route.replace(escapeRegExp, '\\$&') .replace(optionalParam, '(?:$1)?') .replace(namedParam, function(match, optional) { return optional ? match : '([^/?]+)'; }) .replace(splatParam, '([^?]*?)'); return new RegExp('^' + route + '(?:\\?([\\s\\S]*))?$'); } // var params = route.exec(fragment).slice(1); // var match = (window || this).location.href.match(/#(.*)$/);
$を使用する
// $: function(selector) { return this.$el.find(selector); }, this.el = this.$el[0]; // method = _.bind(method, this); // , , eventName += '.delegateEvents' + this.cid; // if (selector === '') { this.$el.on(eventName, method); // } else { // .. jQuery , , - this.$el.on(eventName, selector, method); } this.$el.off('.delegateEvents' + this.cid); // - // var $el = Backbone.$('<' + _.result(this, 'tagName') + '>').attr(attrs); // var xhr = options.xhr = Backbone.ajax(_.extend(params, options)); Backbone.ajax = function() { return Backbone.$.ajax.apply(Backbone.$, arguments); };
ブラウザナビゲーション処理
// if (typeof window !== 'undefined') { this.location = window.location; this.history = window.history; } // fragment = decodeURI(this.location.pathname + this.location.search) Backbone.$(window).on('popstate', this.checkUrl); // Backbone.$(window).on('hashchange', this.checkUrl); this.location.replace(this.root + '#' + this.fragment); // // Backbone.$(window).off('popstate', this.checkUrl).off('hashchange', this.checkUrl);
さまざまなささいなこと
// Backbone root.Backbone = factory(root, {}/* */, root._, (root.jQuery || root.Zepto || root.ender || root.$)) // , , on: function(name, callback, context) { // if (!eventsApi(this, 'on', name, [callback, context]) || !callback) return this; // } off: function(name, callback, context) { var retain, ev, events, names, i, l, j, k; // , - return this; }, // if (obj) (listeningTo = {})[obj._listenId] = obj; // delete this._listeningTo[id] // var remove = !name && !callback; // // eventsApi, , x.on({ }) for (var key in name) { obj[action].apply(obj, [key, name[key]].concat(rest)); // x.on(''); } // ( ) var listenMethods = {listenTo: 'on', listenToOnce: 'once'}; _.each(listenMethods, function(implementation, method) { Events[method] = function(obj, name, callback) { // obj[implementation](name, callback, this); return this; }; }); // , var attrs = attributes || {}; options || (options = {}); unset: function(attr, options) { return this.set(attr, void 0/* undefined*/, _.extend({}, options, {unset: true})/* */); } // if (key == null || typeof key === 'object') { attrs = key; options = val; } else { (attrs = {})[key] = val; } // . method = this.isNew() ? 'create' : (options.patch ? 'patch' : 'update'); // , , , , var iterator = _.isFunction(value) ? value : function(model) { return model.get(value); }; // var methodMap = { 'create': 'POST', 'update': 'PUT', 'patch': 'PATCH', 'delete': 'DELETE', 'read': 'GET' }; // throw new Error('A "url" property or function must be specified'); // , var wrapError = function(model, options) { var error = options.error; options.error = function(resp) { if (error) error(model, resp, options); model.trigger('error', model, resp, options); }; };