Чи можуть класи / об’єкти JavaScript мати конструктори? Як вони створені?
Чи можуть класи / об’єкти JavaScript мати конструктори? Як вони створені?
Відповіді:
Використання прототипів:
function Box(color) // Constructor
{
this.color = color;
}
Box.prototype.getColor = function()
{
return this.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
викликають. Дуже корисно для налагодження.
Ось шаблон, який я іноді використовую для поведінки, подібної до OOP в 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()
Мені здається, більшість із вас наводить приклад getters та setters, а не конструктор, тобто http://en.wikipedia.org/wiki/Constructor_(object-oriented_programming) .
lunched-dan був ближче, але приклад не працював у 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 = функція 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-конструкти. Якщо ви починаєте великий проект, це може заощадити вам багато часу, і він лише досягнув версії 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);
використовувати chainSuper(this).call(this, surName);
з функцією ланцюга, визначеною так:
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/ дуже добре підходить для oop в 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();