8c1dea14

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


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


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


В таблице даны определения Java и JavaScript для этих объектов.

JavaScriptJava

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, Вам нужно добавить дополнительный код в конструктор функции.

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


Рисунок 8.6&nbsp&nbsp Специфицирование свойств в конструкторе, шаг 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.


  • Оператор new передаёт новый объект Engineer-конструктору как значение ключевого слова this.


  • Конструктор создаёт новое свойство base для этого объекта и присваивает значение конструктора WorkerBee свойству base. Это делает конструктор WorkerBee методом Engineer-объекта.

    Имя свойства base не является специальным. Вы можете использовать любое правильное имя свойства; base просто более понятно в данной ситуации.



  • Конструктор вызывает метод base, передавая в качестве его аргументов два из аргументов, переданных конструктору ("Doe, Jane" и ["navigator", "javascript"]), а также строку "engineering". Явное использование "engineering" в конструкторе указывает, что все Engineer-объекты имеют одинаковые значения для наследуемого свойства dept и это значение переопределяет значение, унаследованное от Employee.


  • Поскольку base является методом в Engineer, внутри вызова base JavaScript связывает ключевое слово this с объектом, созданным в . Таким образом, функция WorkerBee, в свою очередь, передаёт аргументы "Doe, Jane" и ["navigator", "javascript"] конструктору функции Employee. После возвращения из конструктора функции Employee, функция WorkerBee использует остальные аргументы для установки свойства projects.


  • После возвращения из метода base, конструктор Engineer инициализирует свойство machine объекта значением  "belau".


  • После возвращения из конструктора, 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".





    Содержание раздела