서문
드디어 포스터의 '언더스코어 소스코드 해석 시리즈' 밑줄 분석이 종료됩니다. 타임라인을 주목해 보면 최근 포스터의 해석 속도가 빨라진 것을 알 수 있습니다. . 11월은 다사다난한 달이다. 작가도 최근 여러 가지 일로 정신적으로나 육체적으로 지쳐 있지만, 이 시리즈를 빨리 끝내고 싶은 마음은 한 가지뿐이다.
이 글은 해석 시리즈의 끝에서 두 번째 글이 될 것으로 예상되며, 마지막 글은 당연히 요약입니다. 포스터의 Underscore 시리즈 해석 풀버전은 https://github.com/hanzichi/u...
정기통화
전에 쓴 글들에서 대부분의 초점이 구체적인 방법에 대해, 구체적으로, 포스터가 전체적인 아키텍처를 이야기한다는 메시지를 남기기도 했는데, 이는 꼭 가르쳐야 할 부분인데, 포스터는 그것을 마지막, 즉 이 글에서 정리했다. 그는 전반적인 아키텍처를 마스터하지 못하고 문제가 있는 특정 방법을 잘 이해하지 못할 것입니다.
Underscore는 _.funcName(xx, xx) 형식으로 가장 많이 호출되는데, 이는 문서에서도 호출 방법으로 사용됩니다.
_.each([1, 2, 3], alert);
이를 구현하는 가장 간단한 방법은 _를 간단한 객체로 간주하는 것입니다.
var _ = {}; _.each = function() { // ... };
JavaScript에서는 실제로 소스의 모든 것이 객체입니다. code 변수는 메소드입니다:
var _ = function(obj) { if (obj instanceof _) return obj; if (!(this instanceof _)) return new _(obj); this._wrapped = obj; };
왜 메소드인가요?
OOP
Underscore는 OOP 형식의 호출을 지원합니다.
_([1, 2, 3]).each(alert);
이것은 실제로 매우 고전적인 "새 구성 없음"입니다. _는 실제로 생성자입니다. _( [ 1, 2, 3]) 결과는 _wrapped 속성을 갖고 속성 값이 [1, 2, 3]인 객체 인스턴스입니다. 인스턴스는 각각의 메소드를 호출해야 하는데, 이 메소드가 없으면 프로토타입 체인에서 가져와야 합니다. 즉, 이 메소드는 _.prototype에 있어야 합니다.
메소드 마운팅
이제 다음 두 가지 사항을 명확히 했습니다.
_ 是一个函数(支持无 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는 몇 가지 배열 기본 메서드도 추가합니다.
// 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)); }; });
체인 호출
Underscore는 체인 호출도 지원합니다.
// 非 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
Zha 언뜻 보면 체인 호출에는 OOP와 비OOP의 두 가지 형태가 있는 것처럼 보입니다. 실제로는 _.chain([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])의 결과를 보고 매개변수를 함수에 대입해 보겠습니다. 실제로 새 항목 없이 매개변수를 구성한 다음 인스턴스에 추가 _chain 속성이 있다는 점을 제외하면 인스턴스를 반환하고 나머지는 direct _([1, 2, 3])와 정확히 동일합니다. _([1, 2, 3]).chain(), _([1, 2, 3])이 구성된 인스턴스를 반환하는 것을 살펴보겠습니다. 이 인스턴스에는 메서드를 호출하고 _chain 속성을 추가합니다. 인스턴스를 반환하고 인스턴스를 반환합니다. 따라서 둘의 효과는 일관되며 결과는 OOP 형식으로 변환됩니다.
너무 많이 말했지만 아직 요점에 도달하지 못한 것 같습니다. 어떻게 "연결"됩니까? 다음 코드를 예로 들어 보겠습니다.
_([1, 2, 3]) .chain() .map(function(a){return a * 2;}) .first() .value(); // 2
언제? map 메소드가 호출되면 실제로 반환 값이 있을 수 있습니다. _.mixin 소스 코드를 살펴보겠습니다.
// 执行 func 方法 // 支持链式操作 return result(this, func.apply(_, args));
결과는 중요한 내부 도우미 함수입니다.
// Helper function to continue chaining intermediate results. // 一个帮助方法(Helper function) var result = function(instance, obj) { // 如果需要链式操作,则对 obj 运行 chain 方法,使得可以继续后续的链式操作 // 如果不需要,直接返回 obj return instance._chain ? _(obj).chain() : obj; };
체인 작업이 필요한 경우(인스턴스는 _chain 속성을 갖습니다. ), 계속해서 연쇄될 수 있도록 연산 결과에 대해 연쇄 함수가 호출됩니다.