Объектно-ориентированные вопросы в Javascript - PullRequest
39 голосов
/ 14 января 2009

Я уже некоторое время использую javascript, но никогда не изучал язык без основ. Я читаю «Техники Pro Javascript» Джона Резига - я задаю некоторые вопросы, но я не нахожу ответы на них в книге, в Google и т. Д.

Джон приводит этот пример в своей книге:
Функция # 1

function User( name, age ){
  this.name = name;
  this.age = age;
}
// Add a new function to the object prototype
User.prototype.getName = function(){
  return this.name;
};
User.prototype.getAge = function(){
  return this.age;
};
var user = new User( "Bob", 44 );
console.log("User: " + user.getName() + ", Age: " + user.getAge());

Я все еще изучаю свойство prototype , поэтому я попытался написать что-то подобное:
Функция # 2

function User (name, age ) {
  this.name = name;
  this.age = age;
  this.getName = function() {
    return this.name;
  };
  this.getAge = function() {
    return this.age;
  };
}
var user = new User( "Bob", 44 );
console.log("User: " + user.getName() + ", Age: " + user.getAge());

Он не использует свойство prototype для создания функций getName и getAge, но вывод такой же, как в примере с Джоном.

Я сделал еще один шаг и создал это:
Функция # 3

var User = {
  name: "",
  age: 0,
  setName: function(name) {
    this.name = name;
  },
  setAge: function(age) {
    this.age = age;
  },
  getName: function() {
    return this.name;
  },
  getAge: function() {
    return this.age;
  }
};
User.setName("Bob");
User.setAge(44);
console.log("User: " + User.getName() + ", Age: " + User.getAge());

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

Вопрос № 1 - в чем разница между тремя функциями? В чем преимущество свойства prototype, и в том, что функция # 2 делает что-то неправильно, потому что кажется, что код # 2 более прост, чем # 1 (хотя я уверен, что # 1 делает это лучше, поскольку Джон его создал) .

Вопрос № 2 - Как я могу изменить функцию # 3, чтобы не использовать методы setName и setAge, но при этом сохранить стенографию {...}? Могут ли сокращения {...} иметь конструкторы?

Заранее спасибо за помощь в обучении!

EDIT Я думаю, что мой второй вопрос был немного запутанным. Я имел в виду, как я мог использовать сокращение {...} для создания объекта User, но затем, после создания объекта, произнесите что-то вроде:

var user = new User("Bob", 44);

Так же, как в функции # 1 - или это невозможно?

РЕДАКТИРОВАТЬ # 2 Вот Это Да! Спасибо всем за потрясающие ответы. Это действительно делает это намного более ясным для меня. Так что, если я правильно понимаю, разница между № 1 и № 2 не слишком велика. Если я только когда-либо создаю один объект «Пользователь» - они, вероятно, не отличаются вообще. Но если моя программа создаст много объектов User, # 1, скорее всего, будет более эффективным и использует меньше памяти, так как все объекты будут иметь одинаковые функции.

Я действительно ценю все отличные ответы - Спасибо!

Ответы [ 7 ]

22 голосов
/ 14 января 2009

Каждый раз, когда функция () {} оценивается, она создает новый объект функции. Поэтому в # 1 все объекты User совместно используют одни и те же функции getName и getAge, но в # 2 и # 3 каждый объект имеет свою собственную копию getName и getAge. Все различные функции getName ведут себя одинаково, поэтому вы не видите никакой разницы в выводе.

Сокращение {...} является конструктором. При оценке он создает новый «Объект» с заданными свойствами. Когда вы запускаете «новый пользователь (...)», он создает нового «пользователя». Вы случайно создали объект с тем же поведением, что и пользователь, но они бывают разных типов.

Ответ на комментарий:

Вы не можете напрямую. Вы можете сделать функцию, которая создает новый объект согласно # 3. Например:

function make_user(name, age) {
    return {
        name: name,
        age: age,
        getName: function() { return name; },
        getAge: function() { return age; },
    };
}

var user = make_user("Joe", "18");
12 голосов
/ 14 января 2009

Если вы хотите сделать ООП в JavaScript, я настоятельно рекомендую поискать замыкания. Я начал изучать эту тему с этих трех веб-страниц:

http://www.dustindiaz.com/javascript-private-public-privileged/

http://www.dustindiaz.com/namespace-your-javascript/

http://blog.morrisjohns.com/javascript_closures_for_dummies

Различия между 1, 2 и 3 заключаются в следующем: 1) Это пример добавления новых методов к существующему объекту. 2) То же самое, что и # 1, за исключением того, что некоторые методы включены в объект в функции User. 3) Это пример определения объекта с использованием JSON . Недостатком является то, что вы не можете использовать new (по крайней мере, в этом примере) для определения новых экземпляров этого объекта. Однако вы получаете все преимущества удобного стиля кодирования JSON.

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

редактировать Если вы хотите использовать new в функции # 3, вы можете написать это как

function User() {
  return {
    name: "",
    age: 0,
    setName: function(name) {
      this.name = name;
    },
    setAge: function(age) {
      this.age = age;
    },
    getName: function() {
      return this.name;
    },
    getAge: function() {
      return this.age;
    }
  };
}

Конечно, все эти функции и свойства будут общедоступными. Чтобы сделать их приватными, вам нужно использовать замыкания. Например, вы можете сделать возраст и имя частным с помощью этого синтаксиса.

function User() {
  var age=0;
  var name="";
  return {
    setName: function(name_) {
      name = name_;
    },
    setAge: function(age_) {
      age = age_;
    },
    getName: function() {
      return name;
    },
    getAge: function() {
      return age;
    }
  };
}
5 голосов
/ 14 января 2009

2

Вы можете получить доступ к имени и возрасту, не используя такие функции. В javascript вы должны использовать различные хаки, чтобы сохранить что-то частное или защищенное.

Это

User.name = "BoB";
User.age = 44;

выдаст тот же результат, что и ваш пример.

Нет конструкторов, как они появляются на других языках. Проще всего было бы просто определить функцию init () и вызвать ее сразу после создания экземпляра объекта.

Но мой самый большой совет для вас - заглянуть в http://www.prototypejs.org/. Это библиотека javascript с множеством интересных функций, которая пытается сделать javascript "более привлекательным" *.

Используя библиотеку прототипов, вы можете заставить классы вести себя как настоящие классы ООП. Также имеются конструкторы.

Edit: Что касается того, что вы спросили в своем комментарии:

person = new User();
person.name = "Bob";
person.age = 44;
4 голосов
/ 14 января 2009

Ваш пример # 1 показывает использование свойства prototype. Это свойство доступно для всех создаваемых вами объектов javascript и позволяет добавлять свойства или функции к объявлению объекта, поэтому у вас был объект с 2 свойствами, а затем вы добавили 4 функции (методы получения и установки).

Вы должны увидеть свойство prototype как способ изменения спецификации вашего объекта во время выполнения, скажем, у вас есть объект с именем name:

var Name = {
  First: "",
  Last: ""
};

Вы можете использовать прототип, чтобы добавить функцию getFullName () позже просто:

Name.prototype.getFullName = function() { return this.First + " " + this.Last; }

В примере 2 вы встраиваете объявление этих методов получения и установки в объявление объекта, так что в конце они совпадают. Наконец, в третьем примере вы используете нотацию объектов JavaScript, которую вы должны увидеть JSON .

По вашему вопросу 2 вы можете просто объявить свой объект как:

var User = {
  name: "",
  age: 0
};

это даст вам тот же объект без геттеров и сеттеров.

1 голос
/ 14 января 2009

Вопрос № 1

prototype имеет преимущество исправление обезьяны . Как показывает первый пример, функция добавляется после факта. Вы можете продолжить это, чтобы добавить или заменить любые методы, которые вам нужны (хотя, с честным предупреждением).

Определение таких объектов, как # 2, больше похоже на классическую ООП. Но, опять же, исправление обезьян не разрешено на всех языках ООП.

Вопрос № 2

В вашей третьей функции вам даже не нужны функции get и set - name и age являются общедоступными свойствами (потенциальный недостаток - до {}).

var User = {
  name: "",
  age: 0
};

User.name = 'Bob';
User.age = 44;

console.log("User: " + User.name + ", Age: " + User.age);

Когда вы создаете объект, используя {} (литерал объекта), {} является конструктором (зависит от браузера). Но, по сути, нет, вы не можете использовать конструктор в этом формате.

0 голосов
/ 24 мая 2010

Если вам интересны разговоры об объявлении класса JavaScript в стиле JSON ...

http://mahtonu.wordpress.com/2010/04/13/json-style-javascript-object-declaration/

0 голосов
/ 14 января 2009

У вас, кажется, есть несколько хороших ответов, но вы можете посмотреть на этот вопрос: лучший подход к переменным-членам в объектно-ориентированном javascript . Это был мой ответ , который описывает различия и сходства.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...