자바 스크립트에서 클래스와 정적 메소드


262

나는 이것이 효과가 있다는 것을 안다.

function Foo() {};
Foo.prototype.talk = function () {
    alert('hello~\n');
};

var a = new Foo;
a.talk(); // 'hello~\n'

하지만 전화하고 싶다면

Foo.talk() // this will not work
Foo.prototype.talk() // this works correctly

Foo.talk일할 수있는 방법을 찾았습니다

  1. Foo.__proto__ = Foo.prototype
  2. Foo.talk = Foo.prototype.talk

다른 방법이 있습니까? 나는 그것이 옳은지 모르겠다. JavaScript 코드에서 클래스 메소드 또는 정적 메소드를 사용합니까?


14
Foo.talk = function ...
조기 최적화

1
@downvoterstepintothelight Foo.walk = function() {}프로토 타입 체인에 없으므로 인스턴스에 영향을 미치지 않습니다. 함수를 [[prototype]]가리 키 는 크로스 브라우저 방법 이 prototype있습니까?
lostyzd

3
클래스 메소드는 정의에 따라 인스턴스영향을 미치지 않기 때문에 아마도 내가 원하는 것을 모릅니다 .
조기 최적화

@downvoterstepintothelight 나는 파이썬과 같은 언어로 된 메소드 가 인스턴스가 클래스 메소드를 호출 할 수 있는지, 차이점은 this포인터 라고 의심합니다 .
lostyzd

답변:


410

첫째, 자바 스크립트는 주로 있다는 사실을 프로토 타입 언어 라기보다는, 클래스 기반 언어 1 . Foo클래스가 아니라 함수 인 객체입니다. 키워드를 사용하여 해당 함수 에서 객체 인스턴스화하여 new표준 OOP 언어로 클래스와 비슷한 것을 만들 수 있습니다.

__proto__교차 브라우저 지원이 좋지 않기 때문에 대부분 무시하는 것이 좋습니다. 대신 방법에 대해 배우십시오.prototype 작동 방식 .

함수 2 에서 생성 된 객체의 인스턴스가 있고 어떤 방식 으로든 멤버 중 하나 (메서드, 속성, 속성, 상수 등)에 액세스하는 경우 액세스는 프로토 타입 계층 구조 아래로 (a) 또는 (b) 다른 프로토 타입을 찾지 못했습니다.

계층 구조는 호출 된 객체에서 시작한 다음 프로토 타입 객체를 검색합니다. 프로토 타입 객체에 프로토 타입이 있으면 프로토 타입이 없으면 반복됩니다 undefined.

예를 들면 다음과 같습니다.

foo = {bar: 'baz'};
console.log(foo.bar); // logs "baz"

foo = {};
console.log(foo.bar); // logs undefined

function Foo(){}
Foo.prototype = {bar: 'baz'};
f = new Foo();
console.log(f.bar);
// logs "baz" because the object f doesn't have an attribute "bar"
// so it checks the prototype
f.bar = 'buzz';
console.log( f.bar ); // logs "buzz" because f has an attribute "bar" set

이 "기본"부분을 이미 어느 정도 이해했다고 생각되지만 확실하게하기 위해 명시 적으로 만들어야합니다.

JavaScript에서 모든 것은 객체입니다 3 .

모든 것이 대상입니다.

function Foo(){} 새로운 함수를 정의 할뿐만 아니라, 새로운 함수를 사용하여 액세스 할 수있는 새로운 함수를 정의 Foo .

그렇기 때문에으로 Foo프로토 타입에 액세스 할 수 있습니다 Foo.prototype.

당신이 할 수있는 일은 더 많은 기능 을 설정 하는 것 입니다 Foo.

Foo.talk = function () {
  alert('hello world!');
};

이 새로운 기능은 다음을 사용하여 액세스 할 수 있습니다.

Foo.talk();

지금까지 함수 객체의 함수와 정적 메서드의 유사성을 주목하기를 바랍니다.

생각 f = new Foo();, 클래스 인스턴스를 만드는 등의 Foo.prototype.bar = function(){...}클래스에 대해 공유 방법을 정의로, 그리고 Foo.baz = function(){...}클래스에 대한 공공 정적 방법을 정의한다.


ECMAScript 2015는 이러한 종류의 선언에 대해 다양한 구문 설탕을 도입하여 쉽게 읽을 수 있고 구현하기가 더 간단 해졌습니다. 따라서 이전 예제는 다음과 같이 작성할 수 있습니다.

class Foo {
  bar() {...}

  static baz() {...}
}

다음 bar과 같이 호출 할 수 있습니다.

const f = new Foo()
f.bar()

다음과 baz같이 부릅니다.

Foo.baz()

1 : classECMAScript 5 사양에서 "Future Reserved Word" 였지만 ES6에서는 class키워드를 사용하여 클래스를 정의하는 기능이 도입되었습니다 .

2 : 본질적으로 생성자에 의해 생성 된 클래스 인스턴스이지만, 당신을 오도하고 싶지 않은 많은 미묘한 차이점이 있습니다.

3 : 원시 값 포함 - 어떤은 undefined, null, 부울, 숫자 및 문자열 됐소 기술적 때문에 그들이있는 거 낮은 수준의 언어 구현 객체. 부울, 숫자 및 문자열은 여전히 ​​프로토 타입 체인과 객체처럼 상호 작용하므로이 답변의 목적 상 비록 그렇지 않더라도 "객체"로 간주하는 것이 더 쉽습니다.


1
@lostyzd-글쎄, 그들은 그것을 통해 액세스 할 수 있습니다 Foo.talk(). 원하는 경우 생성자에서 다음을 지정할 수 this.talk = Foo.talk있습니다 Foo.prototype.talk = Foo.talk. 그러나 이것이 좋은 아이디어라고 확신하지 못합니다. 원칙적으로 인스턴스 메소드는 인스턴스마다 고유해야합니다.
nrabinowitz

2
@Doug Avery Foo.talk()는 네임 스페이스 함수를 호출합니다. Java / C #과 같은 OOP 언어에서 정적 메소드를 호출하는 방법과 유사한 상황에서이를 사용합니다. 유스 케이스의 좋은 예는 다음과 같은 함수 Array.isArray()입니다.
zzzzBov

7
PS 널은 널 객체 대해서 typeof == '물체'
mvladk

1
기본적으로 누락 된 부분은 정적 메서드가 상속된다는 것입니다. Foo.talk = function ()...자체 클래스 이름이있는 서브 클래스에는 사용할 수 없습니다. 이것은 "확장"서브 클래스로 해결할 수 있지만, 여전히 더 우아한 방법을 찾고 있습니다.

1
@nus, 일부 언어에서만 정적 메소드를 상속 할 수 있습니다. 상속이 필요한 경우 정적 메소드를 사용하여 시작해서는 안됩니다.
zzzzBov

67

아래와 같이 달성 할 수 있습니다.

function Foo() {};

Foo.talk = function() { alert('I am talking.'); };

이제 아래와 같이 "토크"기능을 호출 할 수 있습니다 :

Foo.talk();

JavaScript에서는 함수도 객체이기 때문에이 작업을 수행 할 수 있습니다.


37

인스턴스에서 정적 메소드를 호출하십시오.

function Clazz() {};
Clazz.staticMethod = function() {
    alert('STATIC!!!');
};

Clazz.prototype.func = function() {
    this.constructor.staticMethod();
}

var obj = new Clazz();
obj.func(); // <- Alert's "STATIC!!!"

간단한 자바 스크립트 클래스 프로젝트 : https://github.com/reduardo7/sjsClass


13
이것은 정적 호출이 아닙니다. var obj = 새로운 Clazz (); Clazz 의 새 인스턴스 를 만듭니다. 그러나 Clazz.staticMethod ()는 다른 모든 것들없이 결과를 얻습니다.
mpemburn

5
@ mpemburn : 에두아르도 그의 대답에 맞습니다. 그가 당신에게 보여준 것은 "외부"를 통해 정적 메소드를 호출 할 수있을 Clazz.staticMethod뿐만 아니라 인스턴스화 된 객체 내에서 이러한 정적 메소드에 링크하는 방법을 보여줍니다. 이것은 require를 사용하여 원래 생성자에 직접 액세스 할 수없는 Node.js와 같은 환경에서 특히 유용합니다. 내가 추가 할 유일한 것은this.constructor.staticMethod.apply(this, arguments);
Mauvis Ledford

1
커피 스크립트 생성자 내부에서도 작동합니다. constructor: (a) -> @constructor.add @(거의, 어쨌든)
Orwellophile

31

다음은 정적 / 인스턴스 변수 및 메소드에서 Javascript가 작동하는 방법을 보여주는 좋은 예입니다.

function Animal(name) {
    Animal.count = Animal.count+1||1;// static variables, use function name "Animal"
    this.name = name; //instance variable, using "this"
}

Animal.showCount = function () {//static method
    alert(Animal.count)
}

Animal.prototype.showName=function(){//instance method
    alert(this.name);
}

var mouse = new Animal("Mickey");
var elephant = new Animal("Haddoop");

Animal.showCount();  // static method, count=2
mouse.showName();//instance method, alert "Mickey"
mouse.showCount();//Error!! mouse.showCount is not a function, which is different from  Java

좋은 점 : 이것은 정적 기능에 액세스하지 않는 것이 이상 할 수 있습니다 this.
TrapII

해결책에 감사드립니다 이것은 내가 어떤 상황에서 this키워드에 액세스 할 수 있는지를 찾고 있습니다
santhosh

30

추가에, 지금은 함께 할 수 classstatic

'use strict'

class Foo {
 static talk() {
     console.log('talk')
 };

 speak() {
     console.log('speak')
 };

};

줄게

var a = new Foo();
Foo.talk();  // 'talk'
a.talk();    // err 'is not a function'
a.speak();   // 'speak'
Foo.speak(); // err 'is not a function'

예가 천 단어의 가치가 있기 때문에 이것이 가장 좋은 대답입니다. 그러나 왜 a.talk()작동하지 않는지 는 설명 하지 않습니다. 받아 들여진 대답은 프로토 타입 체인이 그것을 찾아야한다고 말합니다. 그러나 그렇지 않습니다
Pynchia

11

네임 스페이스를 사용합니다.

var Foo = {
     element: document.getElementById("id-here"),

     Talk: function(message) {
            alert("talking..." + message);
     },

     ChangeElement: function() {
            this.element.style.color = "red";
     }
};

그리고 그것을 사용하려면 :

Foo.Talk("Testing");

또는

Foo.ChangeElement();

6

ES6는 현재 매력과 같은 키워드 classstatic키워드를 지원합니다 .

class Foo {
    constructor() {}

    talk() {
        console.log("i am not static");
    }

    static saying() {
        console.log(this.speech);
    }

    static get speech() {
        return "i am static method";
    }

}

나는 이런 대답을 찾고있었습니다. 정적 메소드는 비 정적 메소드 / 변수를 호출 할 수 있습니까?
Tomasz Mularczyk

1
@Tomasz 정적 메소드에는 'this'가 클래스의 인스턴스로 설정되지 않고 클래스 자체로 설정됩니다. 물론 정적 메소드 는 인스턴스 메소드를 호출 할 수 있지만, 'static staticMethod () {new Foo (). talk (); } ´
JHH

3

ES5에서 정적 메서드를 작성 해야하는 경우 훌륭한 자습서를 찾았습니다.

//Constructor
var Person = function (name, age){
//private properties
var priv = {};

//Public properties
this.name = name;
this.age = age;

//Public methods
this.sayHi = function(){
    alert('hello');
}
}


// A static method; this method only 
// exists on the class and doesn't exist  
// on child objects
Person.sayName = function() {
   alert("I am a Person object ;)");  
};

@ https://abdulapopoola.com/2013/03/30/static-and-instance-methods-in-javascript/ 참조


2

추가 메모입니다. ES6 클래스를 사용하여 정적 메소드를 작성할 때 Javacsript 엔진은 디스크립터 속성을 구식 "정적"메소드와 약간 다르게 설정합니다.

function Car() {

}

Car.brand = function() {
  console.log('Honda');
}

console.log(
  Object.getOwnPropertyDescriptors(Car)
);

brand ()의 내부 속성 (설명자 속성)을

..
brand: [object Object] {
    configurable: true,
    enumerable: true,
    value: ..
    writable: true

}
..

에 비해

class Car2 {
   static brand() {
     console.log('Honda');
   }
}

console.log(
  Object.getOwnPropertyDescriptors(Car2)
);

brand ()의 내부 속성을

..
brand: [object Object] {
    configurable: true,
    enumerable: false,
    value:..
    writable: true
  }

..

ES6의 정적 메소드에 대해 열거 가능false 로 설정되어 있는지 확인하십시오 .

for-in 루프를 사용하여 객체를 확인할 수 없다는 것을 의미합니다.

for (let prop in Car) {
  console.log(prop); // brand
}

for (let prop in Car2) {
  console.log(prop); // nothing here
}

ES6의 정적 메소드는 정적 메소드가 여전히 쓰기 가능하므로 쓰기 가능한 디스크립터 가 true 로 설정 된다는 점을 제외하고 다른 클래스 개인 특성 (이름, 길이, 생성자)과 같이 취급 { writable: true }됩니다. 또한 우리가 그것을 무시할 수 있음을 의미합니다.

Car2.brand = function() {
   console.log('Toyota');
};

console.log(
  Car2.brand() // is now changed to toyota
);

1

를 호출 Foo.talk하려고하면 JS가 다음을 talk통해 함수 검색을 시도합니다.__proto__ 물론 찾을 수 없습니다.

Foo.__proto__입니다 Function.prototype.


1

정적 메서드 호출은 클래스에서 직접 이루어지며 클래스 인스턴스에서는 호출 할 수 없습니다. 정적 함수는 종종 유틸리티 함수를 만드는 데 사용됩니다

꽤 명확한 설명

mozilla.org에서 직접 가져옴

Foo는 클래스에 바인딩되어야합니다. 그러면 새 인스턴스를 만들 때 myNewInstance.foo ()를 호출 할 수 있습니다. 클래스를 가져 오면 정적 메서드를 호출 할 수 있습니다


0

그런 상황에 처했을 때 나는 다음과 같은 일을했다.

Logger = {
    info: function (message, tag) {
        var fullMessage = '';        
        fullMessage = this._getFormatedMessage(message, tag);
        if (loggerEnabled) {
            console.log(fullMessage);
        }
    },
    warning: function (message, tag) {
        var fullMessage = '';
        fullMessage = this._getFormatedMessage(message, tag);
        if (loggerEnabled) {
            console.warn(fullMessage);`enter code here`
        }
    },
    _getFormatedMessage: function () {}
};

그래서 지금은 정보 메소드를 호출 할 수 있습니다 Logger.info("my Msg", "Tag");


나는 항상 이것을하지만 기본적으로 이름을 지정하는 것입니다. 인스턴스 변수가있는 인스턴스를 만들 수 없습니까?
dcsan

0

귀하의 경우 Foo.talk():

function Foo() {};
// But use Foo.talk would be inefficient
Foo.talk = function () {
    alert('hello~\n');
};

Foo.talk(); // 'hello~\n'

그러나 구현하는 것이 비효율적이며 사용하는 prototype것이 좋습니다.


다른 방법으로, 내 방법은 정적 클래스로 정의됩니다.

var Foo = new function() {
  this.talk = function () {
    alert('hello~\n');
    };
};

Foo.talk(); // 'hello~\n'

정적 클래스는 prototype정적 사용으로 한 번만 생성되므로 정적 클래스를 사용할 필요가 없습니다 .

https://github.com/yidas/js-design-patterns/tree/master/class


@jvitoroc 감사합니다!
Nick Tsai

0

Javascript에는 실제 클래스가 없으며 프로토 타입 체인을 통해 다른 객체에서 객체를 '상속'하는 프로토 타입 상속 시스템을 사용합니다. 이것은 코드 자체를 통해 가장 잘 설명됩니다.

function Foo() {};
// creates a new function object

Foo.prototype.talk = function () {
    console.log('hello~\n');
};
// put a new function (object) on the prototype (object) of the Foo function object

var a = new Foo;
// When foo is created using the new keyword it automatically has a reference 
// to the prototype property of the Foo function

// We can show this with the following code
console.log(Object.getPrototypeOf(a) === Foo.prototype); 

a.talk(); // 'hello~\n'
// When the talk method is invoked it will first look on the object a for the talk method,
// when this is not present it will look on the prototype of a (i.e. Foo.prototype)

// When you want to call
// Foo.talk();
// this will not work because you haven't put the talk() property on the Foo
// function object. Rather it is located on the prototype property of Foo.

// We could make it work like this:
Foo.sayhi = function () {
    console.log('hello there');
};

Foo.sayhi();
// This works now. However it will not be present on the prototype chain 
// of objects we create out of Foo

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.