Могут ли классы / объекты JavaScript иметь конструкторы? Как они созданы?
Могут ли классы / объекты JavaScript иметь конструкторы? Как они созданы?
Ответы:
Использование прототипов:
function Box(color) // Constructor
{
this.color = color;
}
Box.prototype.getColor = function()
{
return this.color;
};
Сокрытие «color» (несколько напоминает приватную переменную-член):
function Box(col)
{
var color = col;
this.getColor = function()
{
return color;
};
}
Применение:
var blueBox = new Box("blue");
alert(blueBox.getColor()); // will alert blue
var greenBox = new Box("green");
alert(greenBox.getColor()); // will alert green
color
. Я бы предположил, что вы используете в значительной степени зависит от личных предпочтений (защита против простоты)
var
делает приватную переменную. this
делает публичную переменную
Foo
, тогда как в последнем случае она будет знать, что она Foo
вызывается. Очень полезно для отладки.
Вот шаблон, который я иногда использую для ООП-подобного поведения в JavaScript. Как видите, вы можете моделировать частные (как статические, так и экземпляры) члены, используя замыкания. То, new MyClass()
что вернётся, это объект с только свойствами, назначенными this
объекту и prototype
объекту «класса».
var MyClass = (function () {
// private static
var nextId = 1;
// constructor
var cls = function () {
// private
var id = nextId++;
var name = 'Unknown';
// public (this instance only)
this.get_id = function () { return id; };
this.get_name = function () { return name; };
this.set_name = function (value) {
if (typeof value != 'string')
throw 'Name must be a string';
if (value.length < 2 || value.length > 20)
throw 'Name must be 2-20 characters long.';
name = value;
};
};
// public static
cls.get_nextId = function () {
return nextId;
};
// public (shared across instances)
cls.prototype = {
announce: function () {
alert('Hi there! My id is ' + this.get_id() + ' and my name is "' + this.get_name() + '"!\r\n' +
'The next fellow\'s id will be ' + MyClass.get_nextId() + '!');
}
};
return cls;
})();
Меня спросили о наследовании с использованием этого шаблона, поэтому здесь идет:
// It's a good idea to have a utility class to wire up inheritance.
function inherit(cls, superCls) {
// We use an intermediary empty constructor to create an
// inheritance chain, because using the super class' constructor
// might have side effects.
var construct = function () {};
construct.prototype = superCls.prototype;
cls.prototype = new construct;
cls.prototype.constructor = cls;
cls.super = superCls;
}
var MyChildClass = (function () {
// constructor
var cls = function (surName) {
// Call super constructor on this instance (any arguments
// to the constructor would go after "this" in call(…)).
this.constructor.super.call(this);
// Shadowing instance properties is a little bit less
// intuitive, but can be done:
var getName = this.get_name;
// public (this instance only)
this.get_name = function () {
return getName.call(this) + ' ' + surName;
};
};
inherit(cls, MyClass); // <-- important!
return cls;
})();
И пример, чтобы использовать все это:
var bob = new MyClass();
bob.set_name('Bob');
bob.announce(); // id is 1, name shows as "Bob"
var john = new MyChildClass('Doe');
john.set_name('John');
john.announce(); // id is 2, name shows as "John Doe"
alert(john instanceof MyClass); // true
Как видите, классы корректно взаимодействуют друг с другом (они используют статический идентификатор MyClass
, announce
метод использует правильный get_name
метод и т. Д.)
Стоит отметить, что необходимо скрывать свойства экземпляра. Вы можете заставить inherit
функцию пройти через все свойства экземпляра (используя hasOwnProperty
), которые являются функциями, и автоматически добавить super_<method name>
свойство. Это позволит вам вызывать this.super_get_name()
вместо того, чтобы хранить его во временном значении и называть его связанным с помощью call
.
Для методов в прототипе вам не нужно беспокоиться о вышеупомянутом, хотя, если вы хотите получить доступ к методам прототипа суперкласса, вы можете просто вызвать this.constructor.super.prototype.methodName
. Если вы хотите сделать его менее подробным, вы, конечно, можете добавить удобные свойства. :)
cls.prototype
части: «общий доступ к экземплярам» предназначен только для чтения значения (вызова announce
). Если вы установите myClassInstance.announce
другое значение, оно создаст новое свойство myClassInstance
, поэтому оно будет применяться только к этому объекту, а не к другим экземплярам класса. Назначение MyClass.prototype.announce
будет влиять на все экземпляры, хотя.
MyClass.get_nextId()
Мне кажется, что большинство из вас приводит пример геттеров и сеттеров, а не конструктор, т.е. http://en.wikipedia.org/wiki/Constructor_(object-oriented_programming) .
Ланч-дан был ближе, но пример не работал в jsFiddle.
В этом примере создается функция частного конструктора, которая запускается только во время создания объекта.
var color = 'black';
function Box()
{
// private property
var color = '';
// private constructor
var __construct = function() {
alert("Object Created.");
color = 'green';
}()
// getter
this.getColor = function() {
return color;
}
// setter
this.setColor = function(data) {
color = data;
}
}
var b = new Box();
alert(b.getColor()); // should be green
b.setColor('orange');
alert(b.getColor()); // should be orange
alert(color); // should be black
Если вы хотите назначить открытые свойства, тогда конструктор можно определить так:
var color = 'black';
function Box()
{
// public property
this.color = '';
// private constructor
var __construct = function(that) {
alert("Object Created.");
that.color = 'green';
}(this)
// getter
this.getColor = function() {
return this.color;
}
// setter
this.setColor = function(color) {
this.color = color;
}
}
var b = new Box();
alert(b.getColor()); // should be green
b.setColor('orange');
alert(b.getColor()); // should be orange
alert(color); // should be black
Box()
функция :). Но этот пример, а также примеры в других ответах могут быть легко расширены для принятия параметров.
Box
функции, и вы готовы (это все еще "личное"). «Приватный» в Javascript означает просто доступ через лексическую область; нет необходимости назначать членам. Дополнительно: этот код неверен. Это создает глобальную __construct
переменную, что довольно плохо. var
следует использовать для ограничения сферы действия __construct
.
Так в чем же смысл свойства "конструктор"? Не можете понять, где это может быть полезно, какие-либо идеи?
Смысл свойства конструктора состоит в том, чтобы обеспечить некоторый способ притворяться, что JavaScript имеет классы. Одна из вещей, которую вы не можете использовать , это изменить конструктор объекта после его создания. Это сложно.
Я написал довольно обширную статью об этом несколько лет назад: http://joost.zeekat.nl/constructors-considered-mildly-confusing.html
Пример здесь: http://jsfiddle.net/FZ5nC/
Попробуйте этот шаблон:
<script>
//============================================================
// Register Namespace
//------------------------------------------------------------
var Name = Name||{};
Name.Space = Name.Space||{};
//============================================================
// Constructor - MUST BE AT TOP OF FILE
//------------------------------------------------------------
Name.Space.ClassName = function Name_Space_ClassName(){}
//============================================================
// Member Functions & Variables
//------------------------------------------------------------
Name.Space.ClassName.prototype = {
v1: null
,v2: null
,f1: function Name_Space_ClassName_f1(){}
}
//============================================================
// Static Variables
//------------------------------------------------------------
Name.Space.ClassName.staticVar = 0;
//============================================================
// Static Functions
//------------------------------------------------------------
Name.Space.ClassName.staticFunc = function Name_Space_ClassName_staticFunc(){
}
</script>
Вы должны настроить свое пространство имен, если вы определяете статический класс:
<script>
//============================================================
// Register Namespace
//------------------------------------------------------------
var Shape = Shape||{};
Shape.Rectangle = Shape.Rectangle||{};
// In previous example, Rectangle was defined in the constructor.
</script>
Пример класса:
<script>
//============================================================
// Register Namespace
//------------------------------------------------------------
var Shape = Shape||{};
//============================================================
// Constructor - MUST BE AT TOP OF FILE
//------------------------------------------------------------
Shape.Rectangle = function Shape_Rectangle(width, height, color){
this.Width = width;
this.Height = height;
this.Color = color;
}
//============================================================
// Member Functions & Variables
//------------------------------------------------------------
Shape.Rectangle.prototype = {
Width: null
,Height: null
,Color: null
,Draw: function Shape_Rectangle_Draw(canvasId, x, y){
var canvas = document.getElementById(canvasId);
var context = canvas.getContext("2d");
context.fillStyle = this.Color;
context.fillRect(x, y, this.Width, this.Height);
}
}
//============================================================
// Static Variables
//------------------------------------------------------------
Shape.Rectangle.Sides = 4;
//============================================================
// Static Functions
//------------------------------------------------------------
Shape.Rectangle.CreateSmallBlue = function Shape_Rectangle_CreateSmallBlue(){
return new Shape.Rectangle(5,8,'#0000ff');
}
Shape.Rectangle.CreateBigRed = function Shape_Rectangle_CreateBigRed(){
return new Shape.Rectangle(50,25,'#ff0000');
}
</script>
Пример реализации:
<canvas id="painting" width="500" height="500"></canvas>
<script>
alert("A rectangle has "+Shape.Rectangle.Sides+" sides.");
var r1 = new Shape.Rectangle(16, 12, "#aa22cc");
r1.Draw("painting",0, 20);
var r2 = Shape.Rectangle.CreateSmallBlue();
r2.Draw("painting", 0, 0);
Shape.Rectangle.CreateBigRed().Draw("painting", 10, 0);
</script>
Функции уведомления определяются как AB = function A_B (). Это должно облегчить отладку вашего скрипта. Откройте панель проверки элемента Chrome, запустите этот скрипт и разверните трассировку отладки:
<script>
//============================================================
// Register Namespace
//------------------------------------------------------------
var Fail = Fail||{};
//============================================================
// Static Functions
//------------------------------------------------------------
Fail.Test = function Fail_Test(){
A.Func.That.Does.Not.Exist();
}
Fail.Test();
</script>
Это конструктор:
function MyClass() {}
Когда вы делаете
var myObj = new MyClass();
MyClass
выполняется, и новый объект возвращается из этого класса.
alert(valuePassedInAsArgument);
и это будет выполняться один раз для каждого экземпляра, поэтому весь класс является самим конструктором.
new object is returned of that class
- разве это не похоже на возвращение нового объекта этой функции?
Я нашел этот урок очень полезным. Этот подход используется большинством плагинов jQuery.
var Class = function(methods) {
var klass = function() {
this.initialize.apply(this, arguments);
};
for (var property in methods) {
klass.prototype[property] = methods[property];
}
if (!klass.prototype.initialize) klass.prototype.initialize = function(){};
return klass;
};
Сейчас же ,
var Person = Class({
initialize: function(name, age) {
this.name = name;
this.age = age;
},
toString: function() {
return "My name is "+this.name+" and I am "+this.age+" years old.";
}
});
var alice = new Person('Alice', 26);
alert(alice.name); //displays "Alice"
alert(alice.age); //displays "26"
alert(alice.toString()); //displays "My name is Alice and I am 26 years old" in most browsers.
//IE 8 and below display the Object's toString() instead! "[Object object]"
klass
Эта модель хорошо послужила мне. С помощью этого шаблона вы создаете классы в отдельных файлах, загружая их в ваше общее приложение «по мере необходимости».
// Namespace
// (Creating new if not instantiated yet, otherwise, use existing and just add to it)
var myApp = myApp || {};
// "Package"
// Similar to how you would establish a package in other languages
(function() {
// "Class"
var MyClass = function(params) {
this.initialize(params);
}
// "Private Static" vars
// - Only accessible to functions in this class.
// - Doesn't get wiped out when we create a new instance.
var countInstances = 0;
var allInstances = [];
// "Private Static" functions
// - Same as above, but it's a function accessible
// only to other functions in this class.
function doSomething(){
}
// "Public Static" vars
// - Everyone has access.
// - Doesn't get wiped out when we create a new instance.
MyClass.counter = 0;
// "Public Static" functions
// - Same as above, but anyone can call this "static method".
// - Kinda like a singleton class situation.
MyClass.foobar = function(){
}
// Public properties and methods are built into the "prototype"
// - This is how each instance can become unique unto itself.
// - Establishing "p" as "local" (Static Private) variable
// simply so we don't have to keep typing "MyClass.prototype"
// for each property and function.
var p = MyClass.prototype;
// "Public" vars
p.id = null;
p.firstname = null;
p.lastname = null;
// "Private" vars
// - Only used by "this" instance.
// - There isn't "true" privacy for each
// instance so we have to fake it.
// - By tradition, we indicate "privacy"
// by prefixing it with an underscore.
// - So technically, anyone can access, but we simply
// don't tell anyone about it (e.g. in your API)
// so no one knows about it :)
p._foo = null;
p.initialize = function(params){
this.id = MyClass.counter++;
this.firstname = params.firstname;
this.lastname = params.lastname;
MyClass.counter++;
countInstances++;
allInstances.push(this);
}
p.doAlert = function(theMessage){
alert(this.firstname + " " + this.lastname + " said: " + theMessage + ". My id:" + this.id + ". Total People:" + countInstances + ". First Person:" + allInstances[0].firstname + " " + allInstances[0].lastname);
}
// Assign class to app
myApp.MyClass = MyClass;
// Close the "Package"
}());
// Usage example:
var bob = new myApp.MyClass({ firstname : "bob",
lastname : "er"
});
bob.doAlert("hello there");
var
в конструкторе (или аргумент функции, или в функции, подобной конструктору).
Да, вы можете определить конструктор внутри объявления класса следующим образом:
class Rectangle {
constructor(height, width) {
this.height = height;
this.width = width;
}
}
Я предполагаю, что я отправлю то, что я делаю с закрытием javascript, так как никто еще не использует закрытие.
var user = function(id) {
// private properties & methods goes here.
var someValue;
function doSomething(data) {
someValue = data;
};
// constructor goes here.
if (!id) return null;
// public properties & methods goes here.
return {
id: id,
method: function(params) {
doSomething(params);
}
};
};
Комментарии и предложения к этому решению приветствуются. :)
Используя приведенный выше пример Ника, вы можете создать конструктор для объектов без параметров, используя оператор return в качестве последнего оператора в определении вашего объекта. Верните функцию конструктора, как показано ниже, и она будет запускать код в __construct при каждом создании объекта:
function Box()
{
var __construct = function() {
alert("Object Created.");
this.color = 'green';
}
this.color = '';
this.getColor = function() {
return this.color;
}
__construct();
}
var b = new Box();
this.getColor();
в строке выше, alert("Object Created.");
ничего не будет предупреждено. Там будет ошибка, как «getColor не определен». Если вы хотите, чтобы конструктор мог вызывать другие методы объекта, он должен быть определен после всех других методов. Поэтому вместо вызова __construct();
последней строки просто определите конструкцию там и поставьте ()
после нее, чтобы принудительно выполнить ее автоматически.
()
в конец определения __construct все равно привело к ошибке. Мне пришлось звонить __construct();
по собственной линии, как в оригинальном коде, чтобы избежать ошибки.
Может быть, это стало немного проще, но вот то, что я придумал сейчас в 2017 году:
class obj {
constructor(in_shape, in_color){
this.shape = in_shape;
this.color = in_color;
}
getInfo(){
return this.shape + ' and ' + this.color;
}
setShape(in_shape){
this.shape = in_shape;
}
setColor(in_color){
this.color = in_color;
}
}
При использовании класса выше, у меня есть следующее:
var newobj = new obj('square', 'blue');
//Here, we expect to see 'square and blue'
console.log(newobj.getInfo());
newobj.setColor('white');
newobj.setShape('sphere');
//Since we've set new color and shape, we expect the following: 'sphere and white'
console.log(newobj.getInfo());
Как видите, конструктор принимает два параметра, и мы устанавливаем свойства объекта. Мы также изменяем цвет и форму объекта, используя setter
функции, и докажем, что его изменение осталось после вызова getInfo()
после этих изменений.
Немного поздно, но я надеюсь, что это поможет. Я проверил это с помощью mocha
юнит-тестирования, и оно работает хорошо.
Они делают, если вы используете Typescript - с открытым исходным кодом от MicroSoft :-)
class BankAccount {
balance: number;
constructor(initially: number) {
this.balance = initially;
}
deposit(credit: number) {
this.balance += credit;
return this.balance;
}
}
Typescript позволяет вам «подделывать» OO-конструкции, которые скомпилированы в javascript-конструкции. Если вы начинаете большой проект, он может сэкономить вам много времени, и он только что достиг версии milestone 1.0.
http://www.typescriptlang.org/Content/TypeScript%20Language%20Specification.pdf
Приведенный выше код «скомпилирован» для:
var BankAccount = (function () {
function BankAccount(initially) {
this.balance = initially;
}
BankAccount.prototype.deposit = function (credit) {
this.balance += credit;
return this.balance;
};
return BankAccount;
})();
В JavaScript тип вызова определяет поведение функции:
func()
obj.func()
new func()
func.call()
илиfunc.apply()
Функция вызывается как конструктор при вызове с использованием new
оператора:
function Cat(name) {
this.name = name;
}
Cat.prototype.getName = function() {
return this.name;
}
var myCat = new Cat('Sweet'); // Cat function invoked as a constructor
Любой экземпляр или прототип объекта в JavaScript имеет свойство constructor
, которое ссылается на функцию конструктора.
Cat.prototype.constructor === Cat // => true
myCat.constructor === Cat // => true
Проверьте этот пост о свойстве конструктора.
При использовании большого шаблона Blixt, описанного выше, я обнаружил, что он не очень хорошо работает с многоуровневым наследованием (MyGrandChildClass, расширяющий MyChildClass, расширяющий MyClass) - он циклически вызывает конструктор первого родителя снова и снова. Итак, вот простой обходной путь - если вам нужно многоуровневое наследование, вместо использования this.constructor.super.call(this, surName);
use chainSuper(this).call(this, surName);
с функцией chain, определенной следующим образом:
function chainSuper(cls) {
if (cls.__depth == undefined) cls.__depth = 1; else cls.__depth++;
var depth = cls.__depth;
var sup = cls.constructor.super;
while (depth > 1) {
if (sup.super != undefined) sup = sup.super;
depth--;
}
return sup;
}
http://www.jsoops.net/ достаточно хорош для упс в Js. Если предоставить частную, защищенную, публичную переменную и функцию, а также функцию наследования. Пример кода:
var ClassA = JsOops(function (pri, pro, pub)
{// pri = private, pro = protected, pub = public
pri.className = "I am A ";
this.init = function (var1)// constructor
{
pri.className += var1;
}
pub.getData = function ()
{
return "ClassA(Top=" + pro.getClassName() + ", This=" + pri.getClassName()
+ ", ID=" + pro.getClassId() + ")";
}
pri.getClassName = function () { return pri.className; }
pro.getClassName = function () { return pri.className; }
pro.getClassId = function () { return 1; }
});
var newA = new ClassA("Class");
//***Access public function
console.log(typeof (newA.getData));
// function
console.log(newA.getData());
// ClassA(Top=I am A Class, This=I am A Class, ID=1)
//***You can not access constructor, private and protected function
console.log(typeof (newA.init)); // undefined
console.log(typeof (newA.className)); // undefined
console.log(typeof (newA.pro)); // undefined
console.log(typeof (newA.getClassName)); // undefined
просто чтобы предложить немного разнообразия. ds.oop - это хороший способ объявить классы с конструкторами в javascript. Он поддерживает все возможные типы наследования (включая 1 тип, который даже не поддерживает c #), а также интерфейсы, что приятно.
var Color = ds.make.class({
type: 'Color',
constructor: function (r,g,b) {
this.r = r; /* now r,g, and b are available to */
this.g = g; /* other methods in the Color class */
this.b = b;
}
});
var red = new Color(255,0,0); // using the new keyword to instantiate the class
Здесь мы должны обратить внимание на один момент в java-скрипте: это язык без классов, однако мы можем достичь этого, используя функции в java-скрипте. Наиболее распространенный способ достижения этого - создать функцию в сценарии java, использовать новое ключевое слово для создания объекта и использовать это ключевое слово для определения свойства и методов. Ниже приведен пример.
// Function constructor
var calculator=function(num1 ,num2){
this.name="This is function constructor";
this.mulFunc=function(){
return num1*num2
};
};
var objCal=new calculator(10,10);// This is a constructor in java script
alert(objCal.mulFunc());// method call
alert(objCal.name);// property call
//Constructors With Prototypes
var calculator=function(){
this.name="Constructors With Prototypes";
};
calculator.prototype.mulFunc=function(num1 ,num2){
return num1*num2;
};
var objCal=new calculator();// This is a constructor in java script
alert(objCal.mulFunc(10,10));// method call
alert(objCal.name); // property call
В большинстве случаев вам нужно как-то объявить необходимое свойство, прежде чем вы сможете вызвать метод, передающий эту информацию. Если вам не нужно изначально устанавливать свойство, вы можете просто вызвать метод внутри объекта следующим образом. Вероятно, не самый красивый способ сделать это, но это все еще работает.
var objectA = {
color: '';
callColor : function(){
console.log(this.color);
}
this.callColor();
}
var newObject = new objectA();