Как "правильно" создать пользовательский объект в JavaScript?

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

Я видел примеры, когда человек использовал var self = this а затем использует self. во всех функциях, чтобы убедиться, что область действия всегда правильная.

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

Может кто-нибудь дать мне пример JavaScript-объекта с некоторыми свойствами и методами?

15 ответов

Решение

Существует две модели реализации классов и экземпляров в JavaScript: способ создания прототипа и способ закрытия. Оба имеют свои преимущества и недостатки, и существует множество расширенных вариантов. У многих программистов и библиотек разные подходы и функции утилит для обработки классов, чтобы написать о некоторых уродливых частях языка.

В результате в смешанной компании вы получите смесь метаклассов, которые ведут себя немного по-разному. Что еще хуже, большинство учебных материалов по JavaScript ужасны и служат неким промежуточным компромиссом, охватывающим все основы, оставляя вас в замешательстве. (Возможно, автор также запутался. Объектная модель JavaScript сильно отличается от большинства языков программирования, и во многих местах она плохо спроектирована.)

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

function Shape(x, y) {
    this.x= x;
    this.y= y;
}

Мы можем добавить методы к экземпляру, созданному new Shape записав их в prototype поиск этой функции конструктора:

Shape.prototype.toString= function() {
    return 'Shape at '+this.x+', '+this.y;
};

Теперь, чтобы создать его подкласс, настолько, насколько вы можете назвать то, что JavaScript делает подклассами. Мы делаем это, полностью заменяя эту странную магию prototype имущество:

function Circle(x, y, r) {
    Shape.call(this, x, y); // invoke the base class's constructor function to take co-ords
    this.r= r;
}
Circle.prototype= new Shape();

перед добавлением методов к нему:

Circle.prototype.toString= function() {
    return 'Circular '+Shape.prototype.toString.call(this)+' with radius '+this.r;
}

Этот пример будет работать, и вы увидите код, подобный этому, во многих руководствах. Но человек, это new Shape() уродливо: мы создаем экземпляр базового класса, даже если не нужно создавать фактическую форму. Это работает в этом простом случае, потому что JavaScript такой небрежный: он позволяет передавать нулевые аргументы, и в этом случае x а также y становиться undefined и назначены на прототип this.x а также this.y, Если бы функция конструктора делала что-то более сложное, она бы не получалась.

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

function subclassOf(base) {
    _subclassOf.prototype= base.prototype;
    return new _subclassOf();
}
function _subclassOf() {};

Это передает членов базового класса в его прототипе новой функции конструктора, которая ничего не делает, а затем использует этот конструктор. Теперь мы можем написать просто:

function Circle(x, y, r) {
    Shape.call(this, x, y);
    this.r= r;
}
Circle.prototype= subclassOf(Shape);

вместо new Shape() неправильность. Теперь у нас есть приемлемый набор примитивов для построенных классов.

Есть несколько уточнений и расширений, которые мы можем рассмотреть в рамках этой модели. Например, вот синтаксически-сахарная версия:

Function.prototype.subclass= function(base) {
    var c= Function.prototype.subclass.nonconstructor;
    c.prototype= base.prototype;
    this.prototype= new c();
};
Function.prototype.subclass.nonconstructor= function() {};

...

function Circle(x, y, r) {
    Shape.call(this, x, y);
    this.r= r;
}
Circle.subclass(Shape);

Любая версия имеет недостаток, заключающийся в том, что функция конструктора не может быть унаследована, как во многих языках. Таким образом, даже если ваш подкласс ничего не добавляет к процессу конструирования, он должен не забывать вызывать конструктор базы с любыми аргументами, которые требуется базе. Это может быть немного автоматизировано с помощью apply, но все же вы должны написать:

function Point() {
    Shape.apply(this, arguments);
}
Point.subclass(Shape);

Таким образом, общее расширение состоит в том, чтобы разбить материал инициализации на его собственную функцию, а не на сам конструктор. Эта функция может потом наследовать от базы просто отлично:

function Shape() { this._init.apply(this, arguments); }
Shape.prototype._init= function(x, y) {
    this.x= x;
    this.y= y;
};

function Point() { this._init.apply(this, arguments); }
Point.subclass(Shape);
// no need to write new initialiser for Point!

Теперь у нас есть один и тот же шаблон функций конструктора для каждого класса. Может быть, мы можем переместить это в свою собственную вспомогательную функцию, чтобы нам не приходилось вводить ее, например, вместо Function.prototype.subclass, поворачивая его и позволяя функции базового класса выплевывать подклассы:

Function.prototype.makeSubclass= function() {
    function Class() {
        if ('_init' in this)
            this._init.apply(this, arguments);
    }
    Function.prototype.makeSubclass.nonconstructor.prototype= this.prototype;
    Class.prototype= new Function.prototype.makeSubclass.nonconstructor();
    return Class;
};
Function.prototype.makeSubclass.nonconstructor= function() {};

...

Shape= Object.makeSubclass();
Shape.prototype._init= function(x, y) {
    this.x= x;
    this.y= y;
};

Point= Shape.makeSubclass();

Circle= Shape.makeSubclass();
Circle.prototype._init= function(x, y, r) {
    Shape.prototype._init.call(this, x, y);
    this.r= r;
};

... который начинает больше походить на другие языки, хотя и с немного неуклюжим синтаксисом. Вы можете добавить несколько дополнительных функций, если хотите. Может ты хочешь makeSubclass взять и запомнить имя класса и предоставить значение по умолчанию toString используй это. Может быть, вы хотите, чтобы конструктор обнаружил, когда он был случайно вызван без new оператор (который в противном случае часто приводил бы к очень раздражающей отладке):

Function.prototype.makeSubclass= function() {
    function Class() {
        if (!(this instanceof Class))
            throw('Constructor called without "new"');
        ...

Может быть, вы хотите передать всех новых членов и иметь makeSubclass добавьте их в прототип, чтобы избавить вас от необходимости писать Class.prototype... довольно много. Многие системы классов делают это, например:

Circle= Shape.makeSubclass({
    _init: function(x, y, z) {
        Shape.prototype._init.call(this, x, y);
        this.r= r;
    },
    ...
});

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


Путь закрытия, тогда. Это позволяет избежать проблем наследования на основе прототипов JavaScript, поскольку вообще не использует наследование. Вместо:

function Shape(x, y) {
    var that= this;

    this.x= x;
    this.y= y;

    this.toString= function() {
        return 'Shape at '+that.x+', '+that.y;
    };
}

function Circle(x, y, r) {
    var that= this;

    Shape.call(this, x, y);
    this.r= r;

    var _baseToString= this.toString;
    this.toString= function() {
        return 'Circular '+_baseToString(that)+' with radius '+that.r;
    };
};

var mycircle= new Circle();

Теперь каждый экземпляр Shape будет иметь свою собственную копию toString метод (и любые другие методы или другие члены класса, которые мы добавляем).

Плохая вещь в каждом экземпляре, имеющем свою собственную копию каждого члена класса, состоит в том, что он менее эффективен. Если вы имеете дело с большим количеством подклассовых экземпляров, прототипное наследование может помочь вам лучше. Кроме того, вызов метода базового класса немного раздражает, как вы можете видеть: мы должны помнить, каким был метод, до того, как конструктор подкласса перезаписал его, или он потеряется.

[Кроме того, потому что здесь нет наследования, instanceof оператор не работает; Вы должны будете предоставить свой собственный механизм для сниффинга классов, если вам это нужно. Хотя вы можете возиться с объектами-прототипами так же, как и с наследованием прототипов, это немного сложнее и не стоит того, чтобы просто получить instanceof за работой.]

Хорошая вещь о каждом экземпляре, имеющем свой собственный метод, - то, что метод может тогда быть привязан к определенному экземпляру, которому он принадлежит. Это полезно из-за странного способа связывания JavaScript this в вызовах методов, что приводит к тому, что если вы отсоедините метод от его владельца:

var ts= mycircle.toString;
alert(ts());

затем this внутри метода не будет экземпляр Circle, как ожидалось (на самом деле это будет глобальный window объект, вызывающий широкое отладочное горе). В действительности это обычно происходит, когда метод берется и присваивается setTimeout, onclick или же EventListener в общем.

При использовании прототипа вы должны включать закрытие для каждого такого назначения:

setTimeout(function() {
    mycircle.move(1, 1);
}, 1000);

или, в будущем (или сейчас, если вы взломали Function.prototype), вы также можете сделать это с function.bind():

setTimeout(mycircle.move.bind(mycircle, 1, 1), 1000);

если ваши экземпляры выполняются способом замыкания, привязка выполняется бесплатно закрытием над переменной экземпляра (обычно называется that или же selfхотя лично я бы посоветовал против последнего как self уже имеет другое, другое значение в JavaScript). Вы не получите аргументы 1, 1 в приведенном выше фрагменте бесплатно, так что вам все равно понадобится еще одно закрытие или bind() если вам нужно сделать это.

Есть много вариантов метода закрытия. Вы можете предпочесть опустить this полностью, создавая новый that и вернуть его вместо использования new оператор:

function Shape(x, y) {
    var that= {};

    that.x= x;
    that.y= y;

    that.toString= function() {
        return 'Shape at '+that.x+', '+that.y;
    };

    return that;
}

function Circle(x, y, r) {
    var that= Shape(x, y);

    that.r= r;

    var _baseToString= that.toString;
    that.toString= function() {
        return 'Circular '+_baseToString(that)+' with radius '+r;
    };

    return that;
};

var mycircle= Circle(); // you can include `new` if you want but it won't do anything

Какой путь "правильный"? И то и другое. Что является "лучшим"? Это зависит от вашей ситуации. FWIW Я склонен к созданию прототипов для реального наследования JavaScript, когда я сильно занимаюсь OO, и замыканий для простых одноразовых эффектов страницы.

Но оба способа довольно нелогичны для большинства программистов. У обоих есть много потенциальных беспорядочных изменений. Вы встретите обе (а также многие промежуточные и вообще неработающие схемы), если будете использовать код / ​​библиотеки других людей. Нет единого общепринятого ответа. Добро пожаловать в удивительный мир объектов JavaScript.

[Это часть 94 Почему JavaScript не мой любимый язык программирования.]

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

var Foo = function()
{

    var privateStaticMethod = function() {};
    var privateStaticVariable = "foo";

    var constructor = function Foo(foo, bar)
    {
        var privateMethod = function() {};
        this.publicMethod = function() {};
    };

    constructor.publicStaticMethod = function() {};

    return constructor;
}();

При этом используется анонимная функция, которая вызывается при создании, возвращая новую функцию конструктора. Поскольку анонимная функция вызывается только один раз, вы можете создать в ней закрытые статические переменные (они находятся внутри замыкания, видимого для других членов класса). Функция конструктора в основном является стандартным объектом Javascript - вы определяете закрытые атрибуты внутри нее, а публичные атрибуты прикрепляются к this переменная.

По сути, этот подход объединяет крокфордский подход со стандартными объектами Javascript для создания более мощного класса.

Вы можете использовать его так же, как и любой другой объект Javascript:

Foo.publicStaticMethod(); //calling a static method
var test = new Foo();     //instantiation
test.publicMethod();      //calling a method

Дуглас Крокфорд широко обсуждает эту тему в "Хороших частях". Он рекомендует избегать нового оператора для создания новых объектов. Вместо этого он предлагает создавать индивидуальные конструкторы. Например:

var mammal = function (spec) {     
   var that = {}; 
   that.get_name = function (  ) { 
      return spec.name; 
   }; 
   that.says = function (  ) { 
      return spec.saying || ''; 
   }; 
   return that; 
}; 

var myMammal = mammal({name: 'Herb'});

В Javascript функция является объектом и может использоваться для создания объектов вместе с оператором new. По соглашению, функции, предназначенные для использования в качестве конструкторов, начинаются с заглавной буквы. Вы часто видите такие вещи, как:

function Person() {
   this.name = "John";
   return this;
}

var person = new Person();
alert("name: " + person.name);**

В случае, если вы забыли использовать оператор new при создании экземпляра нового объекта, то вы получите обычный вызов функции, который связывается с глобальным объектом, а не с новым объектом.

Продолжить ответ Бобинса

В es6 теперь вы можете создать class

Итак, теперь вы можете сделать:

class Shape {
    constructor(x, y) {
        this.x = x;
        this.y = y;
    }

    toString() {
        return `Shape at ${this.x}, ${this.y}`;
    }
}

Таким образом, расширить круг (как в другом ответе) вы можете сделать:

class Circle extends Shape {
    constructor(x, y, r) {
        super(x, y);
        this.r = r;
    }

    toString() {
        let shapeString = super.toString();
        return `Circular ${shapeString} with radius ${this.r}`;
    }
}

Заканчивается немного чище в es6 и немного легче для чтения.


Вот хороший пример этого в действии:

class Shape {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }

  toString() {
    return `Shape at ${this.x}, ${this.y}`;
  }
}

class Circle extends Shape {
  constructor(x, y, r) {
    super(x, y);
    this.r = r;
  }

  toString() {
    let shapeString = super.toString();
    return `Circular ${shapeString} with radius ${this.r}`;
  }
}

let c = new Circle(1, 2, 4);

console.log('' + c, c);

Вы также можете сделать это таким образом, используя структуры:

function createCounter () {
    var count = 0;

    return {
        increaseBy: function(nb) {
            count += nb;
        },
        reset: function {
            count = 0;
        }
    }
}

Затем:

var counter1 = createCounter();
counter1.increaseBy(4);

Другим способом было бы http://jsfiddle.net/nnUY4/(я не знаю, следует ли этому виду обработки функций создания и раскрытия объекта какой-либо конкретной модели)

// Build-Reveal

var person={
create:function(_name){ // 'constructor'
                        //  prevents direct instantiation 
                        //  but no inheritance
    return (function() {

        var name=_name||"defaultname";  // private variable

        // [some private functions]

        function getName(){
            return name;
        }

        function setName(_name){
            name=_name;
        }

        return {    // revealed functions
            getName:getName,    
            setName:setName
        }
    })();
   }
  }

  // … no (instantiated) person so far …

  var p=person.create(); // name will be set to 'defaultname'
  p.setName("adam");        // and overwritten
  var p2=person.create("eva"); // or provide 'constructor parameters'
  alert(p.getName()+":"+p2.getName()); // alerts "adam:eva"

Создание объекта

Самый простой способ создать объект в JavaScript - это использовать следующий синтаксис:

var test = {
  a : 5,
  b : 10,
  f : function(c) {
    return this.a + this.b + c;
  }
}

console.log(test);
console.log(test.f(3));

Это прекрасно работает для хранения данных в структурированном виде.

Однако для более сложных случаев часто лучше создавать экземпляры функций:

function Test(a, b) {
  this.a = a;
  this.b = b;
  this.f = function(c) {
return this.a + this.b + c;
  };
}

var test = new Test(5, 10);
console.log(test);
console.log(test.f(3));

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

Однако это можно сделать более эффективно, используя прототип.

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

В нашем случае имеет смысл перенести метод f к прототипу:

function Test(a, b) {
  this.a = a;
  this.b = b;
}

Test.prototype.f = function(c) {
  return this.a + this.b + c;
};

var test = new Test(5, 10);
console.log(test);
console.log(test.f(3));

наследование

Простой, но эффективный способ сделать наследование в JavaScript, это использовать следующий двухслойный:

B.prototype = Object.create(A.prototype);
B.prototype.constructor = B;

Это похоже на это:

B.prototype = new A();

Основное различие между ними заключается в том, что конструктор A не запускается при использовании Object.create, который является более интуитивным и более похож на наследование на основе классов.

Вы всегда можете по желанию запустить конструктор A при создании нового экземпляра B добавив добавив его в конструктор B:

function B(arg1, arg2) {
    A(arg1, arg2); // This is optional
}

Если вы хотите передать все аргументы B в AВы также можете использовать Function.prototype.apply():

function B() {
    A.apply(this, arguments); // This is optional
}

Если вы хотите смешать другой объект в цепочку конструктора B Вы можете комбинировать Object.create с Object.assign:

B.prototype = Object.assign(Object.create(A.prototype), mixin.prototype);
B.prototype.constructor = B;

демонстрация

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

A.prototype = Object.create(Object.prototype);
A.prototype.constructor = A;

function B() {
  A.apply(this, arguments);
  this.street = "Downing Street 10";
}

B.prototype = Object.create(A.prototype);
B.prototype.constructor = B;

function mixin() {

}

mixin.prototype = Object.create(Object.prototype);
mixin.prototype.constructor = mixin;

mixin.prototype.getProperties = function() {
  return {
    name: this.name,
    address: this.street,
    year: this.year
  };
};

function C() {
  B.apply(this, arguments);
  this.year = "2018"
}

C.prototype = Object.assign(Object.create(B.prototype), mixin.prototype);
C.prototype.constructor = C;

var instance = new C("Frank");
console.log(instance);
console.log(instance.getProperties());


Заметка

Object.create может безопасно использоваться в любом современном браузере, включая IE9+. Object.assign не работает ни в одной версии IE, ни в некоторых мобильных браузерах. Рекомендуется заполнить Object.create и / или Object.assign если вы хотите использовать их и поддерживать браузеры, которые их не реализуют.

Вы можете найти полифилл для Object.create здесь и один для Object.assign здесь

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

Вот ванильный объект JavaScript:

function MyThing(aParam) {
    var myPrivateVariable = "squizzitch";

    this.someProperty = aParam;
    this.useMeAsACallback = function() {
        console.log("Look, I have access to " + myPrivateVariable + "!");
    }
}

// Every MyThing will get this method for free:
MyThing.prototype.someMethod = function() {
    console.log(this.someProperty);
};

Вы можете многое узнать о том, что Дуглас Крокфорд говорит о JavaScript. Джон Резиг также великолепен. Удачи!

Closure универсален. bobince хорошо суммировал подходы прототипа и замыкания при создании объектов. Однако вы можете имитировать некоторые аспекты OOP используя замыкание в функциональном программировании. Помните, что функции - это объекты в JavaScript; так что используйте функцию как объект по-другому.

Вот пример закрытия:

function outer(outerArg) {
    return inner(innerArg) {
        return innerArg + outerArg; //the scope chain is composed of innerArg and outerArg from the outer context 
    }
}

Некоторое время назад я наткнулся на статью Mozilla о закрытии. Вот что бросается в глаза: "Закрытие позволяет связать некоторые данные (среду) с функцией, которая работает с этими данными. Это имеет очевидные параллели с объектно-ориентированным программированием, где объекты позволяют нам связывать некоторые данные (свойства объекта).) одним или несколькими методами". Это был первый раз, когда я прочитал параллелизм между замыканием и классическим ООП без ссылки на прототип.

Как?

Предположим, вы хотите рассчитать НДС для некоторых товаров. НДС, вероятно, останется стабильным в течение срока действия заявки. Один из способов сделать это в ООП (псевдокод):

public class Calculator {
    public property VAT { get; private set; }
    public Calculator(int vat) {
        this.VAT = vat;
    }
    public int Calculate(int price) {
        return price * this.VAT;
    }
}

По сути, вы передаете значение НДС в свой конструктор, и ваш метод расчета может работать с ним через замыкание. Теперь вместо использования класса / конструктора передайте свой НДС в качестве аргумента в функцию. Поскольку единственное, что вас интересует - это сами вычисления, возвращает новую функцию, которая является методом вычисления:

function calculator(vat) {
    return function(item) {
        return item * vat;
    }
}
var calculate = calculator(1.10);
var jsBook = 100; //100$
calculate(jsBook); //110

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

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Closures

Шаблон, который хорошо мне служит
var Klass = function Klass() {
    var thus = this;
    var somePublicVariable = x
      , somePublicVariable2 = x
      ;
    var somePrivateVariable = x
      , somePrivateVariable2 = x
      ;

    var privateMethod = (function p() {...}).bind(this);

    function publicMethod() {...}

    // export precepts
    this.var1 = somePublicVariable;
    this.method = publicMethod;

    return this;
};

Во-первых, вы можете изменить ваши предпочтения добавления методов в экземпляр вместо конструктора prototype объект. Я почти всегда объявляю методы внутри конструктора, потому что я использую Constructor Hijacking очень часто в целях наследования и декораторов.

Вот как я решаю, куда пишутся декларации:

  • Никогда не объявляйте метод непосредственно в объекте контекста (this)
  • Позволять var декларации имеют приоритет над function декларации
  • Пусть примитивы имеют приоритет над объектами ({} а также [])
  • Позволять public декларации имеют приоритет над private декларации
  • предпочитать Function.prototype.bind над thus, self, vm, etc
  • Избегайте объявления класса в другом классе, кроме случаев, когда:
    • Должно быть очевидно, что они неразделимы
    • Внутренний класс реализует шаблон команд
    • Внутренний класс реализует шаблон Singleton
    • Внутренний класс реализует Государственный Шаблон
    • Внутренний класс реализует другой шаблон проектирования, который гарантирует это
  • Всегда возвращайся this изнутри Лексической Области Пространства Закрытия.

Вот почему они помогают:

Конструктор угон
var Super = function Super() {
    ...
    this.inherited = true;
    ...
};
var Klass = function Klass() {
    ...
    // export precepts
    Super.apply(this);  // extends this with property `inherited`
    ...
};
Модельный дизайн
var Model = function Model(options) {
    var options = options || {};

    this.id = options.id || this.id || -1;
    this.string = options.string || this.string || "";
    // ...

    return this;
};
var model = new Model({...});
var updated = Model.call(model, { string: 'modified' });
(model === updated === true);  // > true
Шаблоны проектирования
var Singleton = new (function Singleton() {
    var INSTANCE = null;

    return function Klass() {
        ...
        // export precepts
        ...

        if (!INSTANCE) INSTANCE = this;
        return INSTANCE;
    };
})();
var a = new Singleton();
var b = new Singleton();
(a === b === true);  // > true

Как видите, мне действительно не нужно thus так как я предпочитаю Function.prototype.bind (или же .call или же .apply ) над thus, В нашем Singleton класс, мы даже не называем это thus так как INSTANCE передает больше информации. За Model, мы возвращаемся this так что мы можем вызвать конструктор, используя .call чтобы вернуть экземпляр мы перешли в него. Избыточно, мы присвоили его переменной updated, хотя это полезно в других сценариях.

Кроме того, я предпочитаю конструировать объектные литералы, используя new ключевое слово над {скобками}:

Предпочтительные
var klass = new (function Klass(Base) {
    ...
    // export precepts
    Base.apply(this);  //
    this.override = x;
    ...
})(Super);
Не предпочитаемый
var klass = Super.apply({
    override: x
});

Как видите, последний не имеет возможности переопределить свойство override своего суперкласса.

Если я добавлю методы к классу prototype объект, я предпочитаю объектный литерал - с использованием или без использования new ключевое слово:

Предпочтительные
Klass.prototype = new Super();
// OR
Klass.prototype = new (function Base() {
    ...
    // export precepts
    Base.apply(this);
    ...
})(Super);
// OR
Klass.prototype = Super.apply({...});
// OR
Klass.prototype = {
    method: function m() {...}
};
Не предпочитаемый
Klass.prototype.method = function m() {...};

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

var test = {

  useTitle : "Here we use 'a Title' to declare an Object",
  'useString': "Here we use 'a String' to declare an Object",
  
  onTitle : function() {
    return this.useTitle;
  },
  
  onString : function(type) {
    return this[type];
  }
  
}

console.log(test.onTitle());
console.log(test.onString('useString'));

В дополнение к принятому ответу от 2009 года. Если вы можете ориентироваться на современные браузеры, можно использовать Object.defineProperty.

Метод Object.defineProperty() определяет новое свойство непосредственно для объекта или изменяет существующее свойство объекта и возвращает объект. Источник: Мозилла

var Foo = (function () {
    function Foo() {
        this._bar = false;
    }
    Object.defineProperty(Foo.prototype, "bar", {
        get: function () {
            return this._bar;
        },
        set: function (theBar) {
            this._bar = theBar;
        },
        enumerable: true,
        configurable: true
    });
    Foo.prototype.toTest = function () {
        alert("my value is " + this.bar);
    };
    return Foo;
}());

// test instance
var test = new Foo();
test.bar = true;
test.toTest();

Чтобы увидеть список совместимости с настольными компьютерами и мобильными устройствами, см . Список совместимости браузера Mozilla. Да, IE9+ поддерживает его так же, как Safari mobile.

Вы также можете попробовать это

    function Person(obj) {
    'use strict';
    if (typeof obj === "undefined") {
        this.name = "Bob";
        this.age = 32;
        this.company = "Facebook";
    } else {
        this.name = obj.name;
        this.age = obj.age;
        this.company = obj.company;
    }

}

Person.prototype.print = function () {
    'use strict';
    console.log("Name: " + this.name + " Age : " + this.age + " Company : " + this.company);
};

var p1 = new Person({name: "Alex", age: 23, company: "Google"});
p1.print();
var Person = function (lastname, age, job){
this.name = name;
this.age = age;
this.job = job;
this.changeName = function(name){
this.lastname = name;
}
}
var myWorker = new Person('Adeola', 23, 'Web Developer');
myWorker.changeName('Timmy');

console.log("New Worker" + myWorker.lastname);

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

//Constructor Pattern
function Person(name, age, job){
 this.name = name;
 this.age = age;
 this.job = job;
 this.doSomething = function(){
    alert('I am Happy');
}
}

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

var person1 = new Person('Arv', 30, 'Software');
person1.name //Arv

Ссылка: Профессиональный JS для веб-разработчиков - Nik Z

Другие вопросы по тегам