[javascript] JavaScript에서 ‘prototype’과 ‘this’의 사용?

차이점은 무엇입니까

var A = function () {
    this.x = function () {
        //do something
    };
};

var A = function () { };
A.prototype.x = function () {
    //do something
};



답변

예제의 결과는 매우 다릅니다.

차이점을 살펴보기 전에 다음 사항에 유의해야합니다.

  • 생성자의 프로토 타입 은 인스턴스의 private [[Prototype]]속성을 통해 인스턴스간에 메서드와 값을 공유하는 방법을 제공 합니다.
  • 함수입니다 이이 함수가 호출되는 방법 또는를 사용하여 설정 바인드 (여기서 논의하지 않음). 함수는 객체 (예를 들면 호출되는 경우 myObj.method()그 다음) 있어서 참조 내의 개체. 어디 이이 호출 또는 사용으로 설정되지 않습니다 바인딩 , 그것은 (브라우저의 창) 전역 객체에 대한 기본값 또는 엄격 모드는 정의되지 않은 상태로 유지됩니다.
  • JavaScript는 객체 지향 언어입니다. 즉, 대부분의 값은 함수를 포함한 객체입니다. (문자열, 숫자 및 부울은 객체 가 아닙니다 .)

문제의 스 니펫은 다음과 같습니다.

var A = function () {
    this.x = function () {
        //do something
    };
};

이 경우 변수 A에는 함수에 대한 참조 인 값이 할당됩니다. 이 기능을 사용하여 호출 할 때 A(), 함수의 이이 그것 전역 객체에 대한 기본값과 표현하도록 호출에 의해 설정되지 않은 this.x효과가있다 window.x. 결과적으로 오른쪽의 함수 표현식에 대한 참조가에 지정됩니다 window.x.

다음의 경우 :

var A = function () { };
A.prototype.x = function () {
    //do something
};

매우 다른 일이 일어납니다. 첫 번째 줄에서 변수 A에는 함수에 대한 참조가 할당됩니다. JavaScript에서 모든 함수 객체에는 기본적으로 프로토 타입 속성이 있으므로 A.prototype 객체 를 만드는 별도의 코드가 없습니다 .

두 번째 줄에서 A.prototype.x 에는 함수에 대한 참조가 할당됩니다. x 속성이 없으면 새 속성 을 만들 거나 새 속성이 있으면 새 값을 할당합니다. 따라서 객체의 x 속성이 표현식에 포함 된 첫 번째 예제와의 차이점입니다 .

다른 예는 다음과 같습니다. 그것은 첫 번째 것과 비슷합니다 (아마도 당신이 묻고 싶었던 것) :

var A = new function () {
    this.x = function () {
        //do something
    };
};

이 예제에서는 new함수가 생성자로 호출되도록 연산자가 함수 표현식 앞에 추가되었습니다. 호출 할 때 new함수이다 이것은 누구의 개인 새로운 객체 참조로 설정되어 [[Prototype]]특성 생성자의 공용 참조하도록 설정되어 프로토 타입을 . 대 입문 x에서이 새 객체에 속성이 생성됩니다. 생성자로 호출되면 함수는 기본적 으로이 객체를 반환 하므로 별도의 return this;명령문이 필요하지 않습니다 .

Ax 속성 이 있는지 확인하려면

console.log(A.x) // function () {
                 //   //do something
                 // };

생성자를 참조하는 유일한 방법은 A.constructor통하는 것이므로 new를 일반적으로 사용하지 않습니다 . 수행하는 것이 훨씬 일반적입니다.

var A = function () {
    this.x = function () {
        //do something
    };
};
var a = new A();

비슷한 결과를 얻는 또 다른 방법은 즉시 호출 된 함수 표현식을 사용하는 것입니다.

var A = (function () {
    this.x = function () {
        //do something
    };
}());

이 경우 A오른쪽에서 함수 호출의 반환 값을 할당했습니다. 때문에 여기서 다시, 이것은 호출에 설정되어 있지 않은, 그것은 전역 객체를 참조하는 것 this.x효과적입니다 window.x. 이 함수는 아무것도 반환하지 않으므로 A값은 undefined입니다.

Javascript 객체를 JSON으로 직렬화하거나 직렬화 해제하는 경우 두 접근 방식 간의 이러한 차이점도 나타납니다. 오브젝트의 프로토 타입에 정의 된 메소드는 오브젝트를 직렬화 할 때 직렬화되지 않습니다. 예를 들어 오브젝트의 데이터 부분 만 직렬화하려고하지만 메소드는 아닙니다.

var A = function () {
    this.objectsOwnProperties = "are serialized";
};
A.prototype.prototypeProperties = "are NOT serialized";
var instance = new A();
console.log(instance.prototypeProperties); // "are NOT serialized"
console.log(JSON.stringify(instance));
// {"objectsOwnProperties":"are serialized"} 

관련 질문 :

참고 : 두 접근 방식간에 메모리를 크게 절약 할 수는 없지만 프로토 타입을 사용하여 메소드와 속성을 공유하면 각 사본이있는 각 인스턴스보다 적은 메모리를 사용하게됩니다.

JavaScript는 저수준 언어가 아닙니다. 프로토 타입 또는 다른 상속 패턴을 메모리 할당 방식을 명시 적으로 변경하는 방법으로 생각하는 것은 그리 가치가 없을 수 있습니다.


답변

다른 사람들이 첫 번째 버전을 말했듯이, “this”를 사용하면 클래스 A의 모든 인스턴스에 독자적인 함수 메소드 “x”의 사본이 있습니다. “prototype”을 사용한다는 것은 클래스 A의 각 인스턴스가 동일한 방법 “x”의 사본을 사용한다는 것을 의미합니다.

이 미묘한 차이를 보여주는 코드는 다음과 같습니다.

// x is a method assigned to the object using "this"
var A = function () {
    this.x = function () { alert('A'); };
};
A.prototype.updateX = function( value ) {
    this.x = function() { alert( value ); }
};

var a1 = new A();
var a2 = new A();
a1.x();  // Displays 'A'
a2.x();  // Also displays 'A'
a1.updateX('Z');
a1.x();  // Displays 'Z'
a2.x();  // Still displays 'A'

// Here x is a method assigned to the object using "prototype"
var B = function () { };
B.prototype.x = function () { alert('B'); };

B.prototype.updateX = function( value ) {
    B.prototype.x = function() { alert( value ); }
}

var b1 = new B();
var b2 = new B();
b1.x();  // Displays 'B'
b2.x();  // Also displays 'B'
b1.updateX('Y');
b1.x();  // Displays 'Y'
b2.x();  // Also displays 'Y' because by using prototype we have changed it for all instances

다른 사람들이 언급했듯이 한 가지 방법을 선택해야하는 여러 가지 이유가 있습니다. 내 샘플은 차이점을 명확하게 보여주기위한 것입니다.


답변

이 두 가지 예를 보자.

var A = function() { this.hey = function() { alert('from A') } };

vs.

var A = function() {}
A.prototype.hey = function() { alert('from prototype') };

여기에있는 대부분의 사람들 (특히 최고 답변)은 WHY를 설명하지 않고 어떻게 다른지 설명하려고했습니다. 나는 이것이 잘못되었다고 생각하고 기초를 먼저 이해하면 그 차이가 분명해질 것입니다. 기본 사항을 먼저 설명해 봅시다 …

a) 함수는 JavaScript의 객체입니다. 자바 스크립트의 모든 객체 (의미, 당신은, 다른 속성처럼 액세스 크롬 등의 브라우저에서 어쩌면 제외시켰다 없습니다) 내부 속성을 가져옵니다 종종라고 __proto__(실제로 입력 할 수 있습니다 anyObject.__proto__무엇을 참조를 볼 크롬에.이 단지이다 , 속성, 그 밖의 것 없음 JavaScript의 속성 = 객체 안의 변수, 그 밖의 것 없음 변수는 무엇을 하는가?

__proto__속성 은 무엇을 가리 킵니까? 글쎄, 일반적으로 다른 객체입니다 (나중에 이유를 설명하겠습니다). __proto__속성의 JavaScript 가 다른 객체를 가리 키지 않도록 하는 유일한 방법 은을 사용하는 것 var newObj = Object.create(null)입니다. 이렇게해도 __proto__STILL 속성은 객체의 속성으로 존재하며 다른 객체를 가리 키지 않고를 가리 킵니다 null.

대부분의 사람들이 혼란스러워하는 부분은 다음과 같습니다.

JavaScript에서 새 함수 (객체이기도 함을 기억 하는가?)를 작성할 때, 정의 된 순간 JavaScript는 해당 함수에이라는 새 특성을 자동으로 작성합니다 prototype. 시도 해봐:

var A = [];
A.prototype // undefined
A = function() {}
A.prototype // {} // got created when function() {} was defined

A.prototype__proto__속성과 완전히 다릅니다 . 이 예에서 ‘A’에는 이제 ‘prototype’및이라는 두 가지 속성이 __proto__있습니다. 이것은 사람들에게 큰 혼란입니다. prototype__proto__특성과 관련된 어떠한 방식으로, 그들은 별도의 값을 가리키는 별도의 일을 것입니다.

궁금 할 수도 있습니다. 왜 __proto__모든 객체에 JavaScript가 속성을 생성합니까? 한마디 : 위임 . 객체에서 속성을 호출 할 때 객체에 속성이없는 경우 JavaScript는 참조 된 객체를 찾아 해당 객체 __proto__가 있는지 확인합니다. 그것이 없다면, 그 객체의 __proto__속성 등을보고 … 체인이 끝날 때까지. 따라서 이름 프로토 타입 체인 입니다. 물론 __proto__객체를 가리 키지 않고 대신 null운이 좋은 운임을 가리키면 JavaScript가이를 인식 undefined하고 속성을 반환합니다 .

prototype함수를 정의 할 때 JavaScript가 함수에 대한 속성을 작성하는 이유가 궁금 할 수도 있습니다. 그것은 당신을 속이려고하기 때문에, 클래스 기반 언어처럼 작동한다는 것을 속이십시오 .

예제로 넘어 가서 “object”를 만들어 봅시다 A:

var a1 = new A();

이 일이 발생했을 때 백그라운드에서 무언가가 발생했습니다. a1새로운 빈 객체에 할당 된 일반 변수입니다.

new함수 호출 전에 연산자를 사용했다는 사실 A()은 백그라운드에서 추가 작업을 수행했습니다. new키워드는 이제 참조를 새로운 객체를 생성 a1하고 그 객체가 비어 있습니다. 추가로 일어나는 일은 다음과 같습니다.

우리는 각 함수 정의에 prototype( __proto__프로퍼티 와 달리 액세스 할 수 있는) 새로운 프로퍼티가 생성되었다고 말했 습니까? 그 속성은 현재 사용되고 있습니다.

이제 우리는 갓 구운 빈 a1물건 이있는 지점에 있습니다 . 우리는 JavaScript의 모든 객체 가 null이든 다른 객체이든 __proto__무언가를 가리키는 내부 속성을 가지고 있다고 말했습니다 a1. 무엇 new운영자가하는 것은 그 설정이다 __proto__함수의에 지점 속성을 prototype속성입니다. 다시 읽어보세요. 기본적으로 이것입니다 :

a1.__proto__ = A.prototype;

우리는 A.prototype그것을 정의하기 전에 다른 것으로 변경하지 않는 한 빈 객체에 지나지 않는다고 말했습니다 a1. 이제는 기본적으로 a1.__proto__같은 A.prototype빈 객체를 가리 킵니다. 그들은이 줄이 발생할 때 만들어진 동일한 객체를 가리 킵니다.

A = function() {} // JS: cool. let's also create A.prototype pointing to empty {}

이제 var a1 = new A()진술이 처리 될 때 또 다른 일이 발생 합니다. 기본적 A()으로 실행되고 A가 다음과 같은 경우 :

var A = function() { this.hey = function() { alert('from A') } };

내부의 모든 것들이 function() { }실행될 것입니다. this.hey..라인에 도달하면 this로 변경되고 다음 a1과 같은 결과가 나타납니다.

a1.hey = function() { alert('from A') }

this변경 이유 를 다루지는a1 않지만 자세한 내용 은이 답변 을 참조하십시오.

요약 var a1 = new A()하면 백그라운드에서 3 가지 일이 발생합니다.

  1. 완전히 새로운 빈 객체가 생성되어에 할당됩니다 a1.a1 = {}
  2. a1.__proto__속성은 A.prototype(또 다른 빈 객체 {}) 를 가리키는 것과 동일한 것을 가리 키도록 할당됩니다.

  3. 1 단계에서 생성 된 새로운 빈 객체 A()this설정하여 함수 를 실행 중입니다 (위에서 내가 왜 this변경 했는지에 대한 대답을 읽으십시오 a1)

이제 다른 객체를 만들어 봅시다 :

var a2 = new A();

1,2,3 단계가 반복됩니다. 당신은 뭔가를 알아? 핵심 단어는 반복입니다. 1 단계 : a2새로운 빈 객체가 될 것입니다 .2 단계 : __proto__속성이 같은 것을 A.prototype가리키고 가장 중요하게 A()는 3 단계 : 함수 가 다시 실행 a2됩니다. 즉, 함수 가 hey포함 된 속성을 얻습니다 . a1a2라는 두 개의 별도의 속성이 hey이 개 별도의 기능을 가리! 우리는 이제 같은 일을하는 동일한 두 개의 다른 객체에 중복 함수를 new A가지고 있습니다. 우리는 이것을 어떻게 방지합니까?

__proto__모든 개체에 속성이 존재 하는지 기억 하십니까? yoMan속성 을 검색하면 a1(존재하지 않는) __proto__속성이 참조되며, 속성이 객체 (대부분의 경우) 인 경우 속성이 포함되어 있는지 확인 yoMan하고 그렇지 않은 경우 해당 객체 __proto__등 을 참조 할 것입니다 . 그렇다면 해당 속성 값을 가져 와서 표시합니다.

그래서 누군가 가이 사실 +을 만들 때 a1__proto__속성이 동일한 (빈) 객체를 A.prototype가리키고 이것을 수행한다는 사실을 사용하기로 결정했습니다 .

var A = function() {}
A.prototype.hey = function() { alert('from prototype') };

멋있는! 이제 생성 할 때 a1위의 3 단계를 모두 거치고 3 단계에서는 아무것도 수행하지 않으므로 아무것도하지 않습니다 function A(). 그리고 우리가 할 경우 :

a1.hey

그것은 그 볼 a1이 포함되어 있지 않습니다 hey및 그것 확인합니다 __proto__그것이 어떤 경우인지, 그것을 가지고 있는지 확인하기 위해 속성 개체를.

이 방법을 사용하면 3 단계에서 각각의 새 객체 생성시 함수가 복제되는 부분을 제거합니다. 대신에 a1a2별도 가지는 hey속성을, 지금 그들 중 누구도 그것을이 없습니다. 당신이 지금까지 알아 낸 것 같아요. 좋은 점입니다. 이해 __proto__하고 Function.prototype있다면 이와 같은 질문은 매우 분명합니다.

참고 : 일부 사람들은 내부 프로토 타입 속성을로 부르지 않는 경향 __proto__이 있습니다.이 게시물을 통해이 이름을 사용 Functional.prototype하여 두 가지 다른 속성으로 속성을 명확하게 구분했습니다 .


답변

대부분의 경우 기본적으로 동일하지만 두 번째 버전은 각 객체에 대해 별도의 함수 대신 하나의 함수 인스턴스 만 있기 때문에 메모리를 절약합니다.

첫 번째 양식을 사용하는 이유는 “개인 구성원”에 액세스하기위한 것입니다. 예를 들면 다음과 같습니다.

var A = function () {
    var private_var = ...;

    this.x = function () {
        return private_var;
    };

    this.setX = function (new_x) {
        private_var = new_x;
    };
};

javascript의 범위 지정 규칙으로 인해 private_var는 this.x에 지정된 함수에 사용할 수 있지만 객체 외부에서는 사용할 수 없습니다.


답변

첫 번째 예는 해당 객체의 인터페이스 만 변경합니다. 두 번째 예는 해당 클래스의 모든 객체에 대한 인터페이스를 변경합니다.


답변

this대신에 사용 하는 데있어 궁극적 인 문제 prototype는 메소드를 대체 할 때 기본 클래스의 생성자가 여전히 대체 된 메소드를 참조한다는 것입니다. 이걸 고려하세요:

BaseClass = function() {
    var text = null;

    this.setText = function(value) {
        text = value + " BaseClass!";
    };

    this.getText = function() {
        return text;
    };

    this.setText("Hello"); // This always calls BaseClass.setText()
};

SubClass = function() {
    // setText is not overridden yet,
    // so the constructor calls the superclass' method
    BaseClass.call(this);

    // Keeping a reference to the superclass' method
    var super_setText = this.setText;
    // Overriding
    this.setText = function(value) {
        super_setText.call(this, "SubClass says: " + value);
    };
};
SubClass.prototype = new BaseClass();

var subClass = new SubClass();
console.log(subClass.getText()); // Hello BaseClass!

subClass.setText("Hello"); // setText is already overridden
console.log(subClass.getText()); // SubClass says: Hello BaseClass!

대:

BaseClass = function() {
    this.setText("Hello"); // This calls the overridden method
};

BaseClass.prototype.setText = function(value) {
    this.text = value + " BaseClass!";
};

BaseClass.prototype.getText = function() {
    return this.text;
};

SubClass = function() {
    // setText is already overridden, so this works as expected
    BaseClass.call(this);
};
SubClass.prototype = new BaseClass();

SubClass.prototype.setText = function(value) {
    BaseClass.prototype.setText.call(this, "SubClass says: " + value);
};

var subClass = new SubClass();
console.log(subClass.getText()); // SubClass says: Hello BaseClass!

이것이 문제가 아니라고 생각한다면, 그것은 사적 변수없이 살 수 있는지, 그리고 누수를 볼 때 누수를 알기에 충분한 경험이 있는지에 달려 있습니다. 또한 메소드 정의 후에 생성자 논리를 넣어야하는 것이 불편합니다.

var A = function (param1) {
    var privateVar = null; // Private variable

    // Calling this.setPrivateVar(param1) here would be an error

    this.setPrivateVar = function (value) {
        privateVar = value;
        console.log("setPrivateVar value set to: " + value);

        // param1 is still here, possible memory leak
        console.log("setPrivateVar has param1: " + param1);
    };

    // The constructor logic starts here possibly after
    // many lines of code that define methods

    this.setPrivateVar(param1); // This is valid
};

var a = new A(0);
// setPrivateVar value set to: 0
// setPrivateVar has param1: 0

a.setPrivateVar(1);
//setPrivateVar value set to: 1
//setPrivateVar has param1: 0

대:

var A = function (param1) {
    this.setPublicVar(param1); // This is valid
};
A.prototype.setPublicVar = function (value) {
    this.publicVar = value; // No private variable
};

var a = new A(0);
a.setPublicVar(1);
console.log(a.publicVar); // 1


답변

모든 객체는 프로토 타입 객체에 연결됩니다. 존재하지 않는 속성에 액세스하려고하면 JavaScript는 해당 속성에 대한 객체의 프로토 타입 객체를 찾아 해당 속성이있는 경우이를 반환합니다.

prototype함수 생성자 의 속성은를 사용할 때 해당 함수로 생성 된 모든 인스턴스의 프로토 타입 객체를 나타냅니다 new.


첫 번째 예에서는 함수로 x만든 각 인스턴스에 속성 을 추가 A합니다.

var A = function () {
    this.x = function () {
        //do something
    };
};

var a = new A();    // constructor function gets executed
                    // newly created object gets an 'x' property
                    // which is a function
a.x();              // and can be called like this

두 번째 예에서는 모든 인스턴스가 A가리키는 프로토 타입 객체에 속성을 추가합니다 .

var A = function () { };
A.prototype.x = function () {
    //do something
};

var a = new A();    // constructor function gets executed
                    // which does nothing in this example

a.x();              // you are trying to access the 'x' property of an instance of 'A'
                    // which does not exist
                    // so JavaScript looks for that property in the prototype object
                    // that was defined using the 'prototype' property of the constructor

결론적으로, 첫 번째 예 에서는 함수의 사본이 각 인스턴스에 할당됩니다 . 두 번째 예 에서 함수의 단일 사본은 모든 인스턴스에서 공유됩니다 .