Синтаксис ООП в js и использование prototype

Чем отличаются данные куски кода и какой в каких случаях будет предпочтительнее?



A


function Obj() {}
Obj.method = function(type) {
    return this.coords[type];
};
var obj = new Obj(),
    current = obj.method(type);




B


function Obj() {}
Obj.prototype.method = function(type) {
    return this.coords[type];
};
var obj = new Obj(),
    current = obj.method(type);




C


var obj = {
    method : function(type) {
        return this.coords[type];
    }
},
    current = obj.method(type);




D


function objMethod(type){
    return this.coords[type];
}
var obj = {
    method : objMethod
},
    current = obj.method(type);



added @ 1732:

E


function Obj() {
    this.method = function(type) {
        return this.coords[type];
    };
}
var obj = new Obj(),
    current = obj.method(type);
  • Вопрос задан
  • 11629 просмотров
Решения вопроса 1
runawayed
@runawayed
JS — объектно-ориентированный язык, но в нем отсутствуют классы, их заменяют конструкторы объектов, поэтому вместо обычного наследования через классы существует наследование через прототипы. Т.е. экземпляр класса наследует его свойства и методы, которые находятся в его прототипе.
Конструктор класса (function Obj() {}) — функция, в которой описаны свойства и методы прототипа, поэтому ко всем ним будет доступ при создании экземпляра.

В примере A конструктор пустой, а Obj.method присваивает метод объекту, а не его прототипу, поэтому он не будет наследован в obj = new Obj(). Этот пример не работает.

Пример B — правильный, здесь метод method добавляется в прототип и будет наследоваться всеми экземплярами.

Пример C чаще всего используется, когда нужно реализовать singleton или namespace, потому что это простой хэш без конструктора, его нельзя наследовать. Фактически это не объект в ООП понимании, а просто ассоциативный массив, в котором могут содержаться любые данные, методы и другие объекты.

Пример D аналогичен примеру C, только его свойство method содержит ссылку на внешнюю функцию. Этот пример можно использовать, когда нужно вызвать какую-то функцию из внешней библиотеки.

Пример E правильный и аналогичен примеру B, с разницей в том, что наследуемый метод задается сразу в конструкторе, а не через prototype.
Ответ написан
Пригласить эксперта
Ответы на вопрос 4
Ну на самом деле разница между B и E есть, в случае E, когда метод объявляется прямо в конструкторе он будет иметь доступ к приватным переменным и методам, это так называемые привилегированные методы, а вот в примере B нет возможности работать с приватными данными.
Вот о чем я говорю:
var MyClass = function() {
  // Приватные атрибуты
  var _a, _b;
  // Приватный метод
  function _myPrivate(isbn) {
  } 
  // публичный привилегированный метод
  this.MyPublicPlus = function() {

  };
}

// публичный непривилегированный метод.
MyClass.prototype = {
  MyPublic: function() {
  }
};


Метод MyPublicPlus будет иметь доступ к _a, _b и _myPrivate(), а MyPublic нет…
Ответ написан
Комментировать
runawayed
@runawayed
Вообще, всем кто интересуется тем, как устроен Javascript, советую прочесть www.crockford.com/javascript/javascript.html
Ответ написан
Комментировать
runawayed
@runawayed
В примере E еще стоит обратить внимание на слово this, которое означает, что method будет добавлен в прототип.
Если его не использовать, можно создавать private-свойства и методы, которые будут доступны из других методов экзмепляра, но не напрямую.

function Obj() {
    var privateMethod = function(x) {
        return x;
    }
    this.publicMethod = function(x) {
      return privateMethod(x); 
   }
}
var obj = new Obj();
obj.privateMethod(1); // -> вызовет ошибку
obj.publicMethod(1); // -> вернет '1'

Ответ написан
Zitrix
@Zitrix
A, B, E — имеется конструктор Obj. в случае вызова функции через new — конструктор будет работать с новым объектом, в случае вызова через call/apply будет расширять заданный Вами объект. т.е. одна функция может быть и «классом» и «примесью».
C, D — создание объекта «руками», без конструктора. в D функция вынесена и проименована — подразумевается, что её можно использовать не только как метод.
B — используется прототипирование. прототип есть у каждого объекта*, это значит, что непримитивные типы данных (функции, массивы, даты, объекты) будут храниться только в прототипе, экономя память и время «создания» объекта, но замедляя последующую работу с объектом из-за того, что обращение к «общим» данным/методам будет происходить через (цепочку) прототипов.
A — функция тоже является объектом, и «метод» обозначен именно у объекта функции. объект функции(-конструктора) никак не связан с изменяемыми ею объектами. объет конструктора — хорошее место для всякого хлама, который не хочется распространять на все его «экземпляры».

пара моментов, которые следует осознать:

function Obj() {}
Obj.prototype.q = [1,2,3];
var obj = new Obj();
Obj.prototype.q[1] = 8;
alert(obj.q +" - "+ Obj.prototype.q); // 1,8,3 - 1,8,3

function Obj() {
  this.val = 5;
  this.method = function() { alert(this.val *2); };
};
var obj = new Obj();
window.setTimeout(obj.method, 1); // NaN


почитать:
ru.wikipedia.org/wiki/Прототипное_программирование
* dklab.ru/chicken/nablas/40.html — .constructor.prototype; где-то там же было объяснение необходимости ставить conctructor прототипа в «текущий» класс
www.webreference.com/js/column26/apply.html — apply-примесь
так же рекомендую погуглить про всякие typeof, instanceof, hasOwnProperty, isPrototypeOf, etc.

в какой пропорции использовать прототипы, замыкания, примеси и прочие декораторы — дело исключительно Вашего стиля.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы