Должен ли я использовать объектные литералы или функции-конструкторы?

94

Я не понимаю, каким образом я должен создавать объект в javascript. Кажется, есть как минимум два пути. Один из них - использовать буквальную нотацию объекта, а другой - функции построения. Есть ли у одного преимущество перед другим?

чобо
источник
1
Лучший ответ: stackoverflow.com/questions/4597926/… - Таким образом, вы также можете настроить функцию для создания экземпляров в буквальной нотации. При этом каждый экземпляр переносит все методы, тогда как в конструкторе все экземпляры относятся к методам прототипа. Т.е. у конструктора лучше производительность памяти.
Федерико
Если память не является проблемой, доступ к свойствам литералов объектов намного быстрее - jsperf.com/module-pattern-vs-object-literal-vs-prototype/4
Daniel

Ответы:

131

Если у вас нет поведения, связанного с объектом (т.е. если объект является просто контейнером для данных / состояния), я бы использовал литерал объекта.

var data = {
    foo: 42,
    bar: 43
};

Примените принцип KISS . Если вам не нужно ничего, кроме простого контейнера данных, используйте простой литерал.

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

function MyData(foo, bar) {
    this.foo = foo;
    this.bar = bar;

    this.verify = function () {
        return this.foo === this.bar;
    };
}

// or:
MyData.prototype.verify = function () {
    return this.foo === this.bar;
};

Подобный класс также действует как схема для вашего объекта данных: теперь у вас есть своего рода контракт (через конструктор), какие свойства инициализирует / содержит объект. Свободный литерал - это просто аморфный блок данных.

У вас также может быть внешняя verifyфункция, которая действует на простой старый объект данных:

var data = {
    foo: 42,
    bar: 43
};

function verify(data) {
    return data.foo === data.bar;
}

Однако это неблагоприятно для инкапсуляции: в идеале все данные + поведение, связанные с сущностью, должны существовать вместе.

Атес Горал
источник
12
Отличное объяснение, но как насчет помещения функций в литерал объекта? Я видел это раньше. На самом деле в посте ниже есть пример этого.
чобо 01
23
Если вы включаете определения функций как часть литерала объекта или используете this.fn = function ...подход в конструкторе, каждый из ваших экземпляров объекта будет иметь свои собственные копии функций. Используя подход прототипа, вы присоединяете каждую функцию один раз и только один раз: они будут унаследованы экземплярами через прототипное наследование.
Атес Горал
14
Я считаю, что вы упустили важную вещь. только функция-конструктор может предоставлять как частные, так и открытые члены (инкапсуляция). в литерале объекта - все они публичные.
Рой Намир
Что было бы лучше для игрового движка? Я использовал метод конструктора, но у моих прототипов нет доступа к данным конструктора.
zachdyer
90

По сути, это сводится к тому, нужно ли вам несколько экземпляров вашего объекта или нет; объект, определенный с помощью конструктора, позволяет иметь несколько экземпляров этого объекта. Объектные литералы - это, по сути, синглтоны с общедоступными переменными / методами.

// define the objects:
var objLit = {
  x: 0,
  y: 0,
  z: 0,
  add: function () {
    return this.x + this.y + this.z;
  }
};

var ObjCon = function(_x, _y, _z) {
  var x = _x; // private
  var y = _y; // private
  this.z = _z; // public
  this.add = function () {
    return x + y + this.z; // note x, y doesn't need this.
  };
};

// use the objects:
objLit.x = 3; 
objLit.y = 2; 
objLit.z = 1; 
console.log(objLit.add());    

var objConIntance = new ObjCon(5,4,3); // instantiate an objCon
console.log(objConIntance.add());
console.log((new ObjCon(7,8,9)).add()); // another instance of objCon
console.log(objConIntance.add()); // same result, not affected by previous line
роннбот
источник
1
Это очень хороший момент, о котором следует помнить при принятии решения. Спасибо.
zkent
По моему опыту, именно в этом разница. Отличный наглядный пример.
Воздух
9

Другой способ создания объектов единообразным образом - использовать функцию, возвращающую объект:

function makeObject() {
    var that = {
        thisIsPublic: "a public variable"
        thisIsAlsoPublic: function () {
            alert(that.thisIsPublic);
        }
    };

    var secret = "this is a private variable"

    function secretFunction() { // private method
        secret += "!"; // can manipulate private variables
        that.thisIsPublic = "foo";     
    }

    that.publicMethod = function () {
        secret += "?"; // this method can also mess with private variables
    }

    that.anotherPublicVariable = "baz";

    return that; // this is the object we've constructed
}

makeObject.static = "This can be used to add a static varaible/method";

var bar = makeObject();
bar.publicMethod(); // ok
alert(bar.thisIsPublic); // ok
bar.secretFunction(); // error!
bar.secret // error!

Поскольку функции в JavaScript являются замыканиями, мы можем использовать частные переменные и методы и избегать их new.

Из http://javascript.crockford.com/private.html о частных переменных в JavaScript.

JustcallmeDrago
источник
7

В приведенном ниже коде показаны три метода создания объекта: синтаксис литерала объекта, конструктор функций и Object.create(). Синтаксис литерала объекта просто создает объект на лету, и поэтому он __prototype__является Objectобъектом, и он будет иметь доступ ко всем свойствам и методам Object. Строго с точки зрения шаблона проектирования следует использовать простой литерал объекта для хранения одного экземпляра данных.

Конструктор функции имеет специальное свойство с именем .prototype. Это свойство станет __prototype__одним из объектов, созданных конструктором функции. Все свойства и методы, добавленные к .prototypeсвойству конструктора функции, будут доступны для всех создаваемых им объектов. Конструктор следует использовать, если вам требуется несколько экземпляров данных или требуется поведение вашего объекта. Обратите внимание, что конструктор функции также лучше всего использовать, когда вы хотите смоделировать частный / публичный шаблон разработки. Не забудьте поместить все общие методы в.prototype чтобы они не создавались в каждом экземпляре объекта.

Создание объектов с Object.create()использованием литерала объекта в качестве __prototype__объекта, созданного этим методом. Все свойства и методы, добавленные к литералу объекта, будут доступны всем объектам, созданным из него посредством истинного прототипного наследования. Это мой предпочтительный метод.

//Object Example

//Simple Object Literal
var mySimpleObj = {
    prop1 : "value",
    prop2 : "value"
}

// Function Constructor
function PersonObjConstr()  {
    var privateProp = "this is private";
    this.firstname = "John";
    this.lastname = "Doe";
}
PersonObjConstr.prototype.greetFullName = function()    {
    return "PersonObjConstr says: Hello " + this.firstname + 
    " " + this.lastname;
};

// Object Literal
var personObjLit = {
    firstname : "John",
    lastname: "Doe",
    greetFullName : function() {
        return "personObjLit says: Hello " + this.firstname +
        ", " + this.lastname;
    }
} 

var newVar = mySimpleObj.prop1;
var newName = new PersonObjConstr();
var newName2 = Object.create(personObjLit);
JOP
источник
1
Поскольку вы объявили функцию внутри литерала объекта. Означает ли это, что когда вы создаете объект, используя Object.createфункцию внутри литерала, он будет уникальным для каждого экземпляра?
JohnnyQ
6

Это зависит от того, чем вы хотите заниматься. Если вы хотите использовать (полу) частные переменные или функции в своем объекте, функция-конструктор - это способ сделать это. Если ваш объект содержит только свойства и методы, подойдет литерал объекта.

function SomeConstructor(){
    var x = 5;
    this.multiply5 = function(i){
        return x*i;
    }
}
var myObj = new SomeConstructor;

var SomeLiteral = {
    multiply5: function(i){ return i*5; }
}

Теперь метод multiply5в myObjиSomeLiteral делать то же самое. Единственное отличие в том, что myObj использует частную переменную. Последнее может быть полезно в некоторых случаях. В большинстве случаев литерала объекта достаточно, и это хороший и чистый способ создания JS-объекта.

KooiInc
источник
В чем разница между функцией и методом? Я пришел из фона С #, поэтому для меня функция является автономной, а метод - это просто функция, которая является частью класса.
чобо 01
1
Нет такой большой разницы, см., Например, web-source.net/javascript_tutorial/… . Фактически, в DOMscripting (js на стороне клиента в браузере) все функции становятся методами объекта window (глобального пространства имен), я бы сказал (вы можете адресовать все «автономные» функции как window. [Somefunction].
KooiInc,
5

введите описание изображения здесь

Вам нужен единственный экземпляр объекта для страницы - Literal.

Вы хотите просто передавать данные, такие как объекты DTO, просто GET SET: - Literal

Вы хотите создавать реальные объекты с поведением методов, несколько экземпляров - функция конструктора, следование принципам ООП, наследование: - функции конструктора.

Ниже приведено видео на YouTube, в котором подробно объясняется, что такое буквальное, что такое функции-конструкторы и чем они отличаются друг от друга.

https://www.youtube.com/watch?v=dVoAq2D3n44

Шивпрасад Коирала
источник
1

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

Том
источник
Как создать частные переменные в литерале объекта?
EhevuTov
Вы действительно не можете, это не имеет отношения к исходному вопросу, поэтому я просто дам вам ссылку: javascript.crockford.com/private.html
Tom
1
На самом деле, это актуально, потому что там, где есть разница, есть причина использовать то или другое в зависимости от определенных ситуаций; в этом случае это будет то, нужны ли вам частные переменные или нет. Вы можете создать частные переменные в литерале, сначала создав закрывающую функцию в своем литерале, но, на мой взгляд, это намного уродливее и труднее для чтения.
EhevuTov
Я исправлюсь, мое первоначальное прочтение вопроса заключалось в том, что чобо спрашивал о том, как передавать переменные в конструктор, как в списке параметров по сравнению с параметром литерала одного объекта.
Tom
1

Как упоминалось в https://www.w3schools.com/js/js_object_definition.asp

С помощью литерала объекта, вы оба определить и создать , один объект в одном заявлении.

Также

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

Алиреза Фаттахи
источник
0

Функция конструктора Object () работает немного медленнее и более подробна. Таким образом, рекомендуемый способ создания новых объектов в JavaScript - использовать буквальную нотацию

Объектно-ориентированный JavaScript Udacity

теккуз
источник
0

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

var myObject = {

   publicMethod: function () {
      privateMethod1();
      privateMethod2(); 
      function privateMethod1(){
          console.log('i am privateMethod1');
      } 
      function privateMethod2(){
          console.log('i am privateMethod2');
      } 
   }

}

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

Юрин
источник
-1

// Литерал объекта и конструктор объекта

function MyData(foo, bar) {
        this.foo = foo;
        this.bar = bar;

    }
MyData.prototype.verify = function () {
        return this.foo === this.bar;
    };

//add property using prototype

var MD  = new MyData;//true.
var MD = new MyData();//true.
MD.verify// return only the function structure.
MD.verify(); //return the verify value and in this case return true coz both value is null. 
var MD1  = new MyData(1,2); // intialized the value at the starting. 
MD1.verify// return only the function structure.
MD1.verify(); // return false coz both value are not same.
MD1.verify(3,3);// return false coz this will not check this value intialized at the top 
MyData.prototype.verify = function (foo,bar) {
    return this.foo === this.bar;
};
var MD1  = new MyData(1,2);
MD1.verify();
MD1.verify(3,3);// return false coz this keyword used with foo and bar that will check parent data 
Амит Кумар
источник
1
Где в вашем примере объявлен литерал объекта?
JohnnyQ