まえがき
ついに、投稿者の「アンダースコアソースコード解釈シリーズ」のアンダースコア解析も終わりに近づいてきました。タイムラインに注目してみると、投稿者が最近解釈のスピードを上げていることがわかります。 11月は色々な事があり、心身ともに疲れきっていますが、この連載を早く終わらせたいと思っているのは一つだけです。
この記事は解釈シリーズの最後から 2 番目の記事となる予定であり、最後の記事は明らかに要約です。投稿者のUnderscoreシリーズ解釈の完全版はhttps://github.com/hanzichi/u...
定期電話
以前に書いた記事は主に特定の方法、特定の知識の詳細に焦点を当てており、読者もコメントを残しました投稿者はアーキテクチャ全体について語らなければなりませんが、具体的な内容を理解するのに大きな問題はないと考えたため、投稿者は最後に、つまりこの記事でそれを整理しました。全体的なアーキテクチャを習得せずにメソッドを使用することになります。
アンダースコアは、ほとんどの場合、_.funcName(xx, xx) の形式で呼び出されます。これは、ドキュメント内の呼び出しメソッドでもあります。
_.each([1, 2, 3], alert);
それを実装する最も簡単な方法は、_ を単純なオブジェクトとみなすことができます:
var _ = {}; _.each = function() { // ... };
JavaScript では、すべてがオブジェクトです。実際、ソース コード内の _ 変数はメソッドです:
var _ = function(obj) { if (obj instanceof _) return obj; if (!(this instanceof _)) return new _(obj); this._wrapped = obj; };
それはなぜですか。方法は? 次に見てみましょう。
OOP
アンダースコアはOOP形式の呼び出しをサポートしています:
_([1, 2, 3]).each(alert);
これは実際には非常に古典的な「新しい構築はありません」、_は実際にはコンストラクター、_([1, 2, 3])の結果はオブジェクトですインスタンス 、このインスタンスには _wrapped 属性があり、属性値は [1, 2, 3] です。インスタンスは各メソッドを呼び出す必要があります。このメソッドがない場合は、プロトタイプ チェーンから取得する必要があります。つまり、このメソッドは _.prototype にあるはずです。では、メソッドはどのようにマウントされるのでしょうか?
。メソッドのマウント
これで、次の 2 つの点が明確になりました:
_ 是一个函数(支持无 new 调用的构造函数) _ 的属性有很多方法,比如 _.each,_.template 等等
私たちの目標は、_ の構築されたインスタンスでもこれらのメソッドを呼び出せるようにすることです。よく考えてみると、_ の属性をトラバースすることは、_ のプロトタイプ チェーンに関数をハングすることができます。
これを実現するために、ソース コードの _.mixin メソッドが使用されます:
// Add your own custom functions to the Underscore object. // 可向 underscore 函数库扩展自己的方法 // obj 参数必须是一个对象(JavaScript 中一切皆对象) // 且自己的方法定义在 obj 的属性上 // 如 obj.myFunc = function() {...} // 形如 {myFunc: function(){}} // 之后便可使用如下: _.myFunc(..) 或者 OOP _(..).myFunc(..) _.mixin = function(obj) { // 遍历 obj 的 key,将方法挂载到 Underscore 上 // 其实是将方法浅拷贝到 _.prototype 上 _.each(_.functions(obj), function(name) { // 直接把方法挂载到 _[name] 上 // 调用类似 _.myFunc([1, 2, 3], ..) var func = _[name] = obj[name]; // 浅拷贝 // 将 name 方法挂载到 _ 对象的原型链上,使之能 OOP 调用 _.prototype[name] = function() { // 第一个参数 var args = [this._wrapped]; // arguments 为 name 方法需要的其他参数 push.apply(args, arguments); // 执行 func 方法 // 支持链式操作 return result(this, func.apply(_, args)); }; }); }; // Add all of the Underscore functions to the wrapper object. // 将前面定义的 underscore 方法添加给包装过的对象 // 即添加到 _.prototype 中 // 使 underscore 支持面向对象形式的调用 _.mixin(_);
_.mixin メソッドは、独自に定義したメソッドを Underscore ライブラリに追加できます:
_.mixin({ capitalize: function(string) { return string.charAt(0).toUpperCase() + string.substring(1).toLowerCase(); } }); _("fabio").capitalize(); => "Fabio"
同時に、Underscore はいくつかの Array ネイティブも追加しますメソッド:
// Add all mutator Array functions to the wrapper. // 将 Array 原型链上有的方法都添加到 underscore 中 _.each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) { var method = ArrayProto[name]; _.prototype[name] = function() { var obj = this._wrapped; method.apply(obj, arguments); if ((name === 'shift' || name === 'splice') && obj.length === 0) delete obj[0]; // 支持链式操作 return result(this, obj); }; }); // Add all accessor Array functions to the wrapper. // 添加 concat、join、slice 等数组原生方法给 Underscore _.each(['concat', 'join', 'slice'], function(name) { var method = ArrayProto[name]; _.prototype[name] = function() { return result(this, method.apply(this._wrapped, arguments)); }; });
連鎖呼び出し
アンダースコアは連鎖呼び出しもサポートしています:
// 非 OOP 链式调用 _.chain([1, 2, 3]) .map(function(a) {return a * 2;}) .reverse() .value(); // [6, 4, 2] // OOP 链式调用 _([1, 2, 3]) .chain() .map(function(a){return a * 2;}) .first() .value(); // 2
一見すると、連鎖呼び出しには OOP と非 OOP の 2 つの形式があるように見えますが、実際には _.chain の 1 つだけです。 ([1, 2, 3]) _([1, 2, 3]).chain() の結果は同じです。実装方法を詳しく見てみましょう。
_.chain = function(obj) {
_.chain = function(obj) { // 无论是否 OOP 调用,都会转为 OOP 形式 // 并且给新的构造对象添加了一个 _chain 属性 var instance = _(obj); // 标记是否使用链式操作 instance._chain = true; // 返回 OOP 对象 // 可以看到该 instance 对象除了多了个 _chain 属性 // 其他的和直接 _(obj) 的结果一样 return instance; };
_.chain([1, 2, 3]) の結果を見て、関数にパラメータを代入してみましょう。実際には、new なしでパラメータを構築することを意味します。次に、インスタンスを返します。ただし、インスタンスには追加の _chain 属性があり、その他は直接 _([1, 2, 3]) とまったく同じです。 _([1, 2, 3]).chain() を見てみましょう。_([1, 2, 3]) は構築されたインスタンスを返します。このインスタンスにはメソッドを呼び出し、_chain 属性を追加します。インスタンスを返し、インスタンスを返します。したがって、2 つの効果は一貫しており、結果は OOP に変換されます。
ここまで言っても、まだ本題に到達していないようですが、どのように「連鎖」しているのでしょうか?次のコードを例として見てみましょう:
_([1, 2, 3]) .chain() .map(function(a){return a * 2;}) .first() .value(); // 2
map メソッドが呼び出されるとき、実際には戻り値。 _.mixin ソース コードを見てみましょう:
// 执行 func 方法 // 支持链式操作 return result(this, func.apply(_, args));
result は重要な内部ヘルパー関数 (ヘルパー関数) です:
// Helper function to continue chaining intermediate results. // 一个帮助方法(Helper function) var result = function(instance, obj) { // 如果需要链式操作,则对 obj 运行 chain 方法,使得可以继续后续的链式操作 // 如果不需要,直接返回 obj return instance._chain ? _(obj).chain() : obj; };
チェーン操作が必要な場合 (インスタンスには _chain 属性があります)、chain 関数を呼び出します操作結果に基づいて、チェーンコールを続行できるようにします。