IT

기본 습관을 확장하는 것이 왜 나쁜 습관입니까?

lottoking 2020. 7. 19. 09:04
반응형

기본 습관을 확장하는 것이 왜 나쁜 습관입니까?


모든 JS 의견 리더는 기본 표현을 확장하는 것은 나쁜 습관이라고 생각합니다. 그런데 왜? 성능에 영향을 받습니까? 어떤 사람이 "어떤 루프를 가지고"를 두려워하고 가능한 유형을 추가하여 Object모든 것의 모든 루프를 파괴합니까?

가지고 TJ Holowaychukshould.js 를 예를 들어. 그는 간단한 게터추가Object 하고 모든 것이 잘 작동합니다 ( source ).

Object.defineProperty(Object.prototype, 'should', {
  set: function(){},
  get: function(){
    return new Assertion(Object(this).valueOf());
  },
  configurable: true
});

이것은 실제로 의미가 있습니다. 예를 들어 하나를 확장 할 수 있습니다 Array.

Array.defineProperty(Array.prototype, "remove", {
  set: function(){},
  get: function(){
    return removeArrayElement.bind(this);
  }
});
var arr = [0, 1, 2, 3, 4];
arr.remove(3);

보충 형식의 확장에 대한 논쟁이 있습니까?


객체를 확장하면 동작이 변경됩니다.

자신의 코드에서만 사용할 수있는 객체의 동작을 변경하는 것이 좋습니다. 그러나 다른 코드에서 사용되는 동작을 변경하면 다른 코드가 손상 될 위험이 있습니다.

자바 확장에서 배열 및 배열 클래스에 메소드를 추가 할 때 자바 펼쳐 작동 방식으로 인해 작업을 깨뜨릴 위험이 매우 효율적입니다. 오랜 경험을 통해 이런 종류의 것들이 자바에서 모든 종류의 끔찍한 버그를 일으킨다는 것을 알게되었습니다.

사용자 정의 동작이 필요한 경우 기본 클래스를 변경하는 대신 자체 클래스 (아마도 서브 클래스)를 정의하는 것이 좋습니다. 그렇게하면 아무것도 깨지 않을 것입니다.

클래스를 서브 클래하는 싱하지 않고 클래스의 작동 방식을 변경하는 기능은 좋은 프로그래밍 언어의 중요한 기능이지만 거의 사용하지 않습니다.


성능 측면과 같이 측정 가능한 단점은 없습니다. 아무도 언급하지 않았습니다. 이것이 개인적인 취향과 경험의 문제입니다.

주요 프로 견적 : 더보기 보완입니다 : 구문 설탕. 유형 / 인스턴스 별 기능 제공 유형 / 인스턴스에 특별히 바인딩됩니다.

주요 반대 주장 : 코드가 수정할 수 있습니다. lib A가 함수를 추가하면 lib B의 함수를 사용할 수 있습니다. 코드가 매우 쉽게 깨질 수 있습니다.

둘 다 요점이 있습니다. 유형을 직접 변경하는 두 개의 라이브러리에 의존하는 경우 예상되는 기능이 동일하지 않기 때문에 코드가 깨질 가능성이 먹을 수 있습니다. 나는 동의합니다. 매크로 라이브러리는 기본 유형을 조작 할 수 있습니다. 어떤 이름인지 개발자로서 당신은 무대 뒤에서 무슨 일이 일어나고 있는지 알 수 없습니다.

그리고 이것은 jQuery, 밑줄 자신의 라이브러리를 싫어하는 이유입니다. 그들은 절대적으로 잘 프로그램되어 있고 매력처럼 작동하지만 그들은 . 10 % 만 사용하고 약 1 %를 이해하십시오.

그렇기 때문에 본인 이 꼭 필요한 원자 적 접근 방식을 선호 합니다. 이런 식으로, 당신은 항상 무슨 일이 일어나는지 알고 있습니다. 마이크로 라이브러리는 원하는 작업 만 수행 할 수 있습니다. 어떤 기능이 추가되고 최종 사용자에게 알리는 기본 유형 확장은 안전하고 사용할 수 있습니다.

TL; DR 의심스러운 경우 기본 유형을 확장하지 않습니다. 최종 사용자가 해당 동작을 원하는 경우 100 % 확신 할 경우에만 기본 유형을 확장하십시오. 에서 는 기존의 인터페이스를 깰을 구석으로 같은 경우, 기본 유형의 기존 기능을 조작 할 수 있습니다 .

타입을 확장하기로 결정 ; 할 수 보관 유형을 사용하십시오 .Object.defineProperty(obj, prop, desc)prototype


ErrorJSON을 통해 보낼 수 있기를 원하기 때문에 원래 질문을했습니다 . 그래서 나는 묶을 방법이 필요했습니다. error.stringify()보다 나은 느낌 errorlib.stringify(error); 나는 스스로 작동 errorlib하지 않고 작동 하고 error있습니다.


제 생각에는 나쁜 습관입니다. 주된 이유는 통합입니다. 인용 해야하는 should.js 문서 :

OMG IT는 개체를 확장 축소 ???!?! @ 예, 그렇습니다. 단일 getter를 사용하면 코드가 중단되지 않습니다.

글쎄, 저자는 어떻게 알 수 있습니까? 내 조롱 프레임 워크가 같은 경우에 어떻게됩니까? 내 약속 lib가 동일하게 수행하면 어떻게 검증?

당신이 당신의 자신의 프로젝트에서 권한이 있습니다. 그러나 라이브러리의 경우 나쁜 디자인입니다. Underscore.js는 올바른 방법으로 수행 된 작업의 예입니다.

var arr = [];
_(arr).flatten()
// or: _.flatten(arr)
// NOT: arr.flatten()

사례별로 살펴보면 일부 구현이 허용 될 수 있습니다.

String.prototype.slice = function slice( me ){
  return me;
}; // Definite risk.

이미 많은 곳에서 사용되는 많은 문제가 해결하는 것보다 더 많은 문제가 발생하는 방법을 피하기 위해 프로그래밍 언어에서 일반적으로 사용됩니다. 개발자는 기능이 변경 어떻게 알 수 있습니까?

String.prototype.capitalize = function capitalize(){
  return this.charAt(0).toUpperCase() + this.slice(1);
}; // A little less risk.

이 경우에는 사용하지 않는 문자열을 확장합니다. 이 게시물의 한 주장은 새로운 개발자 가이 방법이 핵심 JS의 일부인지 또는 문서를 발견했습니다. 핵심 JS 문자열 객체가 투자 라는 메소드를 얻는다면 어떻게 될까요?

다른 라이브러리와 충돌 할 수있는 이름을 추가하는 대신 모든 개발자가 사용하는 수있는 수정 보강 사용 회사 / 앱?

String.prototype.weCapitalize = function weCapitalize(){
  return this.charAt(0).toUpperCase() + this.slice(1);
}; // marginal risk.

var myString = "hello to you.";
myString.weCapitalize();
// => Hello to you.

다른 개체를 계속 확장하면 모든 개발자가 (이 경우) we 를 사용하여 야생에서 개체를 만나게 되며 회사 / 앱 특정 확장임을 알립니다.

이것은 이름 충돌을 제거하지는 않지만 가능성을 줄입니다. 핵심 JS 객체를 확장하는 것이 귀하 및 / 또는 귀하의 팀을위한 것이라고 판단되면 아마도 이것은 귀하를위한 것입니다.


내장 된 프로토 타입을 확장하는 것은 참으로 나쁜 생각입니다. 그러나 ES2015는 원하는 동작을 얻는 데 사용할 수있는 새로운 기술을 도입했습니다.

WeakMaps를 사용 하여 유형을 내장 프로토 타입과 연결

다음 구현은 NumberArray프로토 타입을 전혀 건드리지 않고 확장합니다 .

// new types

const AddMonoid = {
  empty: () => 0,
  concat: (x, y) => x + y,
};

const ArrayMonoid = {
  empty: () => [],
  concat: (acc, x) => acc.concat(x),
};

const ArrayFold = {
  reduce: xs => xs.reduce(
   type(xs[0]).monoid.concat,
   type(xs[0]).monoid.empty()
)};


// the WeakMap that associates types to prototpyes

types = new WeakMap();

types.set(Number.prototype, {
  monoid: AddMonoid
});

types.set(Array.prototype, {
  monoid: ArrayMonoid,
  fold: ArrayFold
});


// auxiliary helpers to apply functions of the extended prototypes

const genericType = map => o => map.get(o.constructor.prototype);
const type = genericType(types);


// mock data

xs = [1,2,3,4,5];
ys = [[1],[2],[3],[4],[5]];


// and run

console.log("reducing an Array of Numbers:", ArrayFold.reduce(xs) );
console.log("reducing an Array of Arrays:", ArrayFold.reduce(ys) );
console.log("built-ins are unmodified:", Array.prototype.empty);

보시다시피 원시 프로토 타입도이 기술로 확장 할 수 있습니다. 맵 구조 및 ObjectID를 사용하여 유형을 기본 제공 프로토 타입과 연관시킵니다.

내 예제는 빈 누산기를 만드는 방법과 배열 자체의 요소에서이 누산기와 요소를 연결하는 방법에 대한 정보를 추출 할 수 있기 때문에 단일 인수로 reduce만 예상하는 함수를 활성화 Array합니다.

Map약한 참조가 가비지 수집되지 않는 기본 제공 프로토 타입을 나타낼 때 의미가 없기 때문에 일반 유형 을 사용할 수 있습니다 . 그러나 a WeakMap는 반복 할 수 없으며 올바른 키가 없으면 검사 할 수 없습니다. 이것은 어떤 형태의 유형 반사도 피하고 싶기 때문에 원하는 기능입니다.


네이티브 개체를 확장 하지 않아야하는 또 다른 이유 :

우리는 prototype.js 를 사용 하고 네이티브 오브젝트에서 많은 것을 확장하는 Magento를 사용합니다 . 이것은 새로운 기능을 도입하기로 결정할 때까지 잘 작동하며 여기서 큰 문제가 시작됩니다.

페이지 중 하나에 Webcomponents를 도입 했으므로 webcomponents-lite.js는 IE의 전체 (네이티브) 이벤트 객체를 대체하기로 결정합니다 (왜?). 이것은 물론 prototype.js 를 깨뜨리고 차례로 Magento를 깨뜨립니다. (문제를 찾을 때까지 추적하는 데 많은 시간을 투자 할 수 있습니다.)

문제가 마음에 들면 계속하세요!


나는 이것을하지 않는 세 가지 이유를 볼 수 있습니다 ( 적어도 응용 프로그램에서), 여기에 기존 답변에서 두 가지만 다룹니다.

  1. 잘못하면 확장 유형의 모든 개체에 실수로 열거 가능한 속성을 추가하게됩니다. Object.defineProperty기본적으로 열거 할 수없는 속성을 만드는를 사용하여 쉽게 해결할 수 있습니다.
  2. 사용중인 라이브러리와 충돌을 일으킬 수 있습니다. 부지런히 피할 수 있습니다. 프로토 타입에 무언가를 추가하기 전에 사용하는 라이브러리가 정의하는 방법을 확인하고, 업그레이드 할 때 릴리스 정보를 확인하고, 애플리케이션을 테스트하십시오.
  3. 기본 JavaScript 환경의 향후 버전과 충돌을 일으킬 수 있습니다.

요점 3은 틀림없이 가장 중요한 것입니다. 당신은 때문에 프로토 타입 확장, 사용하는 라이브러리와 충돌을 일으키지 않는 것을 테스트를 통해 확인 할 수 있습니다 당신은 당신이 사용하는 도서관 것을 결정한다. 코드가 브라우저에서 실행된다고 가정하면 네이티브 개체의 경우도 마찬가지입니다. Array.prototype.swizzle(foo, bar)오늘 을 정의 하고 내일 Google Array.prototype.swizzle(bar, foo)이 Chrome에 추가 하면 .swizzle'의 동작이 MDN에 기록 된 내용과 일치하지 않는 것 같은 이유를 궁금해하는 혼란스러운 동료 가 생길 수 있습니다.

(또한 mootools가 소유하지 않은 프로토 타입을 만지작 거리면서 웹이 깨지는 것을 피하기 위해 ES6 메소드의 이름을 변경사례 도 참조하세요 .)

이것은 네이티브 객체에 추가 된 메서드에 대해 애플리케이션 특정 접두사를 사용하여 피할 수 있습니다 (예 : Array.prototype.myappSwizzle대신 정의 Array.prototype.swizzle). 프로토 타입을 보강하는 대신 독립형 유틸리티 함수를 사용하여 해결할 수 있습니다.

참고 URL : https://stackoverflow.com/questions/14034180/why-is-extending-native-objects-a-bad-practice

반응형