КОнтакты, предложения, отзывы

.rar или .zip
Вложение
Онлайн-чат
На этом сайте, вы найдёте полезную информацию, практические советы в области веб-программирования, веб-дизайна и веб-разработок в целом. Мы с удовольствием поделимся с вами реальными примерами и решениями задач, связанных с jQuery , JavaScript , PHP и MySQL , версткой сайтов , поможем разобраться новичкам с современными технологиями, такими как Ajax , HTML5 , CSS3 и многими другими.

Классы и обьекты JavaScript

Здравствуйте. В отличие от PHP, освоить объектно-ориентированные возможности которого у меня не составило большого труда, ООП в языке JavaScript несколько сложнее и туманнее, как мне показалось. К тому же имеет несколько вариантов развития событий. Итак, бдиже к сути..

Объекты

Объекты в языке JavaScript представляют собой набор свойств, представляющие собой пары имя : значение, разделяемые запятыми:
var Fraidz = {
  name: 'Fraidz',
  'Dev. skills': ['java', 'C++'],
  // это свойство называется методом объекта
  work: function(){
    return 'Cyberforum.ru';
  },
  age: 17
}
console.log(Fraidz.name);// обращение через идентификатор
console.log(Fraidz['Dev. skills']);// можем обращаться и так к свойству. Этот вариант полезен тем, что позволяет формировать свойство динамически, а идентификатор должен быть прописан в коде программы, иначе никак.
console.log(Fraidz.work());

// мы можем в ходе программы добавлять или изменять свойства ::
Fraidz.age = 19;
Fraids.dolg = 'два ряба';
console.log(Fraidz);

// также можно создавать объекты с помощью конструктора new Object()
var sash = new Object();
sash.skills = [];
// про конструкторы мы поговорим дальше

// в ECMAScript5 появилась возможность создавать объекты с помощью статического метода create класса Object, который принимает в качестве аргумента объект, который будет являться прототипом создаваемого.
var obj1 = Object.create(null);
var obj2 = Object.create({x:1, y:2});
var obj3 = Object.create({x:1, y:3});

Объекты в яваскрипт очень похожи на ассоциативные массивы PHP.

Удаление свойств

Для Удаления свойств используется оператор delete:
delete obj2.x;// удалить можно только родные свойства
Для проверки наличия свойства в объекте применяется оператор in :
var obj3 = Object.create({x:1, y:3});
console.log('y' in obj3);// true
  
// Цикл for .. in для объектов
for (key in object){
    console.log(key + ': ' + object['key']);
}
  
console.log(Object.keys(person));// возвращает массив всех ключей объекта

this и непрямой вызов методов

ключевое слово this в объекте указывает на сам объект.
var person = {
  name: 'John',
  sayHi = function(){
    alert(this.name);
  }
}

/*Мы можем объявить функцию в любом месте программы и просто оставить в объекте ссылку на неё, причём ершы (this :)) всё равно будет указывать на обьект :: */
var sayHi = function(){
    alert(this.name);
  },
  person = {
    name: 'John',
    sayHi: sayHi
  };

ключевое слово this вне всяких функций указывает на глобальный объект window. Вызов метода с указанием на какой объект указывает this, осуществляется с помощью методов call и apply :
var sayHi = function(){
    alert(this.name);
  },
  person = {
    name: 'John',
    sayHi: sayHi
  },
  anotherPerson = {
    name: 'Bob',
    sayHi: sayHi
  };
console.log(person.sayHi());// John
console.log(anotherPerson.sayHi());// Bob
console.log(anotherPerson.sayHi.call(person));// John

// Если функция sayHi принимает аргументы, мы можем прописывать их после указания объекта, на который будет указывать this через запятую в случае с call или в виде массива с apply:
console.log(anotherPerson.sayHi.call(person, 'sash'));
console.log(anotherPerson.sayHi.apply(person, ['sash']));    

/*Метод bind не вызывает функцию, а просто связывает её с каким-то объектом, на который будет указывать this при её вызове*/
var bound = sayHi.bind(anotherPerson);
console.log(bound());

Аксессоры и атрибуты свойств

Для аксессоров в объектном литерале предусмотрен специальный синтаксис. Функции get и set называют соответственно геттером и сеттером и они вызываются при попытке получить доступ или изменить свойство соответственно.
var person = {
  name: 'sash',
  _age: 27,
  get age(){
    return this._age;
  },
  set age(value){
    this._age = value < 0 ? 0 : value > 122 ? 122 : value;// сделаем так, чтобы невозможно было установит возраст меньше нуля или больше 122
  }
}    
console.log(person.age);// вызывается геттер
person.age = 22;// вызывается сеттер
Помимо имени и значения, каждое свойство объекта имеет три атрибута:
writable - доступность изменения атрибутов свойства
enumerable - для перечисления
configurable - для изменения
Также через аттрибут value доступно значение свойства
Их можно изменять с помощью объекта, который называется дескриптором свойства. Здесь всё довольно просто, чтобы получить этот объект, достаточно воспользоваться статическим методом getOwnPropertyDescriptor класса Object:
var own = Object.getOwnPropertyDescriptor(person, 'name');
console.log(own);

// Определение или Переопределение аттрибутов:
Object.defineProperty(person, 'gender', {
  value: 'male',
  writable: false,
  configurable: false,
  enumerable: false
});

// Определение или Переопределение аттрибутов сразу нескольких свойств:
Object.definePropertys(person, {
  x:{
    value: 'x',
    enumerable: false
  },
  y:{
    value: 'y',
    configurable:false
  }
});
Для проверки расширяемости объекта (возможности добавления новых свойств) используется метод isExtensible класса объект:
var obj ={};
console.log(Object.isExtensible(obj));// true
object.preventExtensions(obj);// делает объект нерасширяемым

Прототипы и наследование

При создании сценария, в котором используются объекты, необходимо спроектировать структуру из данных и кода, называемую классом. Каждый новый объект, основанный на конкретном классе, называется экземпляром класса.
Класс в языке яваскрипт - это чисто условное понятие, которое подразумевает множество всех обьектов, которые наследуют свои свойства от одного прототипа.
Если в вашей программе вам придётся создавать множество однотипных объектов, то каждый раз устанавливать их свойства было бы совсем не по-человечески и ваш код увеличился бы значительно. Вместо этого в объекте, который будет прототипом всех создаваемых на его основе нужно создать функцию constructor, которая будет принимать параметры и присваивать их соответствующим свойствам:
var Person = {
  constructor: function(name, age, gender){
    this.name = name;
    this.age = age;
    this.gender = gender;
    return this;
  },
  sayHi: function(){
    alert('Hi, my name is ' + this.name);
  }
}

var person = Object.create(Person).constructor('Fack', 3, 'female');
Для реализации дочерних классов нужно создать их объект-прототип. Допустим, мы хотим сделать "класс" WebDeveloper дочерним классом класса Person, но не хотим заново переписывать его конструктор:
var WebDeveloper = Object.create(Person);
WebDeveloper.constructor = function(name, age, gender, skills){
  Person.constructor.apply(this, arguments);
  this.skills = skills || [];
  return this;
};
var developer = Object.create(WebDeveloper).constructor('Den', 39, 'malle', ['WebAll']);

Традиционная модель реализации классов

Конструктор в языке JavaScript - это обычная функция, которая, будучи вызвана с ключевым словом new, создаёт объект, наследующий свойства, в ней объявленные :
var Person, person, Developer;

Person = function(name, age){
  this.name = name;  
  this.age = age;  
};

person = new Person('Lazy_Den', 11);
Конструкторы принято называть с большой буквы и ключевое слово this в конструкторе указывает на новый создаваемый объект. в нашем примере конструктор принимает имя и возраст и присваивает их свойствам name и age создаваемого объекта.
аждая функция в языке javascript потенциально является конструктором. Помимо этого, каждая функция имеет своиство prototype, в котором хранится обьект, свойства которого будут наследовать все объекты, создаваемые с помощью этой функции-конструктора. Изначально это пустой обьект, но мы можем вешать на него любые свойства и методы и они будут доступны всем экземпляпам класса.
Person.prototype.greet = function(){
  console.log('Hello, my name is ' + this.name);
}
Использование ключевого слова prototype позволяет добиться существенной экономии оперативной памяти. Каждый экземпляр класса, к примеру, будет содержать три свойства и один метод. Поэтому, если в памяти содержится тысяча таких объектов, метод также будет растиражирован тысячу раз. Но, поскольку в каждом экземпляре присутствует один и тот же метод, можно предписать новому объекту ссылаться на единственный экземпляр этого метода и не создавать его копию.
когда мы используем родные javascript-конструкторы, всем объектам, создаваемым с его помощью, присваивается свойство constructor, которое хранит код конструктора. Это же свойство есть и на пртотипе :
console.log(person.constructor);
console.log(person.prototype.constructor);
В языке джаваскрипт есть бинарный оператор instanceof, с помощью которого можно проверить обьект на принадлежность к классу :
console.log(person instanceof Person);
// аналогичную проверку можно выполнить и таким образом:
console.log(Person.prototype.isPrototypeOf(person));

Реализация дочерних классов

Developer = function(name, age, skills){
  Person.apply(this, arguments);
  this.skills = skills || [];
}

// и для наследования лучше всего использовать метод Object.create():
Developer.prototype = Object.create(Person.prototype);
// Но здесь свойство constructor будет указывать на функцию Person, в то время как нам нужно, чтобы оно указывало на функцию Developer. Это можно исправить:
Developer.prototype.constructor = Developer;
var developer = new Developer('sash', 33, ['PHP', 'JavaScript', 'SQL']);
Если объект developer является экземпляром класса Developer, а класс Developer - дочерний класс по отношению к классу Person, то JavaScript будет считать объект developer экземпляром класса Person:
 
console.log(developer instanceof Developer);// true
console.log(developer instanceof Person);// true

Создание объектов с помощью конструктора Object

var obj = new Object();
Конструктор Object() определяет класс Object, который стоит на вершине иерархии классов в языке жабаскрипт. другими словами, все объекты наследуют свои свойства от объекта Object.prototype. Поэтому на наших объектах person и developer есть методы toString и valueOf, хотя мы сами их не определяли. Мы можем переопределить эти методы на прототипе класса:
// Метод toString преобразовывает объект (массив, функцию) в строку 
Person.prototype.toString = function(){
    return this.name;// к примеру, просто вернём имя
}
// Метод valueOf вызывается когда нужно преобразовать объект в число
console.log(person.valueOf);// NaN

Extend

function extend(Child, Parent) {
  var F = function() { }
  F.prototype = Parent.prototype
  Child.prototype = new F()
  Child.prototype.constructor = Child
  Child.superclass = Parent.prototype
}

/* Использовать ее для наследования можно так: */

// создали базовый класс
function Animal(..) { ... }

// создали класс и сделали его потомком базового
function Rabbit(..)  { ... }
extend(Rabbit, Animal)

// добавили в класс Rabbit методы и свойства
Rabbit.prototype.run = function(..) { ... }

// все, теперь можно создавать объекты
// класса-потомка и использовать методы класса-родителя
rabbit = new Rabbit(..)
rabbit.animalMethod()

/*
В механизме наследования, разобранном выше, есть одно белое пятно. Это - конструктор.
Хотелось бы, чтобы конструкторы всех родителей вызывались по порядку до конструктора самого объекта.
С наследованием через extend - это очень просто.
Вызов конструктора родителя с теми же аргументами, что были переданы осуществляется так:
*/
function Rabbit(..)  {
  ...
  Rabbit.superclass.constructor.apply(this, arguments)

  ...
}
// Конечно же, аргументы можно поменять, благо apply дает возможность вызвать функцию с любыми параметрами вместо arguments в примере.

Наследование копированием свойств

/*
* Наследование копированием свойств
*/
function extendDeep(parent, child) {
  var i,
    toString = Object.prototype.toString,
    aStr = "[object Array]";

  child = child || {};// проверка наличия второго аргумента.

  for (i in parent) {
    if (parent.hasOwnProperty(i)) {
      if (typeof parent[i] == "object") {
        child[i] = (toString.call(parent[i]) == aStr) ? [] : {};
        extendDeep(parent[i], child[i]);// рекурсивный вызов, для того что бы скопировать все свойства объякта или элементы массива.
      } else {
        child[i] = parent[i];
      }
    }
  }

  return child;
}

var base = {
  id: "0",
  name: "base object",
  innerArray: [1, 2, 3]
};

var child = {};

extendDeep(base, child);
// или
var child = extendDeep(base);

child.innerArray.push(4);
console.log(child.innerArray == base.innerArray);// false
console.log(base.innerArray);// 1,2,3
console.log(child.innerArray);// 1,2,3, 4

Аттрибут класса

Когда мы вызываем метод toString у объекта, мы получаем строку [object Object], где второе слово Object это класс объекта. этот класс нельзя изменить и метод toString это единственный способ для получения класса объекта. Так мы можем определять класс всех стандартных объектов, но поскольку эти объекты определяют свои методы toString, мы должны вызывать метод toString обьекта Object.prototype.
console.log(Object.prototype.toString.call([1, 2]));// [object Array]
Это будет работать и для простых значений и мы можем использовать эту конструкцию вместо typeOf. Напишем функцию. которая будет возвращать нам класс объекта:
var classeof = function(obj){
  return Object.prototype.toString.call(obj).slice(8, -1);
};
console.log(classeof([]));// Array
console.log(classeof('me'));// String
console.log(classeof(/.*/));// RegExp
На этом всё. Вы уже можете писать объектно-ориентированный суперкод для захвата мира, ну или свою jQuery. My Congratulatons.
2016.05.21 148

Войдите или Зарегистрируйтесь чтобы оставить комментарий

Комментарии


    Яндекс.Метрика Яндекс.Метрика