>  기사  >  웹 프론트엔드  >  Underscore의 전체 아키텍처에 대한 간략한 분석

Underscore의 전체 아키텍처에 대한 간략한 분석

高洛峰
高洛峰원래의
2016-11-05 09:23:511096검색

서문

드디어 포스터의 '언더스코어 소스코드 해석 시리즈' 밑줄 분석이 종료됩니다. 타임라인을 주목해 보면 최근 포스터의 해석 속도가 빨라진 것을 알 수 있습니다. . 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 속성을 갖습니다. ), 계속해서 연쇄될 수 있도록 연산 결과에 대해 연쇄 함수가 호출됩니다.



성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.