betacode

Техника симулирования класса и наследственности в JavaScript

  1. Класс в Javascript
  2. Класс и наследственность в ES3
  3. Класс и наследственность в ES5

1. Класс в Javascript

Javascript это на самом деле мир функций и объектов. Первоначально он разработан просто, без четкого принципа о классе. Возможно создатели Javascript и не могли подумать что в один день, этот язык будет использоваться так распространенно.
Объект в Javascript сущность с несколькими парами "Ключ/Значение", и вы можете получить доступ в значения через объект и ключ.
object-example1.js
var tom =  {
  name: "Tom",
  country: "USA"
};
// Access:
console.log( tom.name ); // Tom
console.log( tom.country ); // USA
console.log( tom["name"] ); // Tom
console.log( tom["country"] ); // USA
Вы можете добавить новый "Ключ/Значение" в готовый объект или удалить его определенную пару "Ключ/Значение".
object-example2.js
var tom =  {
  name: "Tom",
  country: "USA"
};
// Delete property - country
delete tom["country"]; // Same as: delete tom.country;
// Add property - gender
tom["gender"] = "Male"; // Same as: tom.gender = "Male"; 
// Access:
console.log( tom.name ); // Tom
console.log( tom["name"] ); // Tom

console.log( tom["country"] ); // undefined
console.log( tom.country ); // undefined

console.log( tom["gender"] ); // Male
console.log( tom.gender ); // Male
Класс это современная концепция в таких языках, как Java, C#,... Класс это дизайн, который помогает быстро создавать объекты с одинаковой структурой. Первая версия Javascript не имеет данную концепцию.
Javascript становится все более и более важным, поэтому его необходимо обновить. Дизайнеры Javascript пытаются смоделировать концепцию Класс, основываясь на готовые концепции в Javascript. Синтаксис симулирования класса представлен в ES3, ES5, но только дойдя до ES6 мы получаем современный синтаксис и удовлетворяем всех.
Сначала, для упрощения рассмотри современный синтаксис представленный в ECMAScript 6 чтобы создать класс Rectangle (Прямоугольник), с 2-мя свойствами (property) width (ширина) и height (длина). Данный класс имеет метод getArea() возвращает площадь данного прямоугольника.
es6-class-example.js
// ECMAScript 6 class:
class Rectangle  {
  constructor (width , height)  {
      this.width = width;
      this.height = height;
  }
  getArea()  {
    return this.width * this.height;
  }
}
// ------------- TEST -------------------
var rect = new Rectangle(10, 5);
var area = rect.getArea();

console.log("Width: " + rect.width);
console.log("Height: " + rect.height);
console.log("Area: " + area);
Создать подкласс класс очень просто с ES6:
es6-inheritance-example.js
class Shape  {
    constructor( x, y) {
        this.setLocation(x, y);
    }
    setLocation(x, y) {
        this.x = x;
        this.y = y;
    }
}
// Subclass:
class Circle extends Shape {
    constructor(x, y, radius) {
        // Call Shape's constructor via super
        super(x, y);
        this.radius = radius;
    }
    getArea() {
      return Math.PI * this.radius * this.radius;
    }
}
// ----- TEST ---- 
var circle = new Circle(0, 2, 5);
console.log( circle.getArea() );
ES6 представил современный синтаксис для создания класса и подклассов, но техника на самом деле не поменялась. Синтаксис у ES6 скрыл все неясные понятия у Javascript когда он пытается симулировать класс. В данной статье мы обсудим с вами способ, который используется ES3 & ES5 для сиумилрования класса и наследственность.

2. Класс и наследственность в ES3

ES3 использует ключевое слово function для определения "конструктор объекта". Вы создаете новый объект, когда вы вызываете данную функцию с помощью оператора new:
// ECMAScript 3 class.
function Rectangle(width, height)  {
   this.width = width;
   this.height = height;
}
// Create an Object:
var rect = new Rectangle(10, 5);
Изображение ниже иллюстрирует действие выполненное исполняющей машиной Javascript:
  • Объект 'rect' создан (он является обычным объектом, ничего особенного)
  • Добавить свойство (property) __proto__ объекту 'rect', это скрытое свойство.
  • Переменная this будет направлена на адрес объекта 'rect'.
  • Добавить свойство (property) width объекту 'rect'.
  • Добавить свойство (property) height объекту 'rect'.
Концепция prototype будет представлена в ES3. Рассмотрим какова его цель?
// ECMAScript 3 class.
function Rectangle(width, height)  {
   this.width = width;
   this.height = height;
}
Rectangle.prototype.bgColor = "red";
Rectangle.prototype.borderColor = "blue";

// Create an Object:
var rect = new Rectangle(10, 5); 

console.log(rect); // Rectangle { width: 10, height: 5 }
console.log(rect.__proto__); // Rectangle { bgColor: 'red',borderColor: 'blue' }
console.log(rect.__proto__.bgColor); // red
console.log(rect.__proto__.borderColor); // blue
// (Read the explanation**)
console.log(rect.bgColor); // red
console.log(rect.borderColor); // blue
Что происходит когда машина выполняющая Javascript встречает выражение myObject.myProperty?
Ответ: Он проверяет имеет ли объект myObject свойство (property) myProperty или нет, если есть то он получает доступ в данное свойство, наоборот он получает доступ в myObject.__proto__.myProperty.
es3-proto-example3.js
var rect =  {
   __proto__ : { bgColor : "red", borderColor : "blue" },
   width: 10,
   height: 5
}
console.log(rect.width); // 10
console.log(rect.__proto__.bgColor); // red
console.log(rect.bgColor); // red
new AFunction(args) vs AFunction.call(anObj, args)
Наследственность
Для ES3 вы можете симулировать наследственный класс другого класса разными способами, но это не просто как вы это делаете в ES6, и довольно непонятен многим программистам.
Для простоты, я дам пример наследственности, и анализ правила работы исполняющей машины Javascript в данном случае.
  • Класс Animal имеет 2 свойтсва (property) name & gender.
  • Класс Cat наследует от класса Animal, имеет 1 свойство color.
es3-inheritance-example1.js
function Animal(n, g) {
    this.name = n;
    this.gender = g;
} 
function Cat(n, g, c) {
    Animal.call(this, n, g);
    this.color = c;
}
var tom = new Cat("Male", "Tom", "Black");
// Cat { gender: 'Male', name: 'Tom', color: 'Black' }
console.log(tom);
Наследственность и роль прототипа (ES3)
es3-inheritance-example2.js
// Class: Animal
function Animal(n, g) {
    this.name = n;
    this.gender = g;
}
Animal.prototype.sleep = function()  {
    console.log("Animal sleeping..");
}
Animal.prototype.move = function()  {
    console.log("Animal moving..");
}
// Class: Cat
function Cat(n, g, c) {
    Animal.call(this, n, g); // IMPORTANT!!
    this.color = c;
}
// IMPORTANT!!
var TempFunc = function() {}; // Temporary class.
TempFunc.prototype = Animal.prototype;
Cat.prototype = new TempFunc();
// ------------------
Cat.prototype.cry = function()  {
    console.log("Meo meo");
}
// Override 'move' method of Animal.
Cat.prototype.move = function() {
    console.log("Cat moving..");
}
var tom = new Cat("Male", "Tom", "Black");
// Cat { gender: 'Male', name: 'Tom', color: 'Black' }
console.log(tom);
tom.move(); // Cat moving..
tom.sleep(); // Animal sleeping..
tom.cry(); // Meo meo

3. Класс и наследственность в ES5

Object.create(srcObject)
Метод Object.create(srcObject) создает новый объект newObject, при этом newObject.__proto__ является копией у srcObject.
method-object-create-example.js
var john = {
  name: "John",
  gender: "Male"
};
var other = Object.create(john); 
console.log(other.__proto__); // { name: 'John', gender: 'Male' }
Наследственность (ES5 + Object.create)
Использование метода Object.create(srcObject) у ES5 помогает вам легче симулировать класс и наследственность по сравнению с ES3.
es5-inheritance-example1.js
// Class: Animal
function Animal(n, g) {
    this.name = n;
    this.gender = g;
}
Animal.prototype.sleep = function()  {
    console.log("Animal sleeping..");
}
Animal.prototype.move = function()  {
    console.log("Animal moving..");
}
// Class: Cat
function Cat(n, g, c) {
    Animal.call(this, n, g); // IMPORTANT!!
    this.color = c;
}
Cat.prototype = Object.create(Animal.prototype); // IMPORTANT!!
Cat.prototype.cry = function()  {
    console.log("Meo meo");
}
// Override 'move' method of Animal.
Cat.prototype.move = function() {
    console.log("Cat moving..");
}
var tom = new Cat("Male", "Tom", "Black");
// Cat { gender: 'Male', name: 'Tom', color: 'Black' }
console.log(tom);

tom.move(); // Cat moving..
tom.sleep(); // Animal sleeping..
tom.cry(); // Meo meo

Pуководства ECMAScript, Javascript

Show More