Шаблон модуля в JavaScript для пользовательских конструкторов - PullRequest
2 голосов
/ 27 февраля 2020

Я пытаюсь применить шаблон модуля в JavaScript, когда у меня есть свой конструктор. Давайте возьмем этот пример:

// Assume I need `name` as private
function Dog(name){
    this.name = name;
}

Я могу переписать выше:

function Dog(name) {

    let dogName = name;

    return {

        getName : function () {
            return dogName;
        },

        setName : function (name) {
            dogName = name;
        }
    }
}

Приведенный выше код не имеет права constructor свойство и Dog.prototype и prototype of object returned не соответствует .

Чтобы исправить это, я сделал 2 исправления:

function Dog(name) {

    let dogName = name;

    return {

        // Fix 1
        constructor : Dog,

        getName : function () {
            return dogName;
        },

        setName : function (name) {
            dogName = name;
        }
    }
}


let d2 = new Dog("Tony");
// Fix 2 : This is painful as every time I need to set this up
Object.setPrototypeOf(d2 , Dog.prototype);

Как видите, исправление 2 болезненно. Каждый раз, когда мне нужно создать объект, мне нужно это сделать. Есть ли лучший способ?

Давайте не будем отклоняться от нашего обсуждения, что геттер и сеттер должны быть перенесены в прототип. Выше приведен лишь краткий пример.

Ответы [ 2 ]

2 голосов
/ 27 февраля 2020

Отказ от ответственности: я не защищаю инкапсуляцию в javascript. Но вот несколько уродливых способов сделать это.

Если все, что вам нужно, это уменьшить дублирование кода, то вы можете легко все настроить в «конструкторе», сделав его собственной фабрикой:

function Dog(name) {

    let dogName = name;

    let dog = Object.create(Dog.prototype);

    dog.getName = function () {
        return dogName;
    };

    dog.setName = function (name) {
        dogName = name;
    };

    return dog;
}


let d2 = new Dog("Tony");

Если вы также хотите реально использовать прототип, вы можете использовать символы, чтобы в значительной степени скрыть свойство name:

Dog = function() {

    const nameSymbol = Symbol("name");

    function Dog(name) {
        Object.defineProperty(this, nameSymbol, {
            configurable: true,
            enumerable: false,
            writable: true,
            value: name
        });
    }

    Object.assign(Dog.prototype, {
        getName : function () {
            return this[nameSymbol];
        },

        setName : function (name) {
            this[nameSymbol] = name;
        }
    });

    return Dog;
}();

let d2 = new Dog("Tony");

Но символ все еще можно получить с помощью Object.getOwnPropertySymbols , Поэтому, если вы ищете способ действительно сделать невозможным доступ к имени без использования метода получения / установки, я думаю, вам придется использовать WeakMap:

Dog = function() {

    const dogNames = new WeakMap()

    function Dog(name) {
        dogNames.set(this, name);
    }

    Object.assign(Dog.prototype, {
        getName : function () {
            return dogNames.get(this);
        },

        setName : function (name) {
            dogNames.set(this, name);
        }
    });

    return Dog;
}();

let d2 = new Dog("Tony");
2 голосов
/ 27 февраля 2020

Проблема в том, что вы возвращаете литерал объекта в своем "конструкторе", у литерала объекта есть прототип Object. Вы не должны возвращаться в конструктор, вы должны присваивать вещи this, как вы делали в своем первом фрагменте кода.

function Dog(name) {

    let dogName = name;

    this.getName = function () {
        return dogName;
    };

    this.setName = function (name) {
        dogName = name;
    };
}

Это не обычный шаблон для реализации классов, вы ' Вы даже не используете prototype, поэтому неясно, почему prototype важен для вас. Очевидно, что этот подход позволяет вам закрыть переменную, чтобы сделать ее «закрытой», что было бы невозможно при использовании prototype. Тем не менее, этот подход имеет больше накладных расходов, потому что каждый экземпляр Dog будет иметь свою собственную функцию для getName и setName, стандартное соглашение для «закрытых» переменных и методов состоит в том, чтобы поставить перед ними префикс подчеркивания, чтобы вы могли использовать prototype по назначению.

function Dog(name) {
    this._name = name;
}

Dog.prototype.getName = function () {
    return this._name;
};

Dog.prototype.setName = function (name) {
    this._name = name;
};
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...