Wiki-учебник по веб-технологиям: JavaScript/БолееГибкиеКонструкторы ...

Главная | |

Более гибкие конструкторы


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

Специфицирование свойств в конструкторе, шаг 1



В таблице даны определения Java и JavaScript для этих объектов.
JavaScript
Java
function Employee (name, dept) {
this.name = name || "";
this.dept = dept || "general";
}

public class Employee {
   public String name;
   public String dept;
   public Employee () {
      this("", "general");
   }
   public Employee (name) {
      this(name, "general");
   }
   public Employee (name, dept) {
      this.name = name;
      this.dept = dept;
   }
}
function WorkerBee (projs) {
this.projects = projs || [];
}
WorkerBee.prototype = new Employee;

public class WorkerBee extends Employee {
   public String[] projects;
   public WorkerBee () {
      this(new String[0]);
   }
   public WorkerBee (String[] projs) {
      this.projects = projs;
   }
}
function Engineer (mach) {
   this.dept = "engineering";
   this.machine = mach || "";
}
Engineer.prototype = new WorkerBee;

public class Engineer extends WorkerBee {
   public String machine;
   public WorkerBee () {
      this.dept = "engineering";
      this.machine = "";
   }
   public WorkerBee (mach) {
      this.dept = "engineering";
      this.machine = mach;
   }
}

Эти определения JavaScript используют специальную идиому для установки значений по умолчанию:

this.name = name || "";


Операция JavaScript «логическое ИЛИ» (||) вычисляет свой первый аргумент. Если он конвертируется в true, операция возвращает его. Иначе, операция возвращает значение второго аргумента. Следовательно, эта строка кода проверяет, имеет ли name используемое значение для свойства name. Если это так, в this.name устанавливается это значение. В ином случае, в this.name устанавливается пустая строка. В этой главе используется эта идиома используется для краткости; однако это может на первый взгляд показаться непонятным.

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

jane = new Engineer("belau");


Jane-свойства теперь:

jane.name == "";
jane.dept == "general";
jane.projects == [];
jane.machine == "belau"


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

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

Специфицирование свойств в конструкторе, шаг 2



Давайте рассмотрим одно из этих определений подробнее. Вот новое определение конструктора Engineer:

function Engineer (name, projs, mach) {
this.base = WorkerBee;
this.base(name, "engineering", projs);
this.machine = mach || "";
}


Предположим, вы создаёте новый Engineer-объект:

jane = new Engineer("Doe, Jane", ["navigator", "javascript"], "belau");


JavaScript выполняет следующие шаги:

  1. Оператор new создаёт общий объект и устанавливает в его свойство proto значение Engineer.prototype.
  2. Оператор new передаёт новый объект Engineer-конструктору как значение ключевого слова this.
  3. Конструктор создаёт новое свойство base для этого объекта и присваивает значение конструктора WorkerBee свойству base. Это делает конструктор WorkerBee методом Engineer-объекта.
    Имя свойства base не является специальным. Вы можете использовать любое правильное имя свойства; base просто более понятно в данной ситуации.
  4. Конструктор вызывает метод base, передавая в качестве его аргументов два из аргументов, переданных конструктору (“Doe, Jane” и ["navigator”, «javascript"]), а также строку “engineering”. Явное использование “engineering” в конструкторе указывает, что все Engineer-объекты имеют одинаковые значения для наследуемого свойства dept и это значение переопределяет значение, унаследованное от Employee.
  5. Поскольку base является методом в Engineer, внутри вызова base JavaScript связывает ключевое слово this с объектом, созданным в Шаге 1. Таким образом, функция WorkerBee, в свою очередь, передаёт аргументы “Doe, Jane” и ["navigator”, «javascript"] конструктору функции Employee. После возвращения из конструктора функции Employee, функция WorkerBee использует остальные аргументы для установки свойства projects.
  6. После возвращения из метода base, конструктор Engineer инициализирует свойство machine объекта значением “belau”.
  7. После возвращения из конструктора, JavaScript присваивает новый объект переменной jane.

Вы можете подумать, что, имея вызов конструктора WorkerBee из конструктора Engineer, вы установили соответствующее наследование для Engineer-объектов, но это не так. Вызов конструктора WorkerBee гарантирует, что Engineer-объект стартует со свойствами, специфицированными во всех конструкторах функций, которые были вызваны. Однако, если вы позднее добавите свойства к прототипам Employee или WorkerBee, эти свойства не будут наследоваться Engineer-объектом. Например, мы имеем следующие операторы:

function Engineer (name, projs, mach) {
this.base = WorkerBee;
this.base(name, "engineering", projs);
this.machine = mach || "";
}
jane = new Engineer("Doe, Jane", ["navigator", "javascript"], "belau");
Employee.prototype.specialty = "none";


Объект jane не наследует свойство specialty. Вы всё ещё должны явно установить прототип, чтобы гарантировать динамическое наследование. Предположим, у нас есть такие операторы:

function Engineer (name, projs, mach) {
this.base = WorkerBee;
this.base(name, "engineering", projs);
this.machine = mach || "";
}
Engineer.prototype = new WorkerBee;
jane = new Engineer("Doe, Jane", ["navigator", "javascript"], "belau");
Employee.prototype.specialty = "none";


Теперь значение свойства specialty объекта jane установлено в “none”.