средства доступа к свойствам javascript

В Javascript кажется, что использование средств доступа к свойствам не так уж распространено (в отличие от других языков OO, таких как, например, Java).

Если у меня есть объект Person с именем, определенным как

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

Имя человека не изменится, но я хочу иметь доступ к нему, когда это необходимо, поэтому я мог бы сделать что-то вроде:

function Person(name) {
   var name = name;
   this.getName = function() {
      return name;
   }
}

Я думаю, что даже в динамическом языке принципы использования геттеров и сеттеров применяются так же, как и в статически типизированных объектно-ориентированных языках (например, инкапсуляция, добавление проверки, ограничение доступа и т. д.).

Этот вопрос может быть закрыт как субъективный, но мне любопытно, почему такое поведение не появляется чаще (например, разработчики Java сошли бы с ума, если бы все было общедоступным).

Есть ли «стандартный» способ сделать это в javascript? Я видел Object.defineProperty, но не все браузеры его поддерживают.


person Jeff Storey    schedule 07.09.2012    source источник
comment
Я предполагаю, что это может быть частично связано с желанием уменьшить нагрузку на полосу пропускания. Дополнительная разметка означает дополнительные биты по проводу. Хотя в случае небольшого примера, подобного этому, это может показаться неуместным, в надежном приложении, охватывающем десятки тысяч строк, это может стать важным.   -  person Shmiddty    schedule 08.09.2012
comment
Дополнительное определение также означает дополнительное использование памяти на клиенте, в том же духе, что и мой предыдущий комментарий.   -  person Shmiddty    schedule 08.09.2012
comment
Я думаю, что уже данные ответы показывают, что это не очень хороший вопрос для SO, поскольку он склонен получать ответы, основанные на мнении.   -  person madth3    schedule 08.09.2012


Ответы (5)


Javascript имеет перехватываемые методы доступа к свойствам:

http://ejohn.org/blog/javascript-getters-and-setters/

ИМХО, это гораздо лучшее решение для обеспечения соблюдения унифицированного принципа доступа, чем более строгие явные геттеры Java, но это также является частью простоты и негибкости этого языка (например, Groovy допускает аналогичный перехват).

person Matt Whipple    schedule 07.09.2012
comment
на практике такое много видели? Или обычно вы видите прямой доступ к собственности? - person Jeff Storey; 08.09.2012
comment
Вы бы использовали прямой доступ к свойствам, если вам не нужно изменять логику доступа: тогда вы бы использовали перехват. Возможно, вам придется спросить себя, почему вы используете геттеры и сеттеры в Java, если они ничего не делают: какова реальная практическая причина доктрины инкапсуляции. - person Matt Whipple; 08.09.2012
comment
Иногда я спрашиваю себя, почему, особенно в отношении простых свойств. Теоретически идея состоит в том, что если вы хотите добавить какое-то другое поведение в геттер или сеттер (например, ведение журнала, проверку и т. д.). Но на самом деле это не так часто происходит с простыми свойствами, поэтому это скорее соглашение Java (я предпочитаю, как это делает groovy, где вы явно не объявляете геттеры и сеттеры, но они генерируются и вызываются автоматически, но позволяет написать синтаксис прямого доступа к свойствам). - person Jeff Storey; 08.09.2012
comment
Да, поддерживаемые изменения в клиентском коде. В Java (и подобных языках) использование прямого доступа к свойствам усложнило бы внутренние изменения, а также такие шаблоны, как Decorator. Может быть, не часто требуется, но небольшие инвестиции могут окупиться с лихвой. В языках, которые обеспечивают более гибкий способ подключения к этому (включая большинство новых языков), логика может применяться, когда это необходимо, и не учитываться, когда нет (нет необходимости в упреждающем соглашении). - person Matt Whipple; 08.09.2012

Я знаю свои мысли по этому поводу.

Геттеры и сеттеры - зло.

Ждать! Действительно! Потерпите немного и позвольте мне объяснить.

Просто использовать метод для получения и установки значения... ну... как-то бессмысленно. Это не защищает, на самом деле, и что вы вкладываете, то и получаете.

С другой стороны, мне нравятся методы, которые вводят информацию, а затем извлекают ее обратно. НО вот волшебная часть! Это не та же самая информация. Не напрямую.

function Person(name) {
  this.getFullName = function() {return this.firstName + " " + this.lastName;};
  this.setBirthday = function(date) { this.birthday = date; };

  this.getAge = function() { /* Return age based on the birthday */ };
  this.isOfLegalDrinkingAge function() { /* do your math here too */ };
}

Но большую часть времени я просто вставляю статические данные и получаю статические данные. Какой смысл прятать это за геттерами и сеттерами?

В качестве вторичной причины, имеющей дело с DOM и большинством хост-объектов, вы устанавливаете свойства. Вы не играете с геттерами и сеттерами. Неиспользование их соответствует остальной части «аромата» того, что делают кодеры JS.

person Jeremy J Starcher    schedule 07.09.2012
comment
@BillyMoon -- Интересно, может ли он изменить свое имя пользователя на Javascript Preacher - person Jeremy J Starcher; 08.09.2012
comment
Спасибо за объяснение. Я думаю, что тот факт, что это не вкус javascript, действительно то, что я получил здесь. - person Jeff Storey; 08.09.2012
comment
Моя проблема с этим заключается в том, являются ли вещи функциями или свойствами. Иногда это может показаться довольно произвольным. Person.firstName работает, но Person.firstName() не работает, а наоборот, мне нужно использовать Person.fullName() вместо Person.fullName... - person CuddleBunny; 04.09.2014
comment
@CuddleBunny Я понимаю, что ты имеешь в виду. Вы всегда можете использовать оператор typeof и посмотреть, является ли он функцией. console.log(typeof Person.firstName), например. - person Jeremy J Starcher; 06.09.2014
comment
Конечно, я могу это сделать. Я всегда использую Visual Studio для разработки на JS, и он, как правило, подсказывает мне, что это такое. Я просто люблю постоянство. - person CuddleBunny; 11.09.2014

Я думаю, что ответ заключается в том, что эмуляция классов в javascript не является общепринятой практикой, потому что язык на самом деле является прототипом.

Хотя можно создавать структуры, подобные классам (как в вашем примере), они на самом деле не похожи на классы Java, и, как программист, вы в конечном итоге боретесь с нюансами.

Однако, если вы принимаете прототипическую природу javascript, вы получаете вознаграждение в виде другой, но целостной и простой структуры языка.

Нет необходимости использовать геттеры и сеттеры с прототипной структурой, так как вы можете просто установить объект, установив для него значение, и получить его, вызвав его как значение.

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

Я знаю, что этот ответ не является окончательным и окончательным, но, надеюсь, там есть некоторые идеи, которые помогут вам найти ответ, который вы ищете.

person Billy Moon    schedule 07.09.2012
comment
Благодарю. Вы можете сделать те же значения get/set непосредственно в java (например, если вы сделали что-то общедоступным), но это, похоже, не одобряется. Я думаю, во многом это связано с тем, какими стали идиомы языка. - person Jeff Storey; 08.09.2012
comment
Основная проблема в Java заключается в том, что вам не нужно менять интерфейс вашего класса позже. Будучи скомпилированным языком, предназначенным для долгосрочного обслуживания кода, приоритеты отличаются от приоритетов JS, языка на основе исходного кода, развернутого в быстро меняющейся веб-среде. - person Nate C-K; 28.01.2014

Прошу прощения, если я не правильно понял вопрос, но самовыполняющиеся функции — это один из способов сделать участников публичными/приватными.

var Person = function(){
  var _name = "Roger",
      self = { getName : function (){ return _name; }};
  return self;
}()

Затем вы можете получить доступ к Person.getName() из любого места, но не устанавливать _name.

person labroo    schedule 07.09.2012
comment
Я знаю, что вы можете это сделать, просто кажется, что это не совсем обычная практика... - person Jeff Storey; 08.09.2012

Это то, что я использовал для локальных полей:

TYPE_DEFAULT_VALUE= {
    number: 0,
    string: "",
    array: [],
    object: {},
};

typeOf = function (object) {
    if (typeof object === "number" && isNaN(object))
        return NaN;
    try {
        return Object.prototype.toString.call(object).slice(8, -1).toLowerCase();
    }
    catch(ex) {
        return "N/A";
    };
};

getAccessor = function(obj, key, type, defaultValue) {
    if (defaultValue === undefined) 
        defaultValue =  TYPE_DEFAULT_VALUE[type] === undefined ? null : TYPE_DEFAULT_VALUE[type];
    return {
        enumerable: true,
        configurable: true,
        get: function () {
            if (obj[key] === undefined) 
                obj[key] = defaultValue;
            return obj[key];
        },
        set: function (value) {
            if (typeOf(value) === type)
                obj[key] = value;
        },
    };
}

LocalFields = function (fields, object) {
    /**
    * field properties
    * { 
    *   type: [ required ] ( number | string | array | object | ... ),
    *   defaultValue: [ optional ]
    * }
    */
    if (! fields)
        throw "Too few parameters ...";
    if (! object) 
        object = this;

    var obj = this;
    var fieldsAccessor = {};
    for(key in fields){
        field = fields[key];
        fieldHandler = key[0].toUpperCase() + key.substr(1);
        if(! field.type)
            throw "Type not set for field: " + key;

        fieldsAccessor[fieldHandler] = getAccessor(obj, fieldHandler, field.type, field.defaultValue)
    }
    Object.defineProperties(object, fieldsAccessor);
}

Теперь для каждого класса я могу просто вызвать что-то вроде:

Person = function(){
    new LocalFields({
        id:     { type: "number" },
        name:   { type: "string" },
    }, this);
}

И затем, как геттер и сеттер VS, вы позвоните:

var alex = new Person();
alex.Name = "Alex Ramsi";
console.clear();
console.info(alex.Name);
person Morteza Tourani    schedule 16.05.2015