>  기사  >  웹 프론트엔드  >  고급 웹 애플리케이션을 만들기 위한 js 객체지향 기술_js 객체지향

고급 웹 애플리케이션을 만들기 위한 js 객체지향 기술_js 객체지향

WBOY
WBOY원래의
2016-05-16 18:33:53916검색

JavaScript 개체는 사전입니다.
C 또는 C#에서 개체에 관해 말할 때 클래스나 구조체의 인스턴스를 의미합니다. 객체는 인스턴스화되는 템플릿(즉, 클래스)에 따라 다른 속성과 메서드를 갖습니다. JavaScript 객체의 경우에는 그렇지 않습니다. JavaScript에서 개체는 이름/값 쌍의 집합입니다. 즉, JavaScript 개체를 문자열 키가 포함된 사전으로 생각하세요. 객체의 속성을 가져오고 설정하기 위해 익숙한 "."(점) 연산자나 "[]" 연산자를 사용할 수 있는데, 이는 사전 작업 시 일반적인 접근 방식입니다. 다음 코드 조각

코드 복사 코드는 다음과 같습니다.

var userObject = new Object ();
userObject.lastLoginTime = new Date();
alert(userObject.lastLoginTime);

은 다음 코드 조각과 정확히 동일하게 작동합니다.
코드 복사 코드는 다음과 같습니다.

var userObject = {} // new Object()와 같습니다.
userObject["lastLoginTime" ] = new Date();
alert(userObject[“lastLoginTime”]);

userObject 정의에서 직접 lastLoginTime 속성을 정의할 수도 있습니다.
코드 복사 코드는 다음과 같습니다.

var userObject = { “ lastLoginTime”: new Date() };
alert(userObject.lastLoginTime);

C# 3.0 개체 초기화 프로그램과 매우 유사합니다. 또한 Python에 익숙한 사람들은 두 번째 및 세 번째 코드 조각에서 userObject를 인스턴스화하는 것이 Python에서 사전을 지정하는 것과 정확히 동일하다는 것을 알 수 있습니다. 유일한 차이점은 JavaScript 개체/사전은 Python 사전과 같은 해시 가능한 개체가 아닌 문자열 키만 허용한다는 것입니다.
이러한 예는 또한 JavaScript 개체가 C 또는 C# 개체보다 더 유연하다는 것을 보여줍니다. lastLoginTime 속성을 미리 선언할 필요는 없습니다. userObject에 해당 이름의 속성이 없으면 속성이 userObject에 직접 추가됩니다. JavaScript 개체가 사전이라는 점을 기억한다면 이는 놀라운 일이 아닙니다. 결국 우리는 항상 사전에 새로운 키워드(및 해당 값)를 추가하고 있습니다.
이런 식으로 객체 속성이 있습니다. 객체 메소드는 어떻습니까? 마찬가지로 JavaScript는 C/C#과 다릅니다. 객체 메소드를 이해하려면 먼저 JavaScript 함수를 자세히 살펴봐야 합니다.
자바스크립트 함수가 최고입니다
많은 프로그래밍 언어에서 함수와 객체는 서로 다른 것으로 간주되는 경우가 많습니다. JavaScript에서는 구별이 모호합니다. JavaScript 함수는 실제로 실행 가능한 코드가 연결된 개체입니다. 일반적인 함수를 이렇게 생각해보세요:
코드 복사 코드는 다음과 같습니다.

함수 func(x) {
alert(x);
}
func(“blah”)

이것은 JavaScript에서 함수가 일반적으로 정의되는 방식입니다. 그러나 함수는 다음과 같이 정의할 수도 있습니다. 여기서 익명 함수 객체를 생성하고 이를 func 변수에 할당합니다
코드 복사 코드는 다음과 같습니다.

var func = function(x) {
alert(x)
}
func(“blah2”);
다음과 같이 함수 생성자를 사용할 수도 있습니다.

코드 복사 코드는 다음과 같습니다.
var func = new Function(“x”, “alert(x);”)
func(“blah3”)

이 예는 다음을 보여줍니다. 함수는 실제로 함수 호출이 작동하는 개체를 지원합니다. Function 생성자를 사용하여 함수를 정의하는 마지막 방법은 일반적으로 사용되지 않지만 함수 본문이 정확히 Function 생성자의 String 매개 변수라는 점을 알 수 있기 때문에 이것이 제공하는 가능성은 매우 흥미롭습니다. 이는 런타임에 임의의 함수를 구성할 수 있음을 의미합니다.
함수가 객체라는 점을 더 자세히 보여주기 위해 다른 JavaScript 객체와 마찬가지로 함수에 속성을 설정하거나 추가할 수 있습니다.

코드 복사코드는 다음과 같습니다.

function sayHi(x) {
alert(“안녕, “ x “!”)
}
sayHi.text = “Hello World!”
sayHi[“ text2"] = "Hello World... 다시.";
alert(sayHi["text"]); // "Hello World!" 표시
alert(sayHi.text2); 또... 세계.”

함수는 객체로서 변수에 할당될 수도 있고, 다른 함수에 매개변수로 전달될 수도 있고, 다른 함수의 값으로 반환될 수도 있고, 저장을 위한 객체 또는 배열 요소 등. 그림 1은 그러한 예를 제공합니다.
자바스크립트의 함수가 최고입니다
코드 복사 코드는 다음과 같습니다.

/ / 변수에 익명 함수 할당
var Greeting = function(x) {
alert(“Hello, “ x)
}
greet(“MSDN reader”); > // 함수를 다른 인수로 전달
function square(x) {
return x * x
}
function OperateOn(num, func) {
return func(num; ) ;
}
// 256개 표시
alert(operateOn(16, square))
// 반환 값으로 함수 ​​
function makeIncrementer() {
반환 함수 (x) { return x 1;
}
var inc = makeIncrementer()
// 8개 표시
alert(inc(7))// 배열 요소
var arr = [];
arr[0] = function(x) { return x * x }; arr[2 ] = arr[0](arr[1]);
arr[3] = arr[0](arr[2])
// 256개 표시
alert(arr[3 ]);// 객체 속성으로 기능합니다.
var obj = { “toString” : function() { return “This is an object.” } }// obj.toString() 호출
Alert(obj);


이를 염두에 두고 객체에 메서드를 추가하는 것은 쉽습니다. 이름을 선택하고 해당 이름에 함수를 할당하기만 하면 됩니다. 따라서 해당 메서드 이름에 익명 함수를 할당하여 개체에 세 가지 메서드를 정의했습니다.
Code



Copy Code

코드는 다음과 같습니다: var myDog = { “name” : “Spot”, “bark” : function() { Alert(“Woof!”) }, "displayFullName" : function() {
alert(this.name "The Alpha Dog");
},
"chaseMrPostman" : function() {
// 구현 이 글의 범위
}
};
myDog.bark(); // Woof!


C /C# 개발 displayFullName 함수에 사용되는 "this" 키워드를 숙지하세요. 이는 메서드가 호출되는 개체를 나타냅니다(Visual Basic을 사용하는 개발자도 "Me"라고 하는 이 키워드에 익숙해야 합니다). 따라서 위의 예에서 displayFullName의 "this" 값은 myDog 개체입니다. 그러나 "this"의 값은 정적이지 않습니다. "this"가 다른 개체에서 호출되면 해당 값은 그림 2와 같이 해당 개체를 가리키도록 변경됩니다.
객체 변경에 따라 "this"도 변경됩니다



코드 복사


코드는 다음과 같습니다.
function displayQuote() { // "this"의 값은 // alert(this.memorableQuote) }를 통해 호출되는 개체에 따라 달라집니다. >var williamShakespeare = {
"memorableQuote": "자기 자식을 아는 현명한 아버지입니다.",
"sayIt" : displayQuote
}
var markTwain = {
" 기억에 남는Quote": "골프는 좋은 걷기입니다.",
"sayIt" : displayQuote
};
var oscarWilde = {
"memorableQuote": "진정한 친구는 당신의 앞을 찌를 것입니다."
// 함수를 oscarWilde의 메서드로 지정하지 않고 oscarWilde의 메서드로
// 호출할 수 있습니다.
//"sayIt" : displayQuote
}; >williamShakespeare.sayIt(); // true, true
markTwain.sayIt(); // 그는 골프를 어디서 칠지 몰랐습니다.
// 각 함수에는 call() 메서드가 있습니다. >//
// 인수로 call()에 전달된 객체의
// 메서드로 함수를 호출할 수 있도록 하는 코드입니다.
// 아래 줄은
을 할당하는 것과 같습니다. // sayIt을 표시하고 oscarWilde.sayIt()을 호출합니다.
displayQuote.call(oscarWilde) //


그림 2의 마지막 줄은 함수를 객체의 메서드로 호출하는 또 다른 방법을 나타냅니다. JavaScript의 함수는 객체라는 것을 기억하세요. 모든 함수 객체에는 함수를 첫 번째 인수로 호출하는 call이라는 메서드가 있습니다. 즉, 함수의 첫 번째 매개변수로 call에 전달된 모든 개체는 함수 호출에서 "this"의 값이 됩니다. 이 기술은 나중에 다루게 될 기본 클래스 생성자를 호출하는 데 유용합니다.
기억해야 할 한 가지는 "this"가 포함된(그러나 객체를 소유하지 않은) 함수를 절대 호출하지 않는다는 것입니다. 그렇지 않으면 전역 네임스페이스를 위반하게 됩니다. 왜냐하면 이 호출에서 "this"는 전역 객체를 참조하게 되어 필연적으로 애플리케이션에 재앙을 초래할 것이기 때문입니다. 예를 들어, 다음 스크립트는 JavaScript의 전역 함수 isNaN의 동작을 변경합니다. 이러지 마세요!
코드
코드 복사 코드는 다음과 같습니다.

alert("NaN은 NaN: " isNaN(NaN));
function x() {
this.isNaN = function() {
return “더 이상!”;
};
}
// 경고 !!! 전역 개체 짓밟기!!!
x();
alert(“NaN은 NaN: “ isNaN(NaN))

속성 및 메서드를 포함하여 개체를 만드는 방법을 소개했습니다. 그러나 위의 모든 코드 조각에 주의를 기울이면 속성과 메서드가 개체 정의 자체에 하드코딩되어 있음을 알 수 있습니다. 하지만 객체 생성에 대해 더 많은 제어가 필요한 경우 어떻게 해야 합니까? 예를 들어 특정 매개변수를 기반으로 개체의 속성 값을 계산해야 할 수도 있습니다. 또는 런타임에만 사용할 수 있는 값으로 객체의 속성을 초기화해야 할 수도 있습니다. 또한 객체의 여러 인스턴스를 생성해야 할 수도 있습니다(이 요구 사항은 매우 일반적입니다).
C#에서는 클래스를 사용하여 객체 인스턴스를 인스턴스화합니다. 하지만 JavaScript에는 클래스가 없기 때문에 다릅니다. 다음 섹션에서 볼 수 있듯이 이 상황을 활용할 수 있습니다. 함수는 "new" 연산자와 함께 사용될 때 생성자 역할을 합니다.
클래스 대신 생성자
앞서 언급했듯이 JavaScript OOP의 가장 이상한 점은 C#이나 C와 달리 JavaScript에는 클래스가 없다는 것입니다. C#에서 다음과 같이 하면
Dog spot = new Dog()
는 Dog 클래스의 인스턴스인 개체를 반환합니다. 하지만 JavaScript에는 클래스가 없습니다. 클래스에 접근하는 가장 가까운 접근 방식은 다음과 같이 생성자를 정의하는 것입니다.
코드
코드 복사 코드는 다음과 같습니다. :

function DogConstructor(name) {
this.name = name;
this.respondTo = function(name) {
if(this.name == name) {
alert(“Woof”);
}
};
}
var spot = new DogConstructor(“Spot”)
spot.respondTo(“Rover”); / 아니요
spot.respondTo(“Spot”); // 예!

그럼 결과는 어떻게 될까요? 지금은 DogConstructor 함수 정의를 무시하고 다음 줄을 살펴보세요.
var spot = new DogConstructor("Spot")
"new" 연산자가 수행하는 작업은 간단합니다. 먼저, 새로운 빈 객체를 생성합니다. 그런 다음 바로 뒤에 오는 함수 호출이 실행되어 새 빈 객체를 해당 함수의 "this" 값으로 설정합니다. 즉, "new" 연산자를 포함하는 위의 코드 줄은 다음 두 줄의 코드와 동일하다고 볼 수 있습니다:
코드 복사 코드는 다음과 같습니다.

// 빈 객체 생성
var spot = {}
// 빈 객체의 메서드로 함수 호출
DogConstructor.call(spot, "Spot");

DogConstructor 본문에서 볼 수 있듯이 이 함수를 호출하면 객체가 초기화되고 키워드 "this"는 이 객체를 참조합니다. 부르다. 이 방법으로 개체에 대한 템플릿을 만들 수 있습니다! 유사한 개체를 만들어야 할 때마다 생성자를 사용하여 "new"를 호출할 수 있으며 반환된 결과는 완전히 초기화된 개체가 됩니다. 이것은 수업과 매우 비슷하지 않나요? 실제로 JavaScript의 생성자 이름은 일반적으로 시뮬레이션되는 클래스의 이름이므로 위 예에서 생성자 Dog의 이름을 직접 지정할 수 있습니다.
Code
코드 복사 코드는 다음과 같습니다.

// Dog 클래스
function Dog(name) {
/ / 인스턴스 변수
this.name = name;
// 인스턴스 메소드? 흠...
this.respondTo = function(name) {
if(this.name == name) {
alert( "워프");
}
};
}
var spot = new Dog("스팟");
위의 Dog 정의에서는 name이라는 인스턴스 변수를 정의했습니다. Dog를 생성자로 사용하여 생성된 각 개체에는 인스턴스 변수 이름의 자체 복사본이 있습니다(앞서 언급한 대로 개체 사전 항목임). 이것이 원하는 결과입니다. 결국 각 객체에는 상태를 나타내기 위해 자체 인스턴스 변수 복사본이 필요합니다. 그러나 다음 줄을 보면 각 Dog 인스턴스에는 responseTo 메서드의 자체 복사본이 있다는 것을 알 수 있습니다. 이는 Dog 인스턴스 간에 공유할 수 있는 responseTo 인스턴스가 하나만 필요하다는 것입니다. 이 문제는 다음과 같이 Dog 외부에 responseTo를 정의하여 방지할 수 있습니다.
코드 복사 코드는 다음과 같습니다.

function responseTo() {
// responseTo 정의
}
function Dog(name) {
this.name = name
// 이 함수를
this.respondTo = responseTo;
}

이런 방식으로 모든 Dog 인스턴스(즉, 생성자 Dog로 생성된 모든 인스턴스)는 responseTo 메소드. 그러나 방법의 수가 증가함에 따라 유지 관리가 점점 더 어려워집니다. 마지막으로, 코드 베이스에는 많은 전역 함수가 있을 것이며 더 많은 "클래스"를 추가할수록 상황은 더욱 악화될 것입니다(특히 메소드의 이름이 비슷한 경우). 하지만 이 문제는 다음 섹션의 주제인 프로토타입 객체를 사용하여 더 잘 해결할 수 있습니다.
프로토타입
자바스크립트를 이용한 객체지향 프로그래밍에서 프로토타입 객체는 핵심 개념입니다. 이 이름은 JavaScript의 개체가 기존 인스턴스(예: 프로토타입) 개체의 복사본으로 생성된다는 개념에서 유래되었습니다. 이 프로토타입 객체의 모든 속성과 메서드는 프로토타입 생성자에서 생성된 객체의 속성과 메서드로 나타납니다. 이러한 객체는 프로토타입에서 속성과 메서드를 상속한다고 말할 수 있습니다. 다음과 같이 새 Dog 개체를 생성하는 경우:
var buddy = new Dog("Buddy");
buddy가 참조하는 개체는 프로토타입에서 속성과 메서드를 상속하지만 이는 다음 줄에서만 가능합니다. 프로토타입이 어디서 왔는지 확실히 알 수 없습니다. 객체 buddy의 프로토타입은 생성자의 속성(이 경우 Dog 함수)에서 나옵니다.
JavaScript에서 모든 함수에는 프로토타입 객체를 참조하는 데 사용되는 "프로토타입"이라는 속성이 있습니다. 이 프로토타입 객체에는 함수 자체를 참조하는 "생성자"라는 속성이 있습니다. 이는 순환 참조이며 그림 3은 이 순환 관계를 더 잘 보여줍니다.

그림 3 각 함수의 프로토타입에는 생성자 속성이 있습니다.
이제 "new" 연산자를 통해 함수(위 예에서는 Dog)가 포함된 개체를 생성하면 결과 개체는 Dog.prototype의 속성을 상속하게 됩니다. 그림 3에서는 Dog.prototype 객체에 Dog 함수를 다시 참조하는 생성자 속성이 있는 것을 볼 수 있습니다. 이런 방식으로 모든 Dog 객체(Dog.prototype에서 상속됨)에는 Dog 함수를 다시 참조하는 생성자 속성이 있습니다. 그림 4의 코드가 이를 확인합니다. 그림 5는 생성자, 프로토타입 객체, 이들로 생성된 객체 간의 관계를 보여줍니다.
코드 복사 코드는 다음과 같습니다.

var spot = new Dog("Spot" );
// Dog.prototype은 스팟
alert(Dog.prototype.isPrototypeOf(spot))의 프로토타입입니다.
// 스팟은 Dog.prototype에서 생성자 속성을 상속합니다.
🎜>alert (spot.constructor == Dog.prototype.constructor);
alert(spot.constructor == Dog);
// 그러나 생성자 속성은
//에 속하지 않습니다. 아래 줄은 "false"를 표시합니다
alert(spot.hasOwnProperty("constructor"));
// 생성자 속성은 Dog.prototype에 속합니다
// 아래 줄은 "true"를 표시합니다
alert (Dog.prototype.hasOwnProperty(“생성자”))



그림 5 인스턴스는 프로토타입을 상속합니다.
일부 독자는 그림 4에서 hasOwnProperty 및 isPrototypeOf 메서드에 대한 호출을 알아차렸을 것입니다. 이러한 방법은 어디에서 왔습니까? 그들은 Dog.prototype에서 온 것이 아닙니다. 실제로 Dog.prototype 및 Dog 인스턴스에는 toString, toLocaleString 및 valueOf라는 다른 메서드가 있지만 그 중 어느 것도 Dog.prototype에서 나오지 않습니다. .NET Framework의 System.Object가 모든 클래스의 궁극적인 기본 클래스 역할을 하는 것처럼 JavaScript의 Object.prototype은 모든 프로토타입의 궁극적인 기본 프로토타입입니다. (Object.prototype의 프로토타입은 null입니다.)
이 예에서는 Dog.prototype이 객체라는 점을 기억하세요. 이는 Object 생성자를 호출하여 생성됩니다(비록 보이지는 않지만).
Dog.prototype = new Object();


따라서 Dog 인스턴스가 Dog.prototype을 상속하는 것처럼 Dog. Object.prototype에서 상속됩니다. 이로 인해 모든 Dog 인스턴스가 Object.prototype의 메서드와 속성도 상속하게 됩니다.
모든 JavaScript 개체는 프로토타입 체인을 상속하며 모든 프로토타입은 Object.prototype에서 종료됩니다. 지금까지 본 상속은 활성 개체 사이에 있다는 점에 유의하세요. 이는 클래스가 선언될 때 클래스 간의 상속을 의미하는 일반적인 상속 개념과 다릅니다. 따라서 JavaScript 상속은 더욱 동적입니다. 이는 다음과 같은 간단한 알고리즘을 사용하여 달성됩니다. 객체의 속성/메서드에 액세스하려고 하면 JavaScript는 속성/메서드가 해당 객체에 정의되어 있는지 확인합니다. 그렇지 않은 경우 개체의 프로토타입을 확인합니다. 그렇지 않은 경우 객체 프로토타입의 프로토타입이 검사되고 Object.prototype까지 계속됩니다. 그림 6은 이 구문 분석 프로세스를 보여줍니다.

그림 6 프로토타입 체인의 toString() 메서드 구문 분석(더 크게 보려면 이미지를 클릭하세요.)
JavaScript가 속성 액세스 및 메서드 호출을 동적으로 구문 분석하는 방식은 몇 가지 특수 효과를 생성합니다.
프로토타입에 대한 변경 사항은 해당 객체가 생성된 후에도 프로토타입 객체에서 상속된 객체에 즉시 나타납니다.
속성/메서드 X가 객체에 정의된 경우 동일한 이름의 속성/메서드는 객체의 프로토타입에서 숨겨집니다. 예를 들어 Dog.prototype에서 toString 메서드를 정의하여 Object.prototype의 toString 메서드를 재정의할 수 있습니다.
변경 사항은 프로토타입에서 파생 객체까지 한 방향으로만 전파되고 반대 방향으로는 전파되지 않습니다.
그림 7은 이러한 효과를 보여줍니다. 그림 7에서는 이전에 발생한 원치 않는 메서드 인스턴스 문제를 해결하는 방법도 보여줍니다. 프로토타입 내부에 메서드를 배치하면 각 개체에 대해 별도의 함수 개체 인스턴스를 갖지 않고도 개체가 메서드를 공유할 수 있습니다. 이 예에서 rover와 Spot은 toString 메서드가 어떤 방식으로든 Spot에서 재정의될 때까지 getBreed 메서드를 공유합니다. 이후에는 Spot에 자체 버전의 getBreed 메서드가 있지만 새로운 GreatDane으로 생성된 로버 개체와 후속 개체는 GreatDane.prototype 개체에 정의된 getBreed 메서드의 인스턴스를 계속 공유합니다.
프로토타입 상속
코드 복사 코드는 다음과 같습니다.

function GreatDane() { }
var rover = new GreatDane();
var spot = new GreatDane()
GreatDane.prototype.getBreed = function() {
return “그레이트 데인” ”;
};
// 이 시점에서는 작동합니다.
// 로버와 스팟이 이미 생성되었습니다.
alert(rover.getBreed())// GreatDane.prototype의 getBreed()
spot.getBreed = function() {
return “작은 그레이트 데인”
};
alert(spot.getBreed())// 하지만 물론 getBreed
//에 대한 변경 사항은 GreatDane.prototype
// 및 이를 상속받는 다른 객체로 다시 전파되지 않습니다.
// 스팟 객체
경고에서만 발생합니다. (rover.getBreed());


정적 속성 및 메서드
때로는 인스턴스가 아닌 클래스의 속성이나 메서드에 바인딩해야 하는 경우가 있습니다. , 정적 속성 및 메서드. 함수는 필요에 따라 속성과 메서드를 설정할 수 있는 객체이기 때문에 JavaScript에서는 이 작업을 쉽게 수행할 수 있습니다. 생성자는 JavaScript의 클래스를 나타내므로 다음과 같이 생성자에서 설정하여 정적 메서드와 속성을 클래스에 직접 추가할 수 있습니다.

function DateTime() { }
// set static method now()
DateTime.now = function() {
return new Date()
alert(DateTime.now());
JavaScript에서 정적 메서드를 호출하는 구문은 C#의 구문과 거의 동일합니다. 생성자의 이름은 실제로 클래스의 이름이므로 이는 놀라운 일이 아닙니다. 이러한 방식으로 클래스, 공용 속성/메서드, 정적 속성/메서드가 있습니다. 다른 것이 필요합니까? 물론 개인회원도요. 그러나 JavaScript는 기본적으로 비공개 멤버(및 마찬가지로 보호된 멤버)를 지원하지 않습니다. 누구나 객체의 모든 속성과 메소드에 접근할 수 있습니다. 하지만 클래스에 비공개 멤버를 포함시키는 방법이 있지만 그 전에 먼저 클로저를 이해해야 합니다.

클로저
저는 JavaScript를 의식적으로 배운 적이 없습니다. 그것이 없으면 실제 작업을 위한 AJAX 애플리케이션을 작성할 준비가 덜 되었기 때문에 빨리 배워야 했습니다. 처음에는 내 프로그래밍 수준이 몇 단계나 떨어진 것 같은 느낌이 들었습니다. (JavaScript! 내 C 친구들은 뭐라고 말할까요?) 하지만 일단 초기 장애물을 극복하고 나니 JavaScript가 실제로 강력하고 표현력이 풍부하며 매우 간결한 언어라는 것을 알게 되었습니다. 심지어 더 널리 사용되는 다른 언어가 이제 막 지원하기 시작한 기능도 있습니다.
JavaScript의 고급 기능 중 하나는 C# 2.0이 익명 메서드를 통해 지원하는 기능인 클로저 지원입니다. 클로저는 내부 함수(또는 C#의 내부 익명 메서드)가 외부 함수의 지역 변수에 바인딩될 때 발생하는 런타임 현상입니다. 분명히, 이 내부 함수가 어떻게든 외부 함수에 액세스할 수 없다면 별 의미가 없습니다. 예를 들어 이 점을 더 잘 설명할 수 있습니다.
간단한 조건에 따라 일련의 숫자를 필터링해야 한다고 가정해 보겠습니다. 이 조건은 100보다 큰 숫자만 필터를 통과할 수 있고 나머지 숫자는 무시된다는 것입니다. 이를 위해 그림 8과 같은 함수를 작성할 수 있습니다.
술어 기반 요소 필터링
코드 복사 코드는 다음과 같습니다.

function filter(pred, arr) {
var len = arr.length;
varfiltered = []; // new Array()의 단축 버전
// 배열의 요소 ...
for(var i = 0; i < len; i ) {
var val = arr[i]
// 요소가 조건자를 충족하면 통과시킵니다.
if (pred(val)) {
filtered.push(val);
}
}
returnfiltered
}
var someRandomNumbers = [12, 32, 1, 3, 2, 2, 234, 236, 632,7, 8];
var numberGreaterThan100 = filter(
function(x) { return (x > 100) ? true : false; },
someRandomNumbers) ;
// 234, 236, 632 표시
alert(numbersGreaterThan100)

그러나 이제 이번에는 숫자만 가정하고 다른 필터 조건을 만들고 싶습니다. 300보다 크면 필터를 통과할 수 있으며 다음 함수를 작성할 수 있습니다.

코드 복사 코드는 다음과 같습니다. :

var greatThan300 = filter(
function(x) { return (x > 300) ? true : false; },
someRandomNumbers)

그러면 50, 25, 10, 600 등을 초과하는 등으로 필터링해야 할 수도 있지만, 똑똑한 사람이라면 모두 "보다 큼"이라는 동일한 조건자가 있고 숫자만 다르다는 것을 알게 될 것입니다. 따라서 다음과 유사한 기능을 사용하여 개별 번호를 구분할 수 있습니다.
코드 복사 코드는 다음과 같습니다.

function makeGreaterThanPredicate(lowerBound) {
return function(numberToCheck) {
return (numberToCheck > lowerBound) ? true
}

이렇게 하면 다음과 같은 코드를 작성할 수 있습니다.

코드

var greatThan10 = makeGreaterThanPredicate(10);
var greatThan100 = makeGreaterThanPredicate(100);
alert(filter(greaterThan10, someRandomNumbers)); filter(greaterThan100, someRandomNumbers))


makeGreaterThanPredicate 함수가 반환한 내부 익명 함수를 관찰하면 익명 내부 함수가 makeGreaterThanPredicate에 전달된 매개 변수인 lowerBound를 사용하고 있음을 알 수 있습니다. 일반적인 범위 지정 규칙에 따라 makeGreaterThanPredicate가 종료되면 lowerBound가 범위를 벗어납니다. 그러나 여기서 내부 익명 함수는 makeGreaterThanPredicate가 종료된 후에도 오랜 시간이 지나도 여전히 lowerBound를 전달합니다. 이것이 우리가 클로저라고 부르는 것입니다: 내부 함수가 자신이 정의된 환경(즉, 외부 함수의 매개변수 및 지역 변수)을 닫기 때문입니다.
처음에는 클로저의 강력한 효과를 느끼지 못할 수도 있습니다. 그러나 올바르게 적용하면 아이디어를 코드로 변환하는 데 도움이 되는 매우 창의적이고 재미있는 방법이 될 수 있습니다. JavaScript에서 클로저의 가장 흥미로운 용도 중 하나는 클래스의 개인 변수를 시뮬레이션하는 것입니다.

개인 속성 시뮬레이션
이제 클로저가 개인 멤버를 시뮬레이션하는 데 어떻게 도움이 되는지 살펴보겠습니다. 일반적으로 함수 내의 지역 변수는 함수 외부에서 액세스할 수 없습니다. 함수가 종료된 후에는 다양한 실제적인 이유로 지역 변수가 영원히 사라집니다. 그러나 지역 변수가 내부 함수의 클로저에 의해 캡처되면 살아남습니다. 이 사실은 JavaScript 개인 속성을 에뮬레이션하는 데 핵심입니다. Person 클래스가 있다고 가정합니다.
Code
코드 복사 코드는 다음과 같습니다.

function Person(이름, 나이) {
this.getName = function() { return name;
this.setName = function(newName) { name = newName }; = function() { return age;
this.setAge = function(newAge)
}

매개변수 name과 age는 생성자 사람. Person이 반환되면 이름과 나이는 영원히 사라져야 합니다. 그러나 이는 Person 인스턴스의 메서드로 할당된 네 가지 내부 함수에 의해 캡처되므로 이름과 나이는 사실상 계속 존재하지만 이 네 가지 메서드를 통해서만 엄격하게 액세스할 수 있습니다. 따라서 다음을 수행할 수 있습니다.

코드

코드 복사 코드는 다음과 같습니다.
var ray = new Person(“Ray”, 31);
alert(ray.getName())
alert(ray.getAge())
ray.setName(“젊은이” Ray”);
// 즉시 회복!
ray.setAge(22)
alert(ray.getName() “는 이제 “ ray.getAge()
“ 세입니다.”) ;

생성자에서 초기화되지 않은 전용 멤버는 다음과 같이 생성자의 지역 변수가 될 수 있습니다.

코드

코드 복사 코드는 다음과 같습니다.
function Person(이름, 나이) {
var 직업
this.getOccupation = function() { return 직업 };
this.setOccupation = function(newOcc) { 직업 =
newOcc }// 이름 및 나이에 대한 접근자


이는 비공개 멤버이며 C#에서 기대하는 비공개 멤버와 약간 다릅니다. C#에서는 클래스의 공용 메서드가 해당 전용 멤버에 액세스할 수 있습니다. 그러나 JavaScript에서 프라이빗 멤버는 클로저 내에서 해당 멤버를 소유한 메서드를 통해서만 액세스할 수 있습니다(이러한 메서드는 일반 퍼블릭 메서드와 다르기 때문에 권한 있는 메서드라고도 함). 따라서 Person의 공개 메소드에서 비공개 멤버는 여전히 비공개 멤버의 권한 있는 접근자 메소드를 통해 액세스해야 합니다.


코드 복사 코드는 다음과 같습니다. Person.prototype.somePublicMethod = function() {
// 작동하지 않습니다!
// 경고(this.name); // 아래는 작동합니다
alert(this.getName())


Douglas Crockford는 비공개 멤버를 시뮬레이션하기 위해 클로저를 사용하는 기술을 발견한(또는 게시한) 최초의 사람으로 유명합니다. 그의 웹사이트인 javascript.crockford.com에는 JavaScript에 대한 풍부한 정보가 포함되어 있으며 JavaScript에 관심이 있는 모든 개발자가 읽어야 합니다.

클래스에서 상속
지금까지 생성자와 프로토타입 객체를 사용하여 JavaScript에서 클래스를 모의하는 방법을 살펴보았습니다. 프로토타입 체인이 모든 객체가 Object.prototype의 공개 메소드를 갖도록 보장하고 클로저를 사용하여 클래스의 비공개 멤버를 시뮬레이션하는 방법을 확인했습니다. 하지만 여기에는 뭔가가 빠졌습니다. C#에서 매일 수행하는 클래스에서 파생하는 방법을 본 적이 없습니다. 불행하게도 JavaScript의 클래스에서 상속하는 것은 C#에서 상속하기 위해 콜론을 입력하는 것만큼 간단하지 않으며 훨씬 더 많은 작업이 필요합니다. 반면에 JavaScript는 매우 유연하며 클래스에서 상속할 수 있는 다양한 방법을 허용합니다.
예를 들어 그림 9와 같이 Dog라는 파생 클래스가 있는 기본 클래스 Pet이 있습니다. 이것을 JavaScript로 어떻게 구현하나요? 애완동물 수업은 쉽습니다. 구현 방법을 살펴보았습니다.

코드 복사 코드는 다음과 같습니다.

// class Pet
function Pet(name) {
this.getName = function() { return name }
this.setName = function(newName) { name = newName ; };
}
Pet.prototype.toString = function() {
return “이 애완동물의 이름은 다음과 같습니다: “ this.getName()
}// 수업 종료 Pet
var parrotty = new Pet(“Parrotty the Parrot”);
alert(parrotty);

이제 Pet에서 파생된 Dog 클래스를 만드는 방법은 무엇입니까? 그림 9에서 볼 수 있듯이 Dog에는 Pet의 toString 메서드를 재정의하는 breed라는 또 다른 속성이 있습니다. JavaScript 규칙은 C#에서 권장하는 Pascal 대/소문자 대신 Camel 대/소문자를 메서드 및 속성 이름에 사용하는 것입니다. 그림 10에서는 이를 수행하는 방법을 보여줍니다.

Pet 클래스에서 파생

코드 복사 코드는 다음과 같습니다.
// 클래스 Dog : Pet
// public Dog(문자열 이름, 문자열 품종)
function Dog(이름, 품종) {
// think Dog : 기본(이름)
Pet .call(this, name);
this.getBreed = function() { return breed;
// 품종은 변경되지 않습니다.
// this.setBreed = function(newBreed) { name = newName };
}
// 이렇게 하면 Dog.prototype이 Pet.prototype에서
// 상속됩니다.
Dog.prototype = new Pet(); // Pet .prototype.constructor
//가 Pet을 가리킨다는 점을 기억하세요. 우리는 Dog 인스턴스의
// 생성자가 Dog를 가리키기를 원합니다.
Dog.prototype.constructor = Dog; / 이제 Pet .prototype.toString
Dog.prototype.toString = function() {
return "이 개 이름은 " this.getName()
"이고 품종은 " this입니다. .getBreed();
};
// Dog 클래스 종료
var dog = new Dog(“Buddy”, “Great Dane”)
// 새 toString()
alert(dog );
// testofinstanceof(is 연산자와 유사)
//(dog is Dog)? yes
alert(doginstanceofDog)//(dog) is Pet)? yes
alert(dog instanceof Pet);// (dog is Object)? yes
alert(dog instanceof Object)


사용된 프로토타입 기술이 올바르게 설정됨 프로토타입 체인이 포함되어 있으므로 C#을 사용하는 경우 테스트된 인스턴스가 예상대로 실행됩니다. 또한 권한 있는 메서드는 여전히 예상대로 작동합니다.

모의 네임스페이스
C 및 C#에서는 이름 충돌을 최소화하기 위해 네임스페이스를 사용합니다. 예를 들어 .NET Framework에서 네임스페이스는 Microsoft.Build.Task.Message 클래스를 System.Messaging.Message와 구별하는 데 도움이 됩니다. JavaScript에는 네임스페이스를 지원하는 언어별 기능이 없지만 개체를 ​​사용하여 네임스페이스를 쉽게 에뮬레이트할 수 있습니다. JavaScript 라이브러리를 생성하려면 다음과 같이 전역 함수 및 클래스를 정의하지 않고 네임스페이스 내에 래핑할 수 있습니다.




코드 복사
코드는 다음과 같습니다. var MSDNMagNS = {}; MSDNMagNS.Pet = function(name) { // 코드는 MSDNMagNS.Pet.prototype 입니다. toString = function() { // code };
var pet = new MSDNMagNS.Pet("Yammer")


네임스페이스 수준은 고유하지 않을 수 있습니다. 생성된 중첩 네임스페이스:

코드



코드 복사
코드는 다음과 같습니다.

var MSDNMagNS = {};
// 중첩된 네임스페이스 “Examples”
MSDNMagNS.Examples = {}
MSDNMagNS.Examples.Pet = function(name) { // 코드 } ;
MSDNMagNS.Examples.Pet.prototype.toString = function() { // 코드 }
var pet = new MSDNMagNS.Examples.Pet(“Yammer”)

상상할 수 있듯이 이렇게 길고 중첩된 네임스페이스를 입력하는 것은 피곤할 수 있습니다. 다행히 라이브러리 사용자는 네임스페이스에 대해 더 짧은 별칭을 쉽게 지정할 수 있습니다.
코드 복사 코드는 다음과 같습니다.

// MSDNMagNS.Examples 및 Pet 정의...
// "Eg = MSDNMagNS.Examples; 사용"이라고 생각하세요.
var Eg = MSDNMagNS.Examples
var pet = new Eg.Pet (“Yammer”);
alert(pet);

Microsoft AJAX 라이브러리의 소스 코드를 보면 라이브러리 작성자가 다음과 유사한 기술을 사용한 것을 알 수 있습니다. 명명 공간을 구현합니다(정적 메서드 Type.registerNamespace의 구현 참조). 자세한 내용은 추가 기사 "OOP 및 ASP.NET AJAX"를 확인하세요.

JavaScript는 이렇게 작성해야 하나요?
JavaScript가 객체 지향 프로그래밍을 매우 잘 지원한다는 것을 확인하셨습니다. 프로토타입 기반 언어이기는 하지만 널리 사용되는 다른 언어에서 흔히 볼 수 있는 클래스 기반 프로그래밍 스타일을 수용할 수 있는 유연성과 성능을 갖추고 있습니다. 하지만 문제는 이렇습니다. JavaScript 코드를 이런 식으로 작성해야 할까요? JavaScript로 프로그래밍하는 방식은 C#이나 C로 코딩하는 방식과 동일해야 할까요? JavaScript에서 찾을 수 없는 기능을 에뮬레이트하는 더 스마트한 방법이 있습니까? 모든 프로그래밍 언어는 다르며, 한 언어에 가장 적합한 것이 다른 언어에는 최선이 아닐 수도 있습니다.
JavaScript에서는 클래스가 클래스를 상속하는 것과는 달리 객체가 객체를 상속하는 것을 본 적이 있습니다. 따라서 정적 상속 계층 구조를 사용하여 많은 클래스를 빌드하는 것은 JavaScript에 적합하지 않을 수 있습니다. 아마도 Douglas Crockford가 자신의 기사 Prototypal Inheritance in JavaScript에서 말했듯이 JavaScript로 프로그래밍하는 방법은 프로토타입 개체를 만들고 다음과 같은 간단한 개체 함수를 사용하여 원래 개체를 상속하는 새 개체를 만드는 것입니다.
코드 복사 코드는 다음과 같습니다.

function object(o) {
function F() { }
F.prototype = o;
return new F();
}

그런 다음 JavaScript의 객체는 가변적이므로 , 필요에 따라 새 필드와 새 메서드를 사용하여 개체를 만듭니다.
정말 좋은 일이지만 전 세계 대부분의 개발자가 클래스 기반 프로그래밍에 더 익숙하다는 사실은 부인할 수 없습니다. 실제로 클래스 기반 프로그래밍도 여기에 나타납니다. JavaScript 2.0은 ECMA-262 사양(ECMA-262가 JavaScript의 공식 사양) 버전 4에 따라 실제 클래스를 갖게 됩니다. 결과적으로 JavaScript는 클래스 기반 언어로 진화하고 있습니다. 그러나 JavaScript 2.0이 널리 사용되기까지는 몇 년이 걸릴 것입니다. 동시에 현재 JavaScript가 프로토타입 기반 스타일과 클래스 기반 스타일로 JavaScript 코드를 완전히 읽고 쓸 수 있다는 점을 분명히 해야 합니다.

미래 전망
대화형 씩 클라이언트 AJAX 애플리케이션이 널리 사용되면서 JavaScript는 .NET 개발자에게 가장 중요한 도구 중 하나로 빠르게 자리잡고 있습니다. 그러나 프로토타입 특성은 처음에는 C, C# 또는 Visual Basic과 같은 언어에 더 익숙한 개발자를 놀라게 할 수 있습니다. 나는 JavaScript를 배운 경험이 비록 약간의 좌절감은 있었지만 풍부한 경험이었다는 것을 알았습니다. 이 기사를 통해 여러분의 경험이 조금 더 원활해졌으면 좋겠습니다. 그것이 바로 제가 목표로 하는 것이기 때문입니다.
성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.