js 템플릿. Javascript 템플릿이란 무엇이며 왜 필요한가요? 디자인 패턴은 무엇이고 왜 사용하는가?




안녕, 하브르!
나는 허브에 해당 주제에 대한 자세한 기사가 없다는 사실에 놀랐고, 이로 인해 즉시 이 노골적인 불의를 바로잡게 되었습니다.

웹 애플리케이션의 클라이언트 측이 점점 더 두꺼워지고, 비즈니스 로직이 클라이언트에 가차 없이 침투하고, node.js가 점점 서버 기술의 주권을 침해하는 환경에서 아키텍처를 설계하는 기술에 대해 생각하지 않을 수 없습니다. 자바스크립트. 그리고 이 문제에서 디자인 패턴은 의심할 여지 없이 우리에게 도움이 될 것입니다. 즉, 자주 발생하는 문제를 해결하기 위한 템플릿 방법입니다. 패턴은 변경이 필요할 때 최소한의 노력이 필요한 아키텍처를 구축하는 데 도움이 됩니다. 그러나 이를 만병통치약으로 인식해서는 안 됩니다. 즉, 대략적으로 말하면 코드 품질이 "좋지 않은" 경우 하드 코드와 논리적으로 독립된 모듈 간의 긴밀한 연결로 가득 차 있으므로 어떤 패턴도 이를 저장할 수 없습니다. 그러나 확장 가능한 아키텍처를 설계하는 것이 과제라면 패턴이 좋은 도움이 될 수 있습니다.
그러나 이 기사는 디자인 패턴 자체에 관한 것이 아니라 JavaScript에서의 적용에 관한 것입니다. 이 글의 첫 번째 부분에서는 생성 패턴의 사용에 대해 쓸 것입니다.

싱글톤 작업이 이 패턴을 한 문구로 설명하는 것이라면 다음과 같을 것입니다. 싱글톤은 인스턴스를 하나만 가질 수 있는 클래스입니다.
이 패턴을 구현하기 위한 가장 간단하고 확실한 JavaScript 솔루션은 객체를 사용하는 것입니다.

Var app = ( property1: "값", property2: "값", ... method1: 함수 () ( ... ), ... )

이 방법에는 장점과 단점이 모두 있습니다. 설명하기 쉽고 많은 사람들이 패턴의 존재를 인식하지 못한 채 사용하며 이러한 형태의 작성은 모든 JavaScript 개발자가 이해할 수 있습니다. 그러나 여기에는 중요한 단점도 있습니다. 싱글톤 패턴의 주요 목적은 전역 변수를 사용하지 않고 개체에 대한 액세스를 제공하는 것이며, 이 방법은 현재 범위에서만 앱 변수에 대한 액세스를 제공합니다. 이는 전역인 경우에만 애플리케이션의 어느 곳에서나 앱 개체에 액세스할 수 있음을 의미합니다. 대부분의 경우 이는 매우 허용되지 않습니다. 좋은 JavaScript 개발 스타일은 필요한 모든 것을 캡슐화하는 최대 하나의 전역 변수를 사용하는 것입니다. 이는 위의 접근 방식을 애플리케이션에서 최대 한 번만 사용할 수 있음을 의미합니다.
두 번째 방법은 조금 더 복잡하지만 더 보편적입니다.

함수 SomeFunction () ( if (typeof (SomeFunction.instance) == "object") ( return SomeFunction.instance; ) this.property1 = "value"; this.property2 = "value"; SomeFunction.instance = this; return this ; ) SomeFunction.prototype.method1 = 함수 () ( )

이제 모듈식 시스템(예: requirejs)을 사용하여 애플리케이션 어디에서나 이 생성자 함수에 대한 설명이 포함된 파일을 연결하고 다음을 실행하여 객체에 액세스할 수 있습니다.

Var someObj = 새로운 SomeFunction();

그러나 이 방법에는 단점도 있습니다. 인스턴스는 생성자의 정적 속성으로 저장되므로 누구나 덮어쓸 수 있습니다. 우리는 어떤 상황에서도 애플리케이션의 어느 부분에서나 필요한 객체에 접근할 수 있기를 원합니다. 이는 인스턴스를 저장할 변수를 비공개로 설정해야 하며 클로저가 이에 도움이 될 것임을 의미합니다.

함수 SomeFunction () ( var 인스턴스; SomeFunction = function () ( 인스턴스 반환; ) this.property1 = "값"; this.property2 = "값"; 인스턴스 = this; )

이것이 모든 문제에 대한 해결책인 것처럼 보이지만 새로운 문제가 오래된 문제를 대신합니다. 즉, 인스턴스가 생성된 후 생성자 프로토타입에 포함된 모든 속성을 사용할 수 없습니다. 실제로 새로 정의된 생성자가 아니라 이전 생성자에 기록됩니다. 하지만 이 상황에서 벗어날 수 있는 적절한 방법이 있습니다.

함수 SomeFunction () ( var 인스턴스; SomeFunction = function () ( 인스턴스 반환; ) SomeFunction.prototype = this; 인스턴스 = 새로운 SomeFunction (); 인스턴스.constructor = SomeFunction; 인스턴스.property1 = "값"; instance.property2 = " 값"; 반환 인스턴스; )

싱글톤을 설명하는 이 방법은 위의 모든 단점이 없으며 보편적인 사용에 매우 적합합니다. 그러나 클로저를 사용하여 싱글톤을 설명하는 방법은 requirejs에서는 작동하지 않지만 약간 수정하고 변수를 밖으로 이동하면 함수 자체에 의해 생성된 클로저를 정의에 사용된 함수에 추가하면 문제가 해결됩니다.

Define(, function () ( var 인스턴스 = null; function SomeFunction() ( if (인스턴스) ( 반환 인스턴스; ) this.property1 = "값"; this.property2 = "값"; 인스턴스 = this; ); SomeFunction 반환 ;));

팩토리 메소드 팩토리 메소드에는 두 가지 주요 목적이 있습니다.
1) 명시적으로 구체적인 클래스를 사용하지 마세요.
2) 일반적으로 사용되는 객체 초기화 방법을 함께 결합
팩토리 메소드의 가장 간단한 구현은 다음 예입니다.

Function Foo () ( //... ) function Bar () ( //... ) 함수 팩토리 (유형) ( 스위치 (유형) ( 케이스 "foo": return new Foo(); 케이스 "bar": return 새로운 바();

따라서 객체 생성은 다음과 같습니다.

Foo = 공장("foo"); bar = Factory("바");

보다 우아한 솔루션을 사용할 수 있습니다.

함수 PetFactory() ( ); PetFactory.register = function(name, PetConstructor) ( if (함수 이름 인스턴스) ( PetConstructor = 이름; 이름 = null; ) if (!(PetConstructor 인스턴스of 함수)) ( throw ( 이름: "오류", 메시지: "PetConstructor는 다음과 같습니다. 기능이 아님" ) ) this = PetConstructor; ); PetFactory.create = function(petName) ( var PetConstructor = this; if (!(PetConstructor 인스턴스of Function)) ( throw ( name: "Error", message: "constructor "" + petName + "" undefine" ) ) return new PetConstructor ();

이 경우 팩토리가 생성할 수 있는 클래스 수에 제한을 두지 않고 다음과 같은 방식으로 원하는 만큼 추가할 수 있습니다.

PetFactory.register("dog", function() ( this.say = function () ( console.log("gav"); ) ));

또는 다음과 같습니다:

Function Cat() ( ) Cat.prototype.say = function () ( console.log("meow"); ) PetFactory.register(Cat);

추상 팩토리(Abstract Factory) 추상 팩토리는 상호 관련되거나 상호 의존적인 개체 그룹을 만드는 데 사용됩니다.
동일한 요소로 구성된 여러 팝업 창이 있지만 이러한 요소는 다르게 보이고 사용자 작업에 다르게 반응한다고 가정합니다. 이러한 각 요소는 팩토리 메서드를 사용하여 생성됩니다. 즉, 각 팝업 창 유형에는 고유한 개체 팩토리가 필요합니다.
예를 들어 BluePopupFactory 팩토리를 설명하겠습니다. PetFactory와 구조가 똑같으므로 자세한 내용은 생략하고 그냥 사용하겠습니다.

Function BluePopup () ( //팝업 창 생성) BluePopup.prototype.attach = function (elemens) ( //창에 다른 ui 요소 연결) BluePopupFactory.register("popup", BluePopup); function BluePopupButton () ( //파란색 팝업 창용 버튼 생성) BluePopupButton.prototype.setText = function (text) ( //버튼의 텍스트 설정) BluePopupFactory.register("button", BluePopupButton); function BluePopupTitle () ( //파란창 제목 생성) BluePopupTitle.prototype.setText = function (text) ( //제목 텍스트 설정) BluePopupFactory.register("title", BluePopupTitle);

아마도 인터페이스 요소를 담당하는 일종의 클래스가 있어야 할 것입니다.

Function UI () ( //UI 요소를 담당하는 클래스)

그리고 여기에 createPopup 메소드를 추가하겠습니다.

UI.createPopup = 함수(공장) ( var popup = Factory.create("popup"), ButtonOk = Factory.create("버튼"), ButtonCancel = Factory.create("버튼"), title = Factory.create(" title");buttonOk.setText("확인");buttonCancel.setText("취소"); title.setText("제목 없음"); popup.attach(); )

보시다시피 createPopup은 팩토리를 인수로 사용하여 팝업 자체와 제목이 있는 버튼을 만든 다음 이를 창에 연결합니다.
그런 다음 이 방법을 다음과 같이 사용할 수 있습니다.

Var newPopup = UI.createPopup(BluePopupFactory);

따라서 팩토리를 무제한으로 설명하고 다음 팝업 창을 생성할 때 필요한 팩토리를 전달할 수 있습니다.

때로는 클래스를 직접 인스턴스화하는 것이 바람직하지 않은 경우도 있습니다. 그런 다음 최적의 인스턴스화 메커니즘을 선택할 수 있는 생성 패턴에 의존합니다.

심플팩토리

집을 지을 때 문을 직접 만드는 것은 꽤 어렵기 때문에 상점에서 미리 만들어 놓은 것을 구입합니다.

패턴 간단한 공장에서는 이 프로세스의 복잡성으로 인해 고객을 괴롭히지 않고 필요한 사본을 생산합니다.

구현 예

모든 문에 대한 암시적 인터페이스를 만들어 보겠습니다.

/* Door getWidth() getHeight() */ class WoodenDoor ( constructor(width, height)( this.width = width this.height = height ) getWidth() ( return this.width ) getHeight() ( return this.height ) )

우리는 이를 생산할 공장을 조직할 것입니다:

Const DoorFactory = ( makeDoor: (너비, 높이) => new WoodenDoor(너비, 높이) )

그게 다야, 당신은 일할 수 있습니다:

Const door = DoorFactory.makeDoor(100, 200) console.log("너비:", door.getWidth()) console.log("높이:", door.getHeight())

이 패턴은 객체 생성에 일부 논리가 필요한 경우 유용합니다. 반복되는 코드를 별도의 Simple Factory로 옮기는 것이 합리적입니다.

팩토리 메소드

채용 관리자는 다양한 공석에 대해 후보자와 협력합니다. 그는 각 직위의 복잡성을 파헤치는 대신 기술 인터뷰를 동료 전문가에게 위임합니다.

이 패턴을 사용하면 불필요한 코드로 생성자를 오염시키지 않고 객체의 다양한 변형을 만들 수 있습니다.

구현 예

햄버거 자체부터 시작해 보겠습니다.

클래스 버거 ( constructor(builder) ( this.size = builder.size this.cheeze = builder.cheeze || false this.pepperoni = builder.pepperoni || false this.lettuce = builder.lettuce || false this.tomato = builder .토마토 || 거짓 ) )

빌더는 다음과 같습니다.

클래스 BurgerBuilder ( constructor(size) ( this.size = size ) addPepperoni() ( this.pepperoni = true 반환 this ) addLettuce() ( this.lettuce = true 반환 this ) addCheeze() ( this.cheeze = true 반환 this ) addTomato() ( this.tomato = true return this ) build() ( return new Burger(this) ) )

짜잔! 우리 버거는 다음과 같습니다.

Constburger = (new BurgerBuilder(14)) .addPepperoni() .addLettuce() .addTomato() .build()

객체가 다양한 변형으로 존재할 수 있거나 인스턴스화 프로세스가 여러 단계로 구성된 경우 빌더 패턴이 필요합니다.

하나씩 일어나는 것

나라에는 대통령이 한 명 있어야 합니다. 그렇지 않으면 혼란이 생길 ​​것입니다.

이 패턴은 객체를 래핑하고 해당 동작을 동적으로 변경합니다.

구현 예

커피를 예로 들어보자. 적절한 인터페이스를 구현하는 가장 간단한 커피:

/* 커피 인터페이스: getCost() getDescription() */ class SimpleCoffee( getCost() ( return 10 ) getDescription() ( return "Simple Coffee" ) )

우리는 커피에 다양한 첨가물을 추가할 수 있기를 원합니다. 이를 위해 몇 가지 데코레이터를 만들 것입니다:

클래스 MilkCoffee ( 생성자(coffee) ( this.coffee = 커피 ) getCost() ( return this.coffee.getCost() + 2 ) getDescription() ( return this.coffee.getDescription() + ", milk" ) ) 클래스 WhipCoffee ( 생성자(coffee) ( this.coffee = 커피 ) getCost() ( return this.coffee.getCost() + 5 ) getDescription() ( return this.coffee.getDescription() + ",hip" ) ) 클래스 VanillaCoffee ( 생성자 (커피) ( this.coffee = 커피 ) getCost() ( return this.coffee.getCost() + 3 ) getDescription() ( return this.coffee.getDescription() + ", 바닐라" ) )

이제 원하는 대로 커피를 만들 수 있습니다.

Let someCoffee someCoffee = new SimpleCoffee() console.log(someCoffee.getCost())// 10 console.log(someCoffee.getDescription())// Simple Coffee someCoffee = new MilkCoffee(someCoffee) console.log(someCoffee.getCost( ))// 12 console.log(someCoffee.getDescription())// 일반 커피, 우유 someCoffee = new WhipCoffee(someCoffee) console.log(someCoffee.getCost())// 17 console.log(someCoffee.getDescription() )// 플레인 커피, 우유, 크림 someCoffee = new VanillaCoffee(someCoffee) console.log(someCoffee.getCost())// 20 console.log(someCoffee.getDescription())// 플레인 커피, 우유, 크림, 바닐라

정면

컴퓨터를 켜려면 버튼을 누르기만 하면 됩니다. 아주 간단하지만, 전원을 켜는 컴퓨터 내부에는 복잡한 일이 많이 일어나고 있습니다. 복잡한 시스템에 대한 간단한 인터페이스는 Façade입니다.

구현 예

컴퓨터 클래스를 만들어 보겠습니다.

클래스 컴퓨터 ( getElectricShock() ( console.log("아야!") ) makeSound() ( console.log("삐삐!") ) showLoadingScreen() ( console.log("Loading..") ) bam() ( console.log("사용할 준비가 되었습니다!") ) closeEverything() ( console.log("Bup bup buzzzz!") ) sooth() ( console.log("Zzzzz") ) pullCurrent() ( console.log("Bup bup buzzzz!") log("하아아!") ) )

복잡한 기능을 위한 간단한 Façade:

클래스 ComputerFacade ( 생성자(컴퓨터) ( this.computer = 컴퓨터 ) TurnOn() ( this.computer.getElectricShock() this.computer.makeSound() this.computer.showLoadingScreen() this.computer.bam() ) TurnOff() ( this.computer.closeEverything() this.computer.pullCurrent() this.computer.sooth() ) )

이렇게 하면 컴퓨터 작업이 훨씬 쉬워집니다.

Const 컴퓨터 = new ComputerFacade(new Computer()) 컴퓨터.turnOn() // 아야! 삐삐! 로드 중.. 사용할 준비가 되었습니다! computer.turnOff() // 낑낑 윙윙 소리! 하아! Zzzzz

기회주의자

장거리 열차에서는 모든 사람이 동시에 뜨거운 음료수를 큰 용기에 끓입니다. 이를 통해 전기(또는 가스)를 절약할 수 있습니다.

구직 사이트에서는 관심 있는 직업 옵션을 구독할 수 있습니다. 적절한 제안이 나타나면 사이트에서 알림을 보냅니다.

관찰자 패턴을 사용하면 발생한 변경 사항에 대해 관심 있는 모든 객체에 알릴 수 있습니다.

구현 예

지원자가 알림을 받기를 원하는 경우:

Const JobPost = title = (( title: title )) class JobSeeker ( constructor(name) ( this._name = name ) inform(jobPost) ( console.log(this._name, "새 게시물에 대한 알림을 받았습니다:", jobPost.제목) ) )

게시판에서는 다음과 같은 알림을 보낼 수 있습니다.

클래스 JobBoard ( constructor() ( this._subscribers = ) subscribe(jobSeeker) ( this._subscribers.push(jobSeeker) ) addJob(jobPosting) ( this._subscribers.forEach(subscriber = ( subscriber.notify(jobPosting) )) ) )

// 구독자 생성 const jonDoe = new JobSeeker("John Doe") const janeDoe = new JobSeeker("Jane Doe") const kaneDoe = new JobSeeker("Kane Doe") // 메시지 보드 생성 // 지원자 가입 const jobBoard = new JobBoard() jobBoard.subscribe(jonDoe) jobBoard.subscribe(janeDoe) // 가입자에게 새로운 공석에 대해 알림 jobBoard.addJob(JobPost("Software Engineer")) // John Doe는 새 게시물에 대한 알림을 받았습니다. 소프트웨어 엔지니어 // Jane Doe에게 새로운 게시물이 통보되었습니다: 소프트웨어 엔지니어

방문객

해외여행을 하기 위해서는 허가(비자)를 받아야 합니다. 하지만 일단 국내에 들어오시면 별도의 허락을 구하지 않고도 다양한 장소를 안전하게 방문하실 수 있습니다. 당신은 그들에 대해 알아야합니다.

방문자 패턴을 사용하면 소스 코드를 변경하지 않고도 개체에 추가 작업을 추가할 수 있습니다.

구현 예

다양한 종류의 동물이 있는 동물원을 시뮬레이션해 보겠습니다.

클래스 Monkey ( 외침() ( console.log("오우아아아아!") ) 수락(작업) ( Operation.visitMonkey(this) ) 클래스 라이온 ( roar() ( console.log("로아아아!") ) 수락 (작업) ( Operation.visitLion(this) ) ) class Dolphin ( talk() ( console.log("Tuttu tuutt!") ) accept(작업) ( Operation.visitDolphin(this) ) )

이제 우리는 그들이 어떤 소리를 내는지 듣고 싶습니다. 이를 위해 방문자를 생성합니다.

Const talk = ( VisitMonkey(monkey)( Monkey.shout() ), VisitLion(lion)( lion.roar() ), VisitDolphin(dolphin)( 돌핀.speak() ) )

단순히 각 클래스에 액세스하고 원하는 메서드를 호출합니다.

Const Monkey = new Monkey() const lion = new Lion() const 돌고래 = new Dolphin() Monkey.accept(speak) // 오오오 아아아! lion.accept(speak) // 포효! 돌고래.accept(말하기) // 뚜뚜뚜뚜뚜뚜!

방문자는 기존 개체가 수정되지 않도록 허용합니다. 예를 들어, 도움을 받으면 추가 방법을 만들지 않고도 이러한 모든 동물에게 점프하는 기능을 추가할 수 있습니다.

Const jump = ( VisitMonkey(monkey) ( console.log("20피트 높이로 뛰어올랐습니다! 나무 위로 뛰어올랐습니다!") ), VisitLion(lion) ( console.log("7피트 높이 뛰어올랐습니다! 다시 땅바닥으로 뛰어올랐습니다!") ) , VisitDolphin(돌고래) ( console.log("물 위를 조금 걷고 사라졌습니다.") ) )

Monkey.accept(speak) // 오오오아아아! Monkey.accept(jump) // 20피트 높이로 점프했습니다! 나무에! lion.accept(speak) // 포효! lion.accept(jump) // 7피트 점프했습니다! 다시 지상으로! 돌고래.accept(말하기) // 뚜뚜뚜뚜뚜뚜! 돌고래.accept(jump) // 물 위를 조금 걷고 사라졌습니다.

전략

일부 데이터 세트를 구성하려면 버블 정렬 알고리즘을 사용합니다. 작은 볼륨에는 잘 대처하지만 큰 볼륨에는 속도가 느려집니다. Quicksort에는 반대 문제가 있습니다. 그런 다음 세트의 크기에 따라 알고리즘을 변경하기로 결정합니다. 이것이 당신의 전략입니다.

전략 템플릿을 사용하면 상황에 따라 사용되는 알고리즘을 전환할 수 있습니다.

구현 예

일류 함수는 JavaScript로 전략을 구현하는 데 도움이 됩니다.

Const bubbleSort = 데이터 세트 => ( console.log("버블 정렬을 사용하여 정렬") // ... // ... return 데이터 세트 ) const QuickSort = 데이터 세트 => ( console.log("빠른 정렬을 사용하여 정렬") / / ... // ... 데이터세트 반환 )

그리고 이것은 어떤 전략이든 사용할 수 있는 고객입니다.

Const sorter = 데이터세트 => ( if(dataset.length > 5)( QuickSort 반환 ) else ( bubbleSort 반환 ) )

이제 배열을 정렬할 수 있습니다.

Const longDataSet = const shortDataSet = const sorter1 = sorter(longDataSet) const sorter2 = sorter(shortDataSet) sorter1(longDataSet) // 퀵 정렬로 정렬 sorter2(shortDataSet) // 버블 정렬로 정렬

상태

페인트로 그립니다. 선택에 따라 브러시의 상태가 변경됩니다. 빨간색, 파란색 또는 기타 색상으로 칠해집니다.

State 패턴을 사용하면 상태가 변경될 때 클래스의 동작을 변경할 수 있습니다.

구현 예

텍스트 상태(굵게, 기울임꼴 등)를 변경할 수 있는 텍스트 편집기를 만들어 보겠습니다.

변환 기능은 다음과 같습니다.

Const upperCase = inputString => inputString.toUpperCase() const lowerCase = inputString => inputString.toLowerCase() const defaultTransform = inputString => inputString

그리고 편집자 자신은 다음과 같습니다.

클래스 TextEditor ( 생성자(transform) ( this._transform = 변환 ) setTransform(transform) ( this._transform = 변환 ) type(words) ( console.log(this._transform(words)) ) )

당신은 일할 수 있습니다 :

Const editor = new TextEditor(defaultTransform) editor.type("첫 번째 줄") editor.setTransform(upperCase) editor.type("두 번째 줄") editor.type("세 번째 줄") editor.setTransform(lowerCase) editor.type ("네 번째 줄") editor.type("다섯 번째 줄") // 첫 번째 줄 // 두 번째 줄 // 세 번째 줄 // 네 번째 줄 // 다섯 번째 줄

템플릿 방식

특정 계획에 따라 집을 지을 수 있습니다. 먼저 기초, 그다음 벽, 그 다음 지붕입니다. 이러한 단계의 순서는 변경할 수 없지만 구현은 다를 수 있습니다.

템플릿 메소드는 알고리즘의 "골격"을 정의하지만 단계 구현을 하위 클래스에 위임합니다.

구현 예

애플리케이션을 테스트하고 구축하고 배포하기 위한 도구를 만들어 보겠습니다.

기본 클래스는 어셈블리 알고리즘의 뼈대를 정의합니다.

클래스 빌더( // 템플릿 메소드 build() ( this.test() this.lint() this.assemble() this.deploy() ) )

하위 클래스는 각 단계를 구체적으로 구현합니다.

클래스 AndroidBuilder는 Builder를 확장합니다. ( test() ( console.log("Android 테스트 실행") ) lint() ( console.log("안드로이드 코드 린팅") ) assemble() ( console.log("Android 빌드 조립" ) ) 배포() ( console.log("서버에 android 빌드 배포") ) class IosBuilder 확장 빌더 ( test() ( console.log("ios 테스트 실행") ) lint() ( console.log("린팅 ios 코드") ) assemble() ( console.log("ios 빌드 어셈블링") ) 배포() ( console.log("서버에 ios 빌드 배포") ) )

프로젝트를 조립해 보겠습니다.

Const androidBuilder = new AndroidBuilder() androidBuilder.build() // 안드로이드 테스트 실행 // 안드로이드 코드 린팅 // 안드로이드 빌드 어셈블 // 서버에 안드로이드 빌드 배포 const iosBuilder = new IosBuilder() iosBuilder.build() // iOS 테스트 실행 // iOS 코드 린팅 // iOS 빌드 어셈블 // 서버에 iOS 빌드 배포

  • 번역

번역자 주: JavaScript의 상속 주제는 초보자에게 가장 어려운 주제 중 하나입니다. class 키워드가 포함된 새로운 구문이 추가됨에 따라 근본적으로 새로운 것은 나타나지 않지만 상속을 이해하는 것이 더 쉬워지지는 않았습니다. 이 글은 JavaScript에서 프로토타입 상속 구현의 미묘한 차이를 다루지 않으므로 독자가 질문이 있는 경우 다음 글을 읽어 보시기 바랍니다. JavaScript에 대한 기본 및 오해 및 JavaScript의 OOP 이해 [1부]

번역과 관련된 의견은 개인 메시지로 문의해주세요.

JavaScript는 매우 강력한 언어입니다. 너무 강력해서 객체를 디자인하고 생성하는 다양한 방법이 그 안에 공존합니다. 각 방법에는 장단점이 있으므로 초보자가 이를 알아낼 수 있도록 돕고 싶습니다. 이것은 내 이전 게시물인 JavaScript "분류" 중지에 이어지는 것입니다. 나는 예시를 요청하는 많은 질문과 댓글을 받았고 바로 이러한 목적을 위해 이 글을 쓰기로 결정했습니다.

JavaScript는 프로토타입 상속을 사용합니다. 이는 JavaScript에서 객체가 다른 객체로부터 상속된다는 의미입니다. () 중괄호를 사용하여 만든 JavaScript의 간단한 객체에는 프로토타입이 하나만 있습니다. 객체.프로토타입. 객체.프로토타입, 또한 객체이며 모든 속성과 메서드 객체.프로토타입모든 객체에 사용 가능합니다.

대괄호를 사용하여 생성된 배열에는 다음을 포함한 여러 프로토타입이 있습니다. 객체.프로토타입그리고 배열.프로토타입. 이는 모든 속성과 메서드를 의미합니다. 객체.프로토타입그리고 배열.프로토타입모든 어레이에 사용 가능합니다. 예를 들어 동일한 이름의 속성 및 메서드 .valueOf그리고 .ToString는 가장 가까운 프로토타입에서 호출됩니다. 이 경우에는 배열.프로토타입.

프로토타입 정의 및 객체 생성 방법 1: 생성자 패턴 JavaScript에는 다른 언어의 생성자와 동일한 방식으로 작동하는 생성자라는 특수한 유형의 함수가 있습니다. 생성자 함수는 키워드를 사용해서만 호출됩니다. 새로운생성된 객체를 키워드를 통해 생성자 함수의 컨텍스트와 연결합니다. 이것. 일반적인 생성자는 다음과 같습니다.
function Animal(type)( this.type = type; ) Animal.isAnimal = function(obj, type)( if(!Animal.prototype.isPrototypeOf(obj))( return false; ) 반환 유형 ? obj.type === 유형: 참); function Dog(이름, 품종)( Animal.call(this, "개"); this.name = 이름; this.breed = 품종; ) Object.setPrototypeOf(Dog.prototype, Animal.prototype); Dog.prototype.bark = function())( console.log("ruff, ruff"); ); Dog.prototype.print = function())( console.log("The dog " + this.name + " is a " + this.breed); ); Dog.isDog = function(obj)( return Animal.isAnimal(obj, "dog"); );
이 생성자를 사용하는 것은 다른 언어로 객체를 생성하는 것과 동일해 보입니다.
var Sparkie = new Dog("스파키", "보더 콜리"); 스파키.이름; // "스파키" Sparkie.breed; // "보더 콜리" Sparkie.bark(); // 콘솔: "멍, 멍" Sparkie.print(); // 콘솔: "Sparkie라는 개는 보더 콜리입니다." Dog.isDog(sparkie); // 진실
짖다그리고 인쇄생성자를 사용하여 생성된 모든 객체에 적용되는 프로토타입 메서드 . 속성 이름그리고 새끼를 낳다생성자에서 초기화됩니다. 모든 메서드는 프로토타입에 정의되고 속성은 생성자에 의해 초기화되는 것이 일반적인 관행입니다. 방법 2: ES2015(ES6) 키워드에서 클래스 정의 수업은 처음부터 JavaScript에 예약되어 있었으며 이제 마침내 사용할 시간입니다. JavaScript의 클래스 정의는 다른 언어와 유사합니다.
class Animal ( constructor(type)( this.type = type; ) static isAnimal(obj, type)( if(!Animal.prototype.isPrototypeOf(obj))( return false; ) 반환 유형 ? obj.type === 유형 : true; ) ) 클래스 Dog 확장 Animal ( constructor(name, breed)( super("dog"); this.name = name; this.breed = breed; )bark())( console.log("ruff, ruff " ); ) print() ( console.log("The dog " + this.name + " is a " + this.breed); ) static isDog(obj)( return Animal.isAnimal(obj, "dog"); ) )
많은 사람들이 이 구문이 정적 메소드와 프로토타입 메소드의 생성자와 선언을 하나의 블록에 결합하기 때문에 편리하다고 생각합니다. 사용법은 이전 방법과 완전히 동일합니다.
var Sparkie = new Dog("스파키", "보더 콜리"); 방법 3: 명시적 프로토타입 선언, Object.create, 팩토리 방법 이 방법은 새 키워드 구문이 실제로 무엇인지 보여줍니다. 수업프로토타입 상속을 사용합니다. 이 방법을 사용하면 연산자를 사용하지 않고도 새 개체를 만들 수도 있습니다. 새로운.
var Animal = ( create(type)( var 동물 = Object.create(Animal.prototype);animal.type = type; returnanimal; ), isAnimal(obj, type)( if(!Animal.prototype.isPrototypeOf(obj) )( return false; ) 반환 유형 ? obj.type === 유형: true ), 프로토타입: () ); var Dog = ( create(이름, 품종)( var proto = Object.sign(Animal.create("dog"), Dog.prototype); var dog = Object.create(proto); dog.name = 이름; 개. breed = breed; return dog; ), isDog(obj)( return Animal.isAnimal(obj, "dog"); ), 프로토타입: (bark())( console.log("ruff, ruff"); ), 인쇄 ( )( console.log("The dog " + this.name + " is a " + this.breed); ) ) );
이 구문은 프로토타입이 명시적으로 선언되므로 편리합니다. 프로토타입에 정의된 내용과 객체 자체에 정의된 내용이 명확합니다. 방법 객체.생성지정된 프로토타입에서 객체를 생성할 수 있기 때문에 편리합니다. 확인 중 .isPrototypeOf두 경우 모두 여전히 작동합니다. 용도는 다양하지만 과도하지는 않습니다.
var Sparkie = Dog.create("스파키", "보더 콜리"); 스파키.이름; // "스파키" Sparkie.breed; // "보더 콜리" Sparkie.bark(); // 콘솔: "멍, 멍" Sparkie.print(); // 콘솔: "Sparkie라는 개는 보더 콜리입니다." Dog.isDog(sparkie); // true 방법 4: Object.create, 최상위 팩토리, 지연된 프로토타입 이 방법은 클래스가 팩토리 메서드를 사용하는 객체인 경우와 달리 클래스 자체가 팩토리인 메서드 3을 약간 수정한 것입니다. . 생성자 예제(방법 1)와 유사하지만 팩토리 메서드를 사용하고 객체.생성.
function Animal(type)( var 동물 = Object.create(Animal.prototype);animal.type = type; returnanimal; ) Animal.isAnimal = function(obj, type)( if(!Animal.prototype.isPrototypeOf(obj) )( return false; ) 반환 유형 ? obj.type === 유형: true ); Animal.prototype = (); function Dog(이름, 품종)( var proto = Object.ass(Animal("dog"), Dog.prototype); var dog = Object.create(proto); dog.name = 이름; dog.breed = 품종; return 개; ) Dog.isDog = function(obj)( return Animal.isAnimal(obj, "dog"); ); Dog.prototype = ( 껍질())( console.log("ruff, ruff"); ), print())( console.log("The dog " + this.name + " is a " + this.breed) ; ) );
이 방법은 첫 번째 방법과 유사하지만 키워드가 필요하지 않기 때문에 흥미롭습니다. 새로운그리고 운영자와 함께 일합니다 대신에. 사용법은 첫 번째 방법과 동일하지만 키워드를 사용하지 않습니다. 새로운:
var Sparkie = Dog("스파키", "보더 콜리"); 스파키.이름; // "스파키" Sparkie.breed; // "보더 콜리" Sparkie.bark(); // 콘솔: "멍, 멍" Sparkie.print(); // 콘솔: "Sparkie라는 개는 보더 콜리입니다." Dog.isDog(sparkie); // true 비교 방법 1과 방법 4 방법 4 대신 방법 1을 사용해야 하는 이유는 거의 없습니다. 방법 1에는 키워드를 사용해야 합니다. 새로운, 또는 생성자에 다음 검사를 추가합니다.
if(!(Foo의 이 인스턴스))( return new Foo(a, b, c); )
이 경우에는 사용하기가 더 쉽습니다. 객체.생성팩토리 메소드로. 기능도 사용할 수 없습니다 함수#호출또는 기능#적용생성자 함수를 사용하는 이유는 키워드 컨텍스트를 재정의하기 때문입니다. 이것. 위의 확인으로 이 문제가 해결될 수 있지만 미리 알 수 없는 개수의 인수로 작업해야 하는 경우에는 팩토리 메서드를 사용해야 합니다. 방법 2 대 방법 3 생성자와 연산자에 대한 동일한 추론입니다. 새로운위에서 언급한 내용이 이 경우에 적용됩니다. 확인 중 대신에새로운 구문을 사용하는 경우 필수 수업연산자를 사용하지 않고 새로운또는 사용됩니다 함수#호출또는 기능#적용.내 의견 프로그래머는 자신의 코드를 명확하게 작성하기 위해 노력해야 합니다. 방법 3 구문은 실제로 무슨 일이 일어나고 있는지 매우 명확하게 보여줍니다. 또한 다중 상속과 스택 상속을 쉽게 사용할 수 있습니다. 운영자님 이후로 새로운와 호환되지 않아 개방/폐쇄 원칙을 위반합니다. 적용하다또는 부르다, 피해야 합니다. 예어 수업클래스 시스템이라는 마스크 뒤에 JavaScript 상속의 원형적 특성을 숨깁니다.
"단순한 것이 정교한 것보다 낫다"고 클래스를 사용하는 것은 더 "정교한" 것으로 간주되기 때문에 불필요하고 기술적인 골칫거리일 뿐입니다.
용법 객체.생성 copula를 사용하는 것보다 더 표현적이고 명확합니다. 새로운그리고 이것. 또한 프로토타입은 공장 자체의 컨텍스트 외부에 있을 수 있는 개체에 저장되므로 메서드를 추가하여 더 쉽게 수정하고 확장할 수 있습니다. ES6의 클래스와 같습니다.
예어 수업, JavaScript의 가장 해로운 기능일 수 있습니다. 나는 표준을 작성하는 과정에 참여했던 훌륭하고 열심히 일하는 사람들에 대해 엄청난 존경심을 가지고 있습니다. 그러나 심지어 훌륭한 사람들조차도 때로는 잘못된 일을 합니다. - 에릭 엘리엇
언어의 본성에 반하여 불필요하고 해로울 수 있는 것을 추가하는 것은 경솔하고 잘못된 것입니다.
이용하기로 결정하셨다면 수업, 나는 진심으로 당신의 코드로 작업할 필요가 없기를 바랍니다. 제 생각에는 개발자는 생성자를 사용하는 것을 피해야 합니다. 수업그리고 새로운, 언어의 패러다임과 아키텍처에 더 자연스러운 방법을 사용합니다. 용어 사전 객체.할당(a, b)객체의 모든 열거 가능한 속성을 복사합니다. 반대하다 그런 다음 객체를 반환합니다.
Object.create(프로토)지정된 프로토타입에서 새 객체를 생성합니다. 프로토
Object.setPrototypeOf(obj, proto)내부 속성을 변경합니다. [] 물체 객체~에 프로토

태그: 태그 추가

WordPress는 어디에서나 템플릿을 사용하며 Javascript도 예외는 아닙니다. 이 게시물에서는 JS에서 사용할 수 있는 HTML 템플릿을 생성하기 위해 WordPress에 내장된 기능에 대해 설명하겠습니다. 이러한 템플릿은 WordPress의 다른 많은 기능과 마찬가지로 생성 및 사용이 매우 쉽습니다.

Javascript에서 템플릿을 생성하는 방법에는 여러 가지가 있으며, Mustache라는 별도의 사양도 있습니다. Javascript를 포함한 다양한 언어로 구현됩니다. 예를 들어 Handlebars 라이브러리는 이 사양을 사용하고 이를 약간 확장합니다. 또는 인기 있는 Underscore 미니 라이브러리도 있습니다.

버전 3.5부터 WordPress의 핵심에는 이미 편리한 JS용 템플릿 엔진이 있습니다. 예를 들어 미디어 로더에 대한 블록을 생성할 때 관리자 패널에서 사용됩니다. 앞서 언급한 Underscore 라이브러리를 기반으로 구문이 Mustache 사양과 더욱 일치하도록 약간 수정되었습니다.

WordPress에서 템플릿을 생성하려면 wp.template 메소드가 있습니다.

wp.template(id)

HTML 코드에서 템플릿 개체를 만듭니다. JS에서 사용할 기성 HTML 코드를 얻으려면 템플릿을 작성하기 위한 데이터를 생성된 객체에 전달해야 합니다.

보고

기능. 템플릿 보간을 위해 데이터를 전달하는 함수입니다.

var template = wp.template(id) 사용; var HTML = 템플릿(데이터); ID (선)

템플릿의 HTML 코드가 포함된 HTML 요소의 ID입니다. HTML 요소에는 tmpl- 접두사와 함께 여기에 지정된 id 속성이 있어야 합니다.

예를 들어 여기서 foo를 지정하는 경우 HTML 요소의 ID는 id="tmpl-foo" 여야 합니다.

데이터 (객체)템플릿을 채우는 데 사용될 JS 데이터 개체입니다. 예: ( 텍스트: "안녕하세요") .

패턴 채우기(보간)
  • (((data.unescaped))) - 정리되지 않은 데이터.
  • ((data.escaped)) - 데이터가 지워졌습니다.
  • - js(평가)를 처리합니다.
데이터 접두사.

템플릿의 데이터는 소스 데이터 개체입니다. 데이터 키는 템플릿에 사용되어야 합니다.

wp_send_json_success() 및 wp_send_json_error() 함수에서 반환된 데이터 구조를 일치시키기 위해 wp.template은 수신된 모든 데이터를 데이터 변수에 래핑합니다. 따라서 템플릿의 각 매개변수 앞에 데이터를 지정해야 합니다. 그렇지 않으면 (property) is not Defined 오류가 발생합니다.

정확함 (((data.name)))

부정확함 (((이름)))

예제 템플릿

간단하게 출력됩니다.

escapedValue ((data.escapedValue)) 변수의 값을 인쇄해 보겠습니다.

데이터에 마크업이 포함되어 있으면 이스케이프하지 않고 인쇄하세요.

(((data.unescapedValue)))

어떤 논리를 수행해야 할 때.

data.trueValue = true인 경우에만 인쇄됩니다.

템플릿 생성 및 생성 템플릿 생성

템플릿이 DOM 트리에 어떤 방식으로든 표시되지 않도록 하려면 type="text/html" 유형 표시를 사용하여 스크립트 태그에 템플릿을 생성하는 것이 일반적입니다.

안녕하세요 (((data.name)))

id 속성은 tmpl- 로 시작해야 하며, 이 접두사 뒤의 모든 항목은 wp.template("my-template") 함수에서 사용됩니다.

스크립트 태그에 템플릿을 생성하는 것은 브라우저에서 어떤 방식으로든 사용되지 않는 html 요소를 생성하는 데 유용한 방법입니다. 브라우저가 이해할 수 없는 유형이 지정되면 브라우저는 우리에게 필요한 html 태그를 무시합니다.

템플릿은 다른 HTML 요소(예: 숨길 수 있는 요소)에서도 생성될 수 있으며, id 속성만 지정하면 됩니다.

템플릿을 생성하기 위한 특별한 HTML 태그도 있지만 IE에서는 지원되지 않습니다. 그러나 전반적으로 그것은 상당히 관련이 있습니다.

템플릿 생성

wp.template()은 함수를 반환하므로 결과를 html 요소에 전달하거나 결과를 콘솔에 인쇄하려고 시도하지 마세요. 일반적으로 wp.template()의 결과는 변수로 전달되며, 해당 변수는 함수로 사용되어 템플릿을 채워야 하는 데이터를 전달합니다.

예(위에 표시된 템플릿)

// JS var template = wp.template("my-template"), data = ( name: "Victor" ); jQuery(".my-element").html(template(data));

결과적으로 우리는 HTML을 얻습니다:

안녕하세요 빅터

템플릿을 사용한 AJAX 주석 달기의 예

템플릿을 생성하고 테마의 function.php 파일에 스크립트를 포함시킵니다:

  • (((data.gravatar))) ((data.comment_author))