JavaScript에서 확장 메서드를 작성하려면 어떻게해야합니까?


85

JS로 몇 가지 확장 메서드를 작성해야합니다. C #에서이 작업을 수행하는 방법을 알고 있습니다. 예:

public static string SayHi(this Object name)
{
    return "Hi " + name + "!";
}

다음에 의해 호출됩니다.

string firstName = "Bob";
string hi = firstName.SayHi();

JavaScript에서 이와 같은 작업을 어떻게 수행합니까?

답변:


156

자바 스크립트는 C #의 확장 메서드에 대한 정확한 아날로그가 없습니다. JavaScript와 C #은 완전히 다른 언어입니다.

가장 유사한 방법은 모든 문자열 객체의 프로토 타입 객체를 수정하는 것입니다 : String.prototype. 일반적으로, 가장 좋은 방법은 없습니다 당신이 통제하지 않는 다른 코드와 결합을 의미 라이브러리 코드의 개체 내장의 프로토 타입을 수정할 수 있습니다. (애플리케이션에 포함 된 다른 코드를 제어하는 ​​애플리케이션에서 수행하는 것은 괜찮습니다.)

당신이 경우에 할 수 는 A 내장이있는 만들기 위해 (지금까지) 최선의 프로토 타입 수정 비 열거 사용하여 속성을 Object.defineProperty(이전 그러니까 기본적으로, ES5 +를 현대의 자바 스크립트 환경, 그리고 IE8¹ 이상). 다른 문자열 메서드의 열거 가능성, 쓰기 가능성 및 구성 가능성을 일치 시키려면 다음과 같습니다.

Object.defineProperty(String.prototype, "SayHi", {
    value: function SayHi() {
        return "Hi " + this + "!";
    },
    writable: true,
    configurable: true
});

(기본값은 enumerable입니다 false.)

더 이상 사용되지 않는 환경을 지원해야하는 경우 String.prototype특히에 대해 열거 가능한 속성을 만들 수 있습니다.

// Don't do this if you can use `Object.defineProperty`
String.prototype.SayHi = function SayHi() {
    return "Hi " + this + "!";
};

그것은 좋은 생각이 아니지만 당신은 그것을 피할 수 있습니다. 절대로 으로 그렇게하지 Array.prototypeObject.prototype . 여기에 열거 가능한 속성을 만드는 것은 Bad Thing ™입니다.

세부:

JavaScript는 프로토 타입 언어입니다. 이는 모든 객체가 프로토 타입 객체에 의해 뒷받침된다는 것을 의미 합니다 . JavaScript에서 해당 프로토 타입은 다음 네 가지 방법 중 하나로 지정됩니다.

  • 바이 생성자 함수 오브젝트 (예를 들면, new Foo포함 된 객체를 생성 Foo.prototype의 프로토 타입)
  • 바이 Object.createES5에서 추가 기능 (2009)
  • 에 의해 __proto__접근 자 속성 (ES2015 +, 전용 웹 브라우저에서,이 표준화되기 전에 일부 환경에 존재) 또는 Object.setPrototypeOf(ES2015 +)
  • 메소드를 호출하기 때문에 원시 객체를 생성 할 때 JavaScript 엔진에 의해 ( "프로모션"이라고도 함)

따라서 귀하의 예제에서는 firstName문자열 프리미티브이므로 String메서드를 호출 할 때마다 인스턴스로 승격 되며 해당 String인스턴스의 프로토 타입은 String.prototype. 따라서 함수 String.prototype를 참조 하는 속성을 추가 SayHi하면 해당 함수를 모든 String인스턴스에서 사용할 수 있습니다 (그리고 승격되기 때문에 문자열 프리미티브에서도 효과적으로 사용할 수 있음 ).

예:

이 확장 메서드와 C # 확장 메서드 사이에는 몇 가지 주요 차이점이 있습니다.

  • ( DougR 이 주석에서 지적했듯이) C #의 확장 메서드 nullreference에서 호출 될 수 있습니다 . string확장 메서드 가있는 경우이 코드 :

    string s = null;
    s.YourExtensionMethod();
    

    공장. JavaScript에서는 그렇지 않습니다. null자체 유형이며의 모든 속성 참조 null는 오류를 발생시킵니다. (그렇지 않은 경우에도 Null 유형에 대해 확장 할 프로토 타입이 없습니다.)

  • ( ChrisW 가 주석에서 지적했듯이) C #의 확장 메서드는 전역 적이 지 않습니다. 확장 메서드를 사용하는 코드에서 정의 된 네임 스페이스를 사용하는 경우에만 액세스 할 수 있습니다. (정적 호출에 대한 구문 설탕이므로에서 작동합니다 null.) JavaScript에서는 사실이 아닙니다. 내장 된 프로토 타입을 변경하면 해당 변경 사항이 전체 영역의 모든 코드에 표시됩니다. (영역은 전역 환경 및 관련 내장 객체 등)에서 수행하십시오. 따라서 웹 페이지에서이 작업을 수행하면 해당 페이지에서로드하는 모든 코드에 변경 사항이 적용됩니다. 당신은 Node.js를 모듈에서이 작업을 수행 할 경우, 모든 해당 모듈과 동일한 영역에로드 된 코드는 변경 사항을 볼 수 있습니다. 두 경우 모두 라이브러리 코드에서이 작업을 수행하지 않는 이유입니다. (그들은 메인 스레드가 아닌 다른 지구 환경과 다른 내장 함수를 가지고 있도록 웹 노동자와 Node.js를 작업자 스레드가 자신의 영역에서로드됩니다.하지만 그 영역은 아직 어떤 모듈을 공유 그들은 로드합니다.)


¹ IE8에는가 Object.defineProperty있지만 JavaScript 객체가 아닌 DOM 객체에서만 작동합니다. String.prototypeJavaScript 객체입니다.


@DougR : 죄송합니다, 표준 댓글 정리입니다. 댓글이 쓸모 없게되면 모드 나 고급 사용자가 삭제할 수 있습니다 (모드가 직접 할 수 있고 고급 사용자는 검토 대기열에서 팀을 이루어야합니다). 이 문제를 신고했음을 알리기 위해 답변을 업데이트했습니다. :-)
TJ Crowder 2015 년

1
@Grundy : 감사합니다, 예, String s = null;부분의 요점은 사용하는 것이 었습니다 s.YourExtensionMethod()! 캐치를 감사합니다. :-)
TJ Crowder

확장 메서드가 null 엔터티에 대해 작동한다는 점을 강조해 주셔서 감사합니다.

1
예를 들어 Node.js에서 이것을 수행하면 다른 모듈을 포함하여 프로그램의 모든 문자열에 영향을 미칠 것이라고 생각합니다 (새 속성을 추가)? 두 모듈이 모두 "SayHi"속성을 정의하면 충돌할까요?
ChrisW

1
@ChrisW-꽤. :-) 대신 할 수있는 것은 Array 하위 클래스 ( class MyArray extends Array { singleOrDefault() { ... }})를 만드는 것이지만 지루한 오래된 배열 인 MyArray.from(originalArray)경우 사용하기 전에 해야 함을 의미합니다 originalArray. JavaScript 용 LINQ와 유사한 라이브러리도 있습니다 ( 여기에 요약이 있습니다 ). 유사하게 작업을 수행하기 전에 배열에서 Linq 객체를 생성하는 작업을 포함합니다 (글쎄요, LINQ to JavaScript는 그랬지만 다른 것은 사용하지 않았고 그 라이브러리는 사용하지 않았습니다). 몇년에 걸쳐]). (BTW, 들으 답을 업데이트했습니다.)
TJ 크라우에게

0

전역 증가 사용

declare global {
    interface DOMRect {
        contains(x:number,y:number): boolean;
    }
}
/* Adds contain method to class DOMRect */
DOMRect.prototype.contains = function (x:number, y:number) {                    
    if (x >= this.left && x <= this.right &&
        y >= this.top && y <= this.bottom) {
        return true
    }
    return false
};

이것은 순수 / 바닐라 자바 ​​스크립트에서 작동합니까 아니면 typescript를 사용하는 방법을 알아야 할 것입니까? 당신이 ES6의 문서 또는 MDN typescriptlang.org에 연결하고 말할 때부터 부탁 해요
user1306322을
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.