development

JavaScript로 사용자 정의 객체를 "적절하게"만드는 방법은 무엇입니까?

big-blog 2020. 2. 15. 23:08
반응형

JavaScript로 사용자 정의 객체를 "적절하게"만드는 방법은 무엇입니까?


속성과 메서드가있는 JavaScript 객체를 만드는 가장 좋은 방법이 무엇인지 궁금합니다.

사람이 사용 var self = this하고 self.모든 기능에서 사용하여 범위가 항상 올바른지 확인하는 예제를 보았습니다 .

그런 다음 .prototype속성을 추가 하는 사용 하는 예제를 보았지만 다른 사람들은 인라인으로 수행했습니다.

누군가 속성과 메소드가있는 JavaScript 객체의 적절한 예를 제공 할 수 있습니까?


JavaScript에서 클래스와 인스턴스를 구현하기위한 두 가지 모델, 프로토 타이핑 방식과 클로저 방식이 있습니다. 둘 다 장점과 단점이 있으며 확장 된 변형이 많이 있습니다. 많은 프로그래머와 라이브러리는 언어의 더 추악한 부분을 다루기 위해 서로 다른 접근 방식과 클래스 처리 유틸리티 기능을 가지고 있습니다.

결과적으로 혼합 회사에서는 메타 클래스가 혼동되어 모든 동작이 약간 다르게 작동합니다. 더구나, 대부분의 JavaScript 튜토리얼 자료는 끔찍하며 모든 기반을 다루기 위해 어떤 종류의 타협을 제공하여 혼란스러워합니다. (아마도 저자는 혼란 스러울 수 있습니다. JavaScript의 객체 모델은 대부분의 프로그래밍 언어와 매우 다르며, 여러 곳에서 똑바로 잘못 설계되었습니다.)

prototype way로 시작하겠습니다 . 이것은 당신이 얻을 수있는 가장 JavaScript 고유의 것입니다 : 최소한의 오버 헤드 코드가 있으며 instanceof는 이런 종류의 객체의 인스턴스와 작동합니다.

function Shape(x, y) {
    this.x= x;
    this.y= y;
}

이 생성자 함수 new Shapeprototype조회 에 작성하여 생성 된 인스턴스에 메소드를 추가 할 수 있습니다 .

Shape.prototype.toString= function() {
    return 'Shape at '+this.x+', '+this.y;
};

이제 JavaScript가 서브 클래 싱을하는 것을 호출 할 수있는만큼 서브 클래 싱합니다. 우리는 그 이상한 마법 prototype속성 을 완전히 대체하여 그렇게 합니다.

function Circle(x, y, r) {
    Shape.call(this, x, y); // invoke the base class's constructor function to take co-ords
    this.r= r;
}
Circle.prototype= new Shape();

메소드를 추가하기 전에 :

Circle.prototype.toString= function() {
    return 'Circular '+Shape.prototype.toString.call(this)+' with radius '+this.r;
}

이 예제는 작동하며 많은 자습서에서 이와 유사한 코드를 볼 수 있습니다. 그러나 사람, 그것은 new Shape()추악합니다. 실제 모양을 만들지 않아도 기본 클래스를 인스턴스화하고 있습니다. 자바 스크립트가 너무 실수이기 때문에이 간단한 경우 작업에 발생합니다 제로 인수하는 경우, 전달 될 수 있도록 x하고 yundefined및 프로토 타입의에 할당 this.x하고 this.y. 생성자 함수가 더 복잡한 작업을 수행하면 얼굴이 평평 해집니다.

따라서 기본 클래스의 생성자 함수를 호출하지 않고 클래스 수준에서 원하는 메서드와 다른 멤버를 포함하는 프로토 타입 객체를 만드는 방법을 찾아야합니다. 이를 위해 헬퍼 코드 작성을 시작해야합니다. 이것이 내가 아는 가장 간단한 접근법입니다.

function subclassOf(base) {
    _subclassOf.prototype= base.prototype;
    return new _subclassOf();
}
function _subclassOf() {};

프로토 타입에있는 기본 클래스의 멤버를 아무것도하지 않는 새로운 생성자 함수로 전송 한 다음 해당 생성자를 사용합니다. 이제 간단히 쓸 수 있습니다 :

function Circle(x, y, r) {
    Shape.call(this, x, y);
    this.r= r;
}
Circle.prototype= subclassOf(Shape);

new Shape()잘못 대신 . 이제 클래스를 빌드 할 수있는 기본 요소 세트가 있습니다.

이 모델에서 고려할 수있는 몇 가지 개선 및 확장이 있습니다. 예를 들어 다음은 구문 설탕 버전입니다.

Function.prototype.subclass= function(base) {
    var c= Function.prototype.subclass.nonconstructor;
    c.prototype= base.prototype;
    this.prototype= new c();
};
Function.prototype.subclass.nonconstructor= function() {};

...

function Circle(x, y, r) {
    Shape.call(this, x, y);
    this.r= r;
}
Circle.subclass(Shape);

두 버전 모두 생성자 함수를 여러 언어로 상속 할 수 없다는 단점이 있습니다. 따라서 서브 클래스가 구성 프로세스에 아무 것도 추가하지 않더라도 기본이 원하는 인수를 사용하여 기본 생성자를 호출해야합니다. 이것은를 사용하여 약간 자동화 할 수 apply있지만 여전히 작성해야합니다.

function Point() {
    Shape.apply(this, arguments);
}
Point.subclass(Shape);

따라서 일반적인 확장은 초기화 항목을 생성자 자체가 아닌 자체 함수로 나누는 것입니다. 이 함수는 기본에서 잘 상속받을 수 있습니다.

function Shape() { this._init.apply(this, arguments); }
Shape.prototype._init= function(x, y) {
    this.x= x;
    this.y= y;
};

function Point() { this._init.apply(this, arguments); }
Point.subclass(Shape);
// no need to write new initialiser for Point!

이제 각 클래스에 대해 동일한 생성자 함수 상용구가 있습니다. 어쩌면 우리는 그것을 자신의 도우미 함수로 옮길 수 있으므로 예를 들어 대신에 입력하고 계속 Function.prototype.subclass돌리지 않아도 기본 클래스의 함수가 서브 클래스를 뱉어 낼 수 있습니다.

Function.prototype.makeSubclass= function() {
    function Class() {
        if ('_init' in this)
            this._init.apply(this, arguments);
    }
    Function.prototype.makeSubclass.nonconstructor.prototype= this.prototype;
    Class.prototype= new Function.prototype.makeSubclass.nonconstructor();
    return Class;
};
Function.prototype.makeSubclass.nonconstructor= function() {};

...

Shape= Object.makeSubclass();
Shape.prototype._init= function(x, y) {
    this.x= x;
    this.y= y;
};

Point= Shape.makeSubclass();

Circle= Shape.makeSubclass();
Circle.prototype._init= function(x, y, r) {
    Shape.prototype._init.call(this, x, y);
    this.r= r;
};

... 약간 더 어색한 문법이지만 다른 언어와 조금 더 비슷해지기 시작합니다. 원하는 경우 몇 가지 추가 기능을 뿌릴 수 있습니다. 어쩌면 makeSubclass클래스 이름을 기억하고 toString사용하여 기본값 제공하고 싶을 수도 있습니다 . 어쩌면 new연산자 없이 실수로 호출되었을 때 생성자를 감지하고 싶을 수도 있습니다 (그렇지 않으면 종종 매우 성가신 디버깅이 발생합니다).

Function.prototype.makeSubclass= function() {
    function Class() {
        if (!(this instanceof Class))
            throw('Constructor called without "new"');
        ...

어쩌면 모든 새 멤버를 전달 makeSubclass하고 프로토 타입에 추가하여 Class.prototype...너무 많이 쓰지 않아도 될 수 있습니다 . 많은 클래스 시스템이이를 수행합니다. 예 :

Circle= Shape.makeSubclass({
    _init: function(x, y, z) {
        Shape.prototype._init.call(this, x, y);
        this.r= r;
    },
    ...
});

객체 시스템에서 바람직하다고 생각할 수있는 많은 잠재적 인 기능이 있으며 아무도 특정 공식에 동의하지 않습니다.


폐쇄 방법 , 그럼. 이것은 상속을 전혀 사용하지 않음으로써 JavaScript의 프로토 타입 기반 상속 문제를 피할 수 있습니다. 대신 :

function Shape(x, y) {
    var that= this;

    this.x= x;
    this.y= y;

    this.toString= function() {
        return 'Shape at '+that.x+', '+that.y;
    };
}

function Circle(x, y, r) {
    var that= this;

    Shape.call(this, x, y);
    this.r= r;

    var _baseToString= this.toString;
    this.toString= function() {
        return 'Circular '+_baseToString(that)+' with radius '+that.r;
    };
};

var mycircle= new Circle();

이제 모든 단일 인스턴스에는 Shape고유 한 toString메소드 사본 (및 추가 한 다른 메소드 또는 다른 클래스 멤버)이 있습니다.

각 반원의 고유 사본을 가진 모든 인스턴스에 대한 나쁜 점은 덜 효율적이라는 것입니다. 많은 수의 서브 클래스 인스턴스를 처리하는 경우 프로토 타입 상속이 더 적합합니다. 또한 기본 클래스의 메소드 호출은 약간 성가시다. 서브 클래스 생성자가 덮어 쓰기 전에 메소드가 무엇인지 기억해야한다.

[여기에 상속이 없기 때문에 instanceof연산자는 작동하지 않습니다. 필요한 경우 클래스 스니핑을위한 고유 한 메커니즘을 제공해야합니다. 프로토 타입 상속과 비슷한 방식으로 프로토 타입 객체를 피들 링 수는 있지만 약간 까다 롭고 실제로 instanceof작업하는 것이 가치가 없습니다 .]

고유 한 메서드를 가진 모든 인스턴스에 대한 좋은 점은 메서드가 해당 메서드를 소유 한 특정 인스턴스에 바인딩 될 수 있다는 것입니다. 이것은 this메소드 호출에서 JavaScript의 이상한 바인딩 방법 때문에 유용합니다 . 메소드에서 소유자로부터 메소드를 분리하면 결과가 나타납니다.

var ts= mycircle.toString;
alert(ts());

그런 다음 this메서드 내부에 예상대로 Circle 인스턴스가되지 않습니다 (실제로 전역 window객체가되어 광범위한 디버깅 문제가 발생합니다). 방법은 촬영과에 할당 될 때 현실에서 이것은 일반적으로 발생 setTimeout, onclick또는 EventListener일반적이다.

프로토 타입 방식을 사용하면 이러한 모든 과제에 대한 마감을 포함해야합니다.

setTimeout(function() {
    mycircle.move(1, 1);
}, 1000);

또는 앞으로 (또는 지금 Function.prototype을 해킹하는 경우) 다음과 같이 할 수도 있습니다 function.bind().

setTimeout(mycircle.move.bind(mycircle, 1, 1), 1000);

인스턴스가 클로저 방식으로 수행되는 경우 바인딩은 인스턴스 변수를 통해 클로저에 의해 무료로 수행됩니다 (일반적으로 that또는 라고 self하지만 개인적으로 후자 self는 JavaScript에서 다른 다른 의미를 가지고 있으므로 권장합니다 ). 1, 1위의 스 니펫에서 인수 를 무료로 얻지 못 하므로 다른 클로저가 필요하거나 bind()그렇게 해야하는 경우 여전히 필요 합니다.

클로저 방법에는 많은 변형이 있습니다. 연산자 를 사용하는 대신 this완전히 작성하여 새로 작성하여 that리턴 하는 것을 선호 할 수 있습니다 new.

function Shape(x, y) {
    var that= {};

    that.x= x;
    that.y= y;

    that.toString= function() {
        return 'Shape at '+that.x+', '+that.y;
    };

    return that;
}

function Circle(x, y, r) {
    var that= Shape(x, y);

    that.r= r;

    var _baseToString= that.toString;
    that.toString= function() {
        return 'Circular '+_baseToString(that)+' with radius '+r;
    };

    return that;
};

var mycircle= Circle(); // you can include `new` if you want but it won't do anything

"적절한"방법은 무엇입니까? 양자 모두. 어느 것이 "최고"입니까? 상황에 따라 다릅니다. FOWW 나는 강력한 OO 작업을 할 때 실제 JavaScript 상속을위한 프로토 타입을 만들고 경향이 있으며 간단한 버리기 페이지 효과를 닫습니다.

그러나 두 가지 방법 모두 대부분의 프로그래머에게는 직관적이지 않습니다. 둘 다 잠재적으로 지저분한 변형이 많이 있습니다. 다른 사람의 코드 / 라이브러리를 사용하는 경우 (중간 및 일반적으로 손상된 많은 체계뿐만 아니라) 두 가지를 모두 충족하게됩니다. 일반적으로 인정되는 답변은 없습니다. JavaScript 객체의 놀라운 세계에 오신 것을 환영합니다.

[이것은 JavaScript가 내가 가장 좋아하는 프로그래밍 언어가 아닌 이유 94의 일부였습니다.]


이 패턴을 상당히 자주 사용합니다. 필자가 필요할 때 상당히 융통성이 있다는 것을 알게되었습니다. 사용시 Java 스타일 클래스와 비슷합니다.

var Foo = function()
{

    var privateStaticMethod = function() {};
    var privateStaticVariable = "foo";

    var constructor = function Foo(foo, bar)
    {
        var privateMethod = function() {};
        this.publicMethod = function() {};
    };

    constructor.publicStaticMethod = function() {};

    return constructor;
}();

생성시 호출되는 익명 함수를 사용하여 새 생성자 함수를 반환합니다. 익명 함수는 한 번만 호출되므로 비공개 정적 변수를 만들 수 있습니다 (클로저 내부에 있으며 클래스의 다른 구성원에게 표시됨). 생성자 함수는 기본적으로 표준 Javascript 객체입니다. 내부에서 개인 속성을 정의하면 공용 속성이 this변수에 첨부됩니다 .

기본적으로이 접근 방식은 Crockfordian 접근 방식과 표준 Javascript 객체를 결합하여보다 강력한 클래스를 만듭니다.

다른 Javascript 객체와 마찬가지로 사용할 수 있습니다.

Foo.publicStaticMethod(); //calling a static method
var test = new Foo();     //instantiation
test.publicMethod();      //calling a method

Douglas CrockfordThe Good Parts 에서이 주제에 대해 광범위하게 논의합니다 . 연산자를 사용하여 새 개체를 만들지 않는 것이 좋습니다 . 대신 그는 맞춤형 생성자를 만들 것을 제안합니다. 예를 들어 :

var mammal = function (spec) {     
   var that = {}; 
   that.get_name = function (  ) { 
      return spec.name; 
   }; 
   that.says = function (  ) { 
      return spec.saying || ''; 
   }; 
   return that; 
}; 

var myMammal = mammal({name: 'Herb'});

Javascript에서 함수는 객체이며 new 연산자 와 함께 객체를 구성하는 데 사용할 수 있습니다 . 일반적으로 생성자로 사용되는 함수는 대문자로 시작합니다. 당신은 종종 다음과 같은 것을 본다 :

function Person() {
   this.name = "John";
   return this;
}

var person = new Person();
alert("name: " + person.name);**

새 객체를 인스턴스화하는 동안 new 연산자 를 사용하는 것을 잊어 버린 경우 일반적인 함수 호출 이 이루어 지며 새 객체 대신 전역 객체에 바인딩됩니다.


bobince의 답변 을 계속하려면

es6에서는 이제 실제로 class

이제 할 수 있습니다 :

class Shape {
    constructor(x, y) {
        this.x = x;
        this.y = y;
    }

    toString() {
        return `Shape at ${this.x}, ${this.y}`;
    }
}

따라서 다른 답변과 마찬가지로 원으로 확장하면 다음과 같이 할 수 있습니다.

class Circle extends Shape {
    constructor(x, y, r) {
        super(x, y);
        this.r = r;
    }

    toString() {
        let shapeString = super.toString();
        return `Circular ${shapeString} with radius ${this.r}`;
    }
}

es6에서 약간 더 깔끔하고 읽기가 더 쉽습니다.


다음은 이에 대한 좋은 예입니다.

class Shape {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }

  toString() {
    return `Shape at ${this.x}, ${this.y}`;
  }
}

class Circle extends Shape {
  constructor(x, y, r) {
    super(x, y);
    this.r = r;
  }

  toString() {
    let shapeString = super.toString();
    return `Circular ${shapeString} with radius ${this.r}`;
  }
}

let c = new Circle(1, 2, 4);

console.log('' + c, c);


구조체를 사용하여 이런 식으로 할 수도 있습니다.

function createCounter () {
    var count = 0;

    return {
        increaseBy: function(nb) {
            count += nb;
        },
        reset: function {
            count = 0;
        }
    }
}

그런 다음 :

var counter1 = createCounter();
counter1.increaseBy(4);

또 다른 방법은 http://jsfiddle.net/nnUY4/입니다 (이러한 종류의 객체 생성 및 공개 기능이 특정 패턴을 따르는 지 여부는 알 수 없습니다)

// Build-Reveal

var person={
create:function(_name){ // 'constructor'
                        //  prevents direct instantiation 
                        //  but no inheritance
    return (function() {

        var name=_name||"defaultname";  // private variable

        // [some private functions]

        function getName(){
            return name;
        }

        function setName(_name){
            name=_name;
        }

        return {    // revealed functions
            getName:getName,    
            setName:setName
        }
    })();
   }
  }

  // … no (instantiated) person so far …

  var p=person.create(); // name will be set to 'defaultname'
  p.setName("adam");        // and overwritten
  var p2=person.create("eva"); // or provide 'constructor parameters'
  alert(p.getName()+":"+p2.getName()); // alerts "adam:eva"

생성자 호출 중에 "this"를 닫는 트릭을 사용하는 경우, 오브젝트에서 메소드를 호출하지 않으려는 다른 오브젝트에 의해 콜백으로 사용될 수있는 함수를 작성하기위한 것입니다. "범위를 올바르게 만들기"와 관련이 없습니다.

다음은 바닐라 JavaScript 객체입니다.

function MyThing(aParam) {
    var myPrivateVariable = "squizzitch";

    this.someProperty = aParam;
    this.useMeAsACallback = function() {
        console.log("Look, I have access to " + myPrivateVariable + "!");
    }
}

// Every MyThing will get this method for free:
MyThing.prototype.someMethod = function() {
    console.log(this.someProperty);
};

Douglas Crockford 가 JavaScript에 대해 말한 내용 을 읽으면 많은 것을 얻을 수 있습니다 . 존 레식 도 훌륭합니다. 행운을 빕니다!


Closure다목적입니다. bobince객체 생성시 프로토 타입과 클로저 접근 방식 을 잘 요약했습니다 . 그러나 OOP함수형 프로그래밍 방식으로 클로저 사용하는 몇 가지 측면을 모방 할 수 있습니다 . 함수는 JavaScript의 객체 라는 것을 기억하십시오 . 함수를 다른 방식으로 객체로 사용하십시오.

폐쇄 예는 다음과 같습니다.

function outer(outerArg) {
    return inner(innerArg) {
        return innerArg + outerArg; //the scope chain is composed of innerArg and outerArg from the outer context 
    }
}

얼마 전에 저는 Closure에 관한 Mozilla의 기사를 보았습니다. "잠금으로 일부 데이터 (환경)를 해당 데이터에서 작동하는 함수와 연결할 수 있습니다. 이는 객체 지향 프로그래밍과 객체와의 연관 관계를 갖는 객체 지향 프로그래밍과 명백히 유사합니다. )를 하나 이상의 방법으로 " 프로토 타입을 참조하지 않고 클로저와 클래식 OOP의 병렬 처리를 처음 읽은 것은 처음이었습니다.

어떻게?

일부 품목의 부가가치세를 계산한다고 가정합니다. VAT는 신청 기간 동안 안정적으로 유지 될 것입니다. OOP (의사 코드)에서이를 수행하는 한 가지 방법 :

public class Calculator {
    public property VAT { get; private set; }
    public Calculator(int vat) {
        this.VAT = vat;
    }
    public int Calculate(int price) {
        return price * this.VAT;
    }
}

기본적으로 VAT 값을 생성자에 전달하면 계산 메소드가 클로저 를 통해 계산할 수 있습니다 . 이제 클래스 / 생성자를 사용하는 대신 VAT를 인수로 함수에 전달하십시오. 관심있는 유일한 것은 계산 자체이기 때문에 새로운 함수를 반환합니다.

function calculator(vat) {
    return function(item) {
        return item * vat;
    }
}
var calculate = calculator(1.10);
var jsBook = 100; //100$
calculate(jsBook); //110

프로젝트에서 VAT가 계산에 적합한 후보 인 최상위 값을 식별하십시오. 동일한 인수를 계속해서 전달할 때마다 경험적으로 클로저를 사용하여이를 개선 할 수있는 방법이 있습니다. 전통적인 객체를 만들 필요가 없습니다.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Closures


객체 만들기

JavaScript로 객체를 생성하는 가장 쉬운 방법은 다음 구문을 사용하는 것입니다.

var test = {
  a : 5,
  b : 10,
  f : function(c) {
    return this.a + this.b + c;
  }
}

console.log(test);
console.log(test.f(3));

이것은 구조화 된 방식으로 데이터를 저장하는 데 효과적입니다.

그러나 더 복잡한 사용 사례의 경우 종종 함수 인스턴스를 작성하는 것이 좋습니다.

function Test(a, b) {
  this.a = a;
  this.b = b;
  this.f = function(c) {
return this.a + this.b + c;
  };
}

var test = new Test(5, 10);
console.log(test);
console.log(test.f(3));

이를 통해 예를 들어 클래스를 사용하는 방법과 유사한 동일한 "청사진"을 공유하는 여러 객체를 만들 수 있습니다. 자바.

그러나 프로토 타입을 사용하면 여전히 더 효율적으로 수행 할 수 있습니다.

함수의 다른 인스턴스가 동일한 메소드 또는 특성을 공유 할 때마다 해당 오브젝트의 프로토 타입으로 이동할 수 있습니다. 이렇게하면 함수의 모든 인스턴스가 해당 메서드 나 속성에 액세스 할 수 있지만 모든 인스턴스에 대해 복제 할 필요는 없습니다.

우리의 경우, 메소드 f를 프로토 타입 으로 옮기는 것이 합리적입니다 .

function Test(a, b) {
  this.a = a;
  this.b = b;
}

Test.prototype.f = function(c) {
  return this.a + this.b + c;
};

var test = new Test(5, 10);
console.log(test);
console.log(test.f(3));

계승

JavaScript에서 상속을 수행하는 간단하지만 효과적인 방법은 다음 두 줄을 사용하는 것입니다.

B.prototype = Object.create(A.prototype);
B.prototype.constructor = B;

그것은 이것을하는 것과 비슷합니다 :

B.prototype = new A();

두 가지의 주요 차이점은의 생성자 A가를 사용할 때 실행되지 않는다는 것입니다 Object.create. 이는 클래스 기반 상속과 더 직관적이고 유사합니다.

다음과 같이 생성자 에 추가하여 A새 인스턴스를 만들 때 생성자를 선택적으로 실행하도록 선택할 수 있습니다 .BB

function B(arg1, arg2) {
    A(arg1, arg2); // This is optional
}

당신의 모든 인수 전달하려는 경우 B에를 A, 당신은 또한 사용할 수 있습니다 Function.prototype.apply():

function B() {
    A.apply(this, arguments); // This is optional
}

다른 객체를의 생성자 체인에 혼합 B하려면 다음 Object.create결합 하면됩니다 Object.assign.

B.prototype = Object.assign(Object.create(A.prototype), mixin.prototype);
B.prototype.constructor = B;

데모

function A(name) {
  this.name = name;
}

A.prototype = Object.create(Object.prototype);
A.prototype.constructor = A;

function B() {
  A.apply(this, arguments);
  this.street = "Downing Street 10";
}

B.prototype = Object.create(A.prototype);
B.prototype.constructor = B;

function mixin() {

}

mixin.prototype = Object.create(Object.prototype);
mixin.prototype.constructor = mixin;

mixin.prototype.getProperties = function() {
  return {
    name: this.name,
    address: this.street,
    year: this.year
  };
};

function C() {
  B.apply(this, arguments);
  this.year = "2018"
}

C.prototype = Object.assign(Object.create(B.prototype), mixin.prototype);
C.prototype.constructor = C;

var instance = new C("Frank");
console.log(instance);
console.log(instance.getProperties());


노트

Object.createIE9 +를 포함한 모든 최신 브라우저에서 안전하게 사용할 수 있습니다. Object.assignIE 버전이나 일부 모바일 브라우저에서는 작동하지 않습니다. 하는 것이 좋습니다 polyfill Object.create 및 / 또는 Object.assign당신이 그들을 지원 브라우저를 사용하려는 경우 그 그들을 구현하지 않습니다.

당신의 polyfill을 찾을 수 있습니다 Object.create 여기 와 하나 Object.assign 여기 .


var Person = function (lastname, age, job){
this.name = name;
this.age = age;
this.job = job;
this.changeName = function(name){
this.lastname = name;
}
}
var myWorker = new Person('Adeola', 23, 'Web Developer');
myWorker.changeName('Timmy');

console.log("New Worker" + myWorker.lastname);

최신 브라우저를 대상으로 할 수 있으면 Object.defineProperty를 사용할 수 있습니다 .

Object.defineProperty () 메서드는 객체에서 직접 새 속성을 정의하거나 객체의 기존 속성을 수정하고 객체를 반환합니다. 출처 : Mozilla

var Foo = (function () {
    function Foo() {
        this._bar = false;
    }
    Object.defineProperty(Foo.prototype, "bar", {
        get: function () {
            return this._bar;
        },
        set: function (theBar) {
            this._bar = theBar;
        },
        enumerable: true,
        configurable: true
    });
    Foo.prototype.toTest = function () {
        alert("my value is " + this.bar);
    };
    return Foo;
}());

// test instance
var test = new Foo();
test.bar = true;
test.toTest();

데스크탑 및 모바일 호환성 목록을 보려면 Mozilla의 브라우저 호환성 목록을 참조하십시오 . 예, IE9 +는 Safari 모바일뿐만 아니라이를 지원합니다.


당신은 또한 이것을 시도 할 수 있습니다

    function Person(obj) {
    'use strict';
    if (typeof obj === "undefined") {
        this.name = "Bob";
        this.age = 32;
        this.company = "Facebook";
    } else {
        this.name = obj.name;
        this.age = obj.age;
        this.company = obj.company;
    }

}

Person.prototype.print = function () {
    'use strict';
    console.log("Name: " + this.name + " Age : " + this.age + " Company : " + this.company);
};

var p1 = new Person({name: "Alex", age: 23, company: "Google"});
p1.print();

나에게 잘 어울리는 패턴
var Klass = function Klass() {
    var thus = this;
    var somePublicVariable = x
      , somePublicVariable2 = x
      ;
    var somePrivateVariable = x
      , somePrivateVariable2 = x
      ;

    var privateMethod = (function p() {...}).bind(this);

    function publicMethod() {...}

    // export precepts
    this.var1 = somePublicVariable;
    this.method = publicMethod;

    return this;
};

먼저 생성자의 prototype객체 대신 인스턴스에 메소드를 추가하는 환경 설정을 변경할 수 있습니다 . Inheritance & Decorators와 관련 하여 Constructor Hijacking을 매우 자주 사용하기 때문에 생성자 내부에서 메서드를 거의 항상 선언합니다 .

다음은 어느 선언이 작성되는지 결정하는 방법입니다.

  • 컨텍스트 객체에서 직접 메소드를 선언하지 마십시오 ( this)
  • 보자 var선언보다 우선 function선언
  • 프리미티브가 객체 ( {}[]) 보다 우선합니다
  • 보자 public선언보다 우선 private선언
  • 선호 Function.prototype.bind이상 thus, self, vm,etc
  • 다음과 같은 경우를 제외하고 다른 클래스 내에서 클래스를 선언하지 마십시오.
    • 두 사람은 분리 할 수 ​​없다는 것이 분명해야합니다
    • 내부 클래스는 명령 패턴을 구현합니다.
    • Inner 클래스는 싱글 톤 패턴을 구현합니다
    • Inner 클래스는 State 패턴을 구현합니다.
    • 내부 클래스는이를 보증하는 다른 디자인 패턴을 구현합니다.
  • 폐쇄 공간 this어휘 범위 내에서 항상 반환 하십시오.

다음은 이러한 도움이되는 이유입니다.

생성자 하이재킹
var Super = function Super() {
    ...
    this.inherited = true;
    ...
};
var Klass = function Klass() {
    ...
    // export precepts
    Super.apply(this);  // extends this with property `inherited`
    ...
};
모델 디자인
var Model = function Model(options) {
    var options = options || {};

    this.id = options.id || this.id || -1;
    this.string = options.string || this.string || "";
    // ...

    return this;
};
var model = new Model({...});
var updated = Model.call(model, { string: 'modified' });
(model === updated === true);  // > true
디자인 패턴
var Singleton = new (function Singleton() {
    var INSTANCE = null;

    return function Klass() {
        ...
        // export precepts
        ...

        if (!INSTANCE) INSTANCE = this;
        return INSTANCE;
    };
})();
var a = new Singleton();
var b = new Singleton();
(a === b === true);  // > true

보시다시피, thus내가 선호하는 Function.prototype.bind(또는 .call또는 .apply) 이상thus 이므로 실제로 필요하지 않습니다 . 우리 Singleton학급 에서는 더 많은 정보를 전달 하기 thus때문에 이름을 INSTANCE짓지 않습니다 . 를 들어 Model, 우리는 반환 this우리가 사용하는 생성자를 호출 할 수 있도록 .call우리가 그것으로 전달 된 인스턴스를 반환 할 수 있습니다. 중복 적으로 우리는 변수에 변수를 할당 updated했지만 다른 시나리오에서는 유용합니다.

또한, new{brackets} 대신 키워드를 사용하여 객체 리터럴을 구성하는 것을 선호합니다 .

선호하는
var klass = new (function Klass(Base) {
    ...
    // export precepts
    Base.apply(this);  //
    this.override = x;
    ...
})(Super);
선호하지 않음
var klass = Super.apply({
    override: x
});

보다시피 후자는 슈퍼 클래스의 "재정의"속성을 재정의하는 기능이 없습니다.

클래스의 prototype객체 에 메소드를 추가 하면 new키워드 를 사용하거나 사용하지 않고 객체 리터럴을 선호합니다 .

선호하는
Klass.prototype = new Super();
// OR
Klass.prototype = new (function Base() {
    ...
    // export precepts
    Base.apply(this);
    ...
})(Super);
// OR
Klass.prototype = Super.apply({...});
// OR
Klass.prototype = {
    method: function m() {...}
};
선호하지 않음
Klass.prototype.method = function m() {...};

제목 이나 문자열 을 사용하여 객체를 선언 할 수 있다고 언급하고 싶습니다 .
각 유형을 호출하는 방법에는 여러 가지가 있습니다. 아래를보십시오 :

var test = {

  useTitle : "Here we use 'a Title' to declare an Object",
  'useString': "Here we use 'a String' to declare an Object",
  
  onTitle : function() {
    return this.useTitle;
  },
  
  onString : function(type) {
    return this[type];
  }
  
}

console.log(test.onTitle());
console.log(test.onString('useString'));


기본적으로 JS에는 클래스 개념이 없으므로 기존 디자인 패턴과 관련된 클래스 생성자로 함수를 사용합니다.

//Constructor Pattern
function Person(name, age, job){
 this.name = name;
 this.age = age;
 this.job = job;
 this.doSomething = function(){
    alert('I am Happy');
}
}

지금까지 JS는 객체를 만들려는 단서가 없으므로 여기에 새로운 키워드가옵니다.

var person1 = new Person('Arv', 30, 'Software');
person1.name //Arv

Ref : 웹 개발자를위한 전문 JS-Nik Z

참고 URL : https://stackoverflow.com/questions/1595611/how-to-properly-create-a-custom-object-in-javascript



반응형