Javascript.RU

ООП в Javascript: наследование

Update: Более новый материал по этой теме находится по адресу https://learn.javascript.ru/prototypes.

Javascript - очень гибкий язык. В отличие от Java, PHP, C++ и многих других языков, где наследование можно делать одним способом - в javascript таких способов много.

На уровне языка реализовано наследование на прототипах. С помощью некоторых трюков можно сделать (хотя и не так удобно, как в Java/C++) наследование на классах, объявить приватные свойства объекта и многое другое.

Корректность этой статьи

С момента появления эта статья вызвала критику некоторых профессионалов в javascript. Поэтому появилось это небольшое "пред-введение".

Не всё, происходящее при наследовании в javascript, статья описывает абсолютно корректно. В том числе, упрощено описание таких вещей как activation object, scope, prototype встроенных объектов.

Если Вы хотите максимально корректное и точное описание наследования, содержащее все детали - оно дано в стандарте языка, параграфы 13.2 и 15.3. Возможно, хороший вариант - прочитать статью для создания общей картины, а потом - уточнить технические детали, прочитав стандарт.

Упрощения в статье не затрагивают сути происходящего и не играют роли при real-life использовании javascript. Если Вы аргументированно считаете, что это не так - напишите комментарий, буду рад принять его к сведению и дополнить статью.

P.S Комментарии типа "А что будет, если прототип функции сделать числом и почему статья этого не описывает, это неправильная статья" - не принимаются. Не делают такого в real-life.

Обязательно пишите в комментах, если что неправильно или непонятно - на все отвечу и поправлю. Автор.

Любая функция, кроме некоторых встроенных, может создать объект.

Для этого ее нужно вызвать через директиву new.

Например, функция Animal в примере ниже создаст новый объект.

function Animal(name) {
    this.name = name
    this.canWalk = true
}

var animal = new Animal("скотинка")

Во время работы функции, вызванной директивой new, новосоздаваемый объект доступен как this, так что можно проставить любые свойства.

В этом примере был создан объект класса Animal, и ему добавлены свойства name и canWalk. Получилось вот так:

animal.name = 'скотинка'
animal.canWalk = true

Класс объекта определяется функцией, которая его создала. Для проверки принадлежности классу есть оператор instanceof:

alert(animal instanceof Animal)  // => true
Этот оператор иногда не работает, как полагается. Далее мы подробно разберем логику его работы, чтобы понимать, при каком наследовании и как его использовать.

В javascript базовое наследование основано не на классах. То есть, нет такого, что классы наследуют друг от друга, а объект класса-потомка получает общие свойства.

Вместо этого объекты наследуют от объектов без всяких классов. Наследование на классах можно построить(эмулировать), опираясь на базовое наследование javascript.

Разберем подробнее, что такое наследование от объектов и как оно работает.

Реализуется наследование через неявную(внутреннюю) ссылку одного объекта на другой, который называется его прототипом и в спецификации обозначается [[prototype]]. Это свойство обычно скрыто от программиста.

Также существует свойство с похожим названием prototype (без квадратных скобок) - оно вспомогательное и указывает, откуда брать прототип при создании объекта.

Когда вы ставите функции Animal свойство Animal.prototype = XXX - вы этим декларируете: "все новые объекты класса Animal будут иметь прототип XXX".

Исходя из спецификации языка, ссылка на прототип объекта [[prototype]] не обязана быть доступной для чтения и изменения.

Однако в реализации javascript, используемой в Gecko-браузерах: Firefox/Mozilla и т.п., эта ссылка является обычным свойством объекта: __proto__.
В этих браузерах ее можно читать и изменять.

Наследование происходит через скрытое свойство прототип [[prototype]], однако единственный кроссбраузерный способ указать прототип - это использовать свойство prototype функции-конструктора.

Например пусть объект кролик "rabbit" наследует от объекта животное "animal".

В наследовании на прототипах это реализуется как ссылка rabbit.[[prototype]] = animal:

наследование javascript Объект

Ссылка [[prototype]] работает так:

  1. Любое запрошенное свойство ищется сначала в rabbit
  2. Если свойство там не найдено, то оно ищется в rabbit.[[prototype]], т.е в animal

Благодаря поиску по прототипу получается, что все функции и переменные, которые были в animal, доступны и в rabbit.

Ссылка на прототип создается оператором new во время создания объекта.

Ее значением становится свойство prototype функции-конструктора. Значение prototype указывает, от кого будут наследовать новые объекты

Прототип работает как резервное хранилище свойств. Если свойства нет у объекта - оно ищется в его прототипе. Получается наследование.

Сделаем класс, наследующий от Animal - назовем его Rabbit.

Для этого сначала объявим функцию Rabbit.

function Rabbit(name) {
    this.name = name
}

Пока что она просто создает объекты Rabbit. Поставим свойство prototype, чтобы новые объекты имели прототип animal (мы объявили этот объект чуть выше):

Rabbit.prototype = animal
function Animal(name) {
    this.name = name
    this.canWalk = true
}

var animal = new Animal("скотинка")

function Rabbit(name) {
	this.name = name
}

// все объекты, созданные Rabbit
// будут иметь прототип (наследовать) animal
Rabbit.prototype = animal

А теперь - создадим пару кроликов.

big = new Rabbit('Chuk')
small = new Rabbit('Gek')

alert(big.name)  // Chuk
alert(small.name) // Gek

alert(big.canWalk) // true

// в Firefox можно еще так
if (big.__proto__) {  // в Firefox __proto__ это [[Prototype]]
    alert(big.__proto__.name) // скотинка
}

Свойство name хранится прямо в объектах Rabbit, а canWalk берется из прототипа animal.

Так как у обоих кроликов один прототип, то его изменение тут же отразится на обоих.

alert(big.canWalk)  // true

// поменяли в прототипе
animal.canWalk = false

alert(big.canWalk)  // false
alert(small.canWalk)  // false

Запишем свойство canWalk напрямую в объект Rabbit:

animal.canWalk = false

small.canWalk = true

alert(big.canWalk)  // false
alert(small.canWalk)  // true

У разных кроликов получилось разное значение canWalk, независимое от родителя.

Таким образом мы реализовали перекрытие (override) свойств родительского объекта.

Наверху цепочки всегда находится объект встроенного класса Object.

Так получается из-за того, что по умолчанию свойство prototype функции равно пустому объекту new Object().

// Animal.prototype не указан явно, по умолчанию:
Animal.prototype = {}

Получается такая картинка:

наследование javascript объект

Это хорошо, потому что у класса Object есть ряд полезных функций: toString(), hasOwnProperty()... А, например в Firefox, есть даже функция toSource(), которая дает исходный код, т.е "полный дамп" объекта.

Благодаря тому, что вверху цепочки наследования стоит Object, все остальные объекты имеют доступ к этому функционалу.

При вызове метода - он имеет доступ ко всем данным "своего" объекта.

Для этого в javascript (как, впрочем, и во многих других языках) используется ключевое слово this.

Например мы хотим добавить всем объектам класса Animal функцию перемещения. Для этого запишем в Animal.prototype метод move. Каждый его вызов будет изменять расстояние distance:

Animal.prototype.move = function(n) {
	this.distance = n
	alert(this.distance)
}

Теперь если мы сделаем новый объект, то он сможет передвигаться:

var animal = new Animal("животное")
animal.move(3)   // => 3
animal.move(4)   // => 4
...

При вызове animal.move, интерпретатор находит нужный метод в прототипе animal: Animal.prototype.move и выполняет его, устанавливая this в "текущий" объект.

this в javascript

В javascript this работает не так, как в PHP, C, Java.

Значение this ставится на этапе вызова функции и может быть различным, в зависимости от контекста.

Подробнее это описано в статье как javascript работает с this.

Точно также смогут вызывать move и объекты класса Rabbit, так как их прототипом является animal.

Методы класса объявляются в Класс.prototype. Например, Animal.prototype - содержит методы для всех объектов класса Animal.

Альтернативный подход заключается в добавлении методов объекту в его конструкторе.

Объявление move в классе Animal при таком подходе выглядело бы вот так:

function Animal(n) {
    // конструируем объект
    .....
    // добавляем методы
    this.move = function(n) {
        this.distance = n
        alert(this.distance)
    }
}

В наиболее распространенных javascript-библиотеках используется первый подход, т.е добавление методов в прототип.

Свойства-объекты или "иногда прототип это зло"

Объявление всех свойств в прототипе может привести к незапланированному разделению одного и того же свойства разными объектами.

Например, объявим объект класса хомяк(Hamster). Метод found набирает еду за щеки, набранное хранит в массиве food.

function Hamster() {  }
Hamster.prototype = {
	food: [],
	found: function(something) {
		this.food.push(something)
	}
}

Создадим двух хомячков: speedy и lazy и накормим первого:

speedy = new Hamster()
lazy = new Hamster()

speedy.found("apple")
speedy.found("orange")

alert(speedy.food.length) // 2
alert(lazy.food.length) // 2 (!??)

Открыть этот код в новом окне

Как видно - второй хомяк тоже оказался накормленным! В чем дело?

Причина заключается в том, что food не является элементарным значением.

Если при простом присвоении hamster.property="..." меняется свойство property непосредственно в объекте hamster, то при вызове hamster.food.push(...) - яваскрипт сначала находит свойство food - а так, как в hamster его нет, то оно берется из прототипа Hamster.prototype, а затем вызывает для него метод push.

На каком бы хомяке не вызывался hamster.food.push(..) - свойство food будет браться одно и то же, из общего прототипа всех хомяков.

Мы получили пример "статического свойства класса". Да, оно бывает полезно. Например, мы можем разделять общую информацию между всеми хомяками посетителя.

Но в данном случае такое ни к чему.

Чтобы разделить данные, неэлементарные свойства обычно присваивают в конструкторе:

function Hamster() {
    this.food = []
}
Hamster.prototype = {
    food: [], // просто для информации
    found: function(something) {
        this.food.push(something)
    }
}

Теперь у каждого объекта-хомяка будет свой собственный массив food.

Свойство food в прототипе оставлено как комментарий. Оно не используется, но может быть полезно для удобства документирования.

Рабочий вариант наследования на классах, в общем-то, готов.

Для того чтобы объект класса Rabbit унаследовал от класса Animal - нужно

  1. Описать Animal
  2. Описать Rabbit
  3. Унаследовать кролика от объекта Animal:
    Rabbit.prototype = new Animal()
    

Однако, у такого подхода есть два недостатка:

  1. Для наследования создается совершенно лишний объект new Animal()
  2. Конструктор Animal должен предусматривать этот лишний вызов для и при необходимости делать такое "недоживотное", годное лишь на прототип.

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

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()

Функция очень удобная и работает "на ура".

Она не создает лишних объектов и в качестве бонуса записывает класс-родитель в свойство потомка superclass - это удобно для вызова родительских методов в конструкторе и при перекрытии методов.

Как оно работает?

Есть разные мнения, кто придумал функцию extend, но популяризацией она обязана Дугласу Крокфорду.

Как и почему она все-таки работает - может быть неочевидно даже опытным javascript-специалистам.

Попробуйте понять это, полистав спецификацию, особенно параграфы 13.2 и 15.3, а если какие-то вопросы остались - читайте дальше.

Предупреждение. Объяснение сложное, подробное и, вообще говоря, не обязательное, ведь функция "просто работает". Читайте на свой страх и риск.

Здесь мы разберем то, что, вообще говоря, происходит при создании любой функции (и в первой строке extend).

mwsnap023.jpg

Этот синтаксис - ни что иное как удобная форма записи для:

F = new Function()

Эта строка cоздает новый объект класса Function (встроенный класс javascript).

Конструктор Function хранит ссылку на Function.prototype, который содержит общие свойства функций: call, apply, constructor, toString и т.п. Поэтому F.[[prototype]] = Function.prototype.

Кстати, за счет такого прототипа все функции и имеют доступ к методам call, apply и т.д.

Создание объекта F можно изобразить так:

mwsnap026.jpg

На картинке также изображено свойство prototype, которое автоматически устанавливается в new Object(). Свойство constructor также генерируется интерпретатором и показывает обратно, так что по кругу prototype.constructor для функции можно идти бесконечно: F.prototype.constructor === F.

Следующая строка устанавливает свойство F.prototype:

F.prototype = Parent.prototype

До второй строки свойства имели такие значения:

mwsnap027.jpg

Свойство F.prototype указывало на объект new Object() (справа снизу на рисунке).

Теперь значение поменялось, и старый new Object() перестал быть доступен - ни одна ссылка на него не ведет. Поэтому сборщик мусора убивает его.

Вот так изменения отразятся на картине:

mwsnap028.jpg

Следующая строка устанавливает свойство prototype для дочернего класса, чтобы оно служило прототипом всех дочерних объектов.

Child.prototype = new F()

При создании объекта класса F, свойство [[prototype]] нового объекта конструктор возьмет из F.prototype:

(new F).[[prototype]] = (т.к F.prototype==Parent.prototype) = Parent.prototype

т.е получится такая цепочка присвоения

Child.prototype.[[prototype]] = (new F).[[prototype]] = Parent.prototype

Иначе говоря, у нас получилось, что

Child.prototype = [объект,  прототип которого - Parent.prototype]

Это присвоение можно изобразить на картинке вот так:

mwsnap029.jpg

В правом-нижнем углу - как раз и находится Child.prototype, прототипом которого получился Parent.prototype.

Собственно, наследование уже работает. В самом деле, создадим новый объект класса Child:

child = new Child(...)

Вспоминаем, что интерпретатор, выполняя new, ставит child.[[prototype]] = Child.prototype.

Поиск свойств, не найденных в child будет идти по
child.[[prototype] = Child.prototype = new F().
Если там не нашли, то интерпретатор будет искать в new F().[[prototype]] = F.prototype = Parent.prototype, то есть, в конечном счете:

child -> Child.prototype -> Parent.prototype

.. Что и требовалось получить.

Свойство Child.prototype.constructor осталось старое, и его нужно поправить строкой:

Child.prototype.constructor = Child

Для каждой функции свойство prototype.constructor всегда должно указывать на саму функцию.

Если это так, то объекты, которые функция создает, тоже будут иметь (через прототип) правильное свойство constructor, указывающее на создавшую их функцию.

Например:

function Z() {}
alert(Z.prototype.constructor)  // => функция Z

z = new Z()
//в z нет ничего, но в z.[[prototype]]=Z.prototype есть constructor
alert(z.constructor)   // => функция Z

Эта строка extend как раз и проставляет правильное свойство prototype.constructor.

Добавим в класс явную ссылку на родительский класс для удобного обращения к его методам. Понадобится для вызова конструктора родителя или если родительский метод был перекрыт в потомке.

Child.superclass = Parent.prototype

В механизме наследования, разобранном выше, есть одно белое пятно. Это - конструктор.

Хотелось бы, чтобы конструкторы всех родителей вызывались по порядку до конструктора самого объекта.

С наследованием через extend - это очень просто.

Вызов конструктора родителя с теми же аргументами, что были переданы осуществляется так:

function Rabbit(..)  {
	...
	Rabbit.superclass.constructor.apply(this, arguments)

	...
}

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

Аналогично можно вызвать и любой другой метод родительского класса:

Rabbit.superclass.run.apply(this, ...)

В этих примерах везде в явном виде указано имя класса Rabbit, хотя можно бы попробовать указать this.constructor, который должен указывать на Rabbit, т.к объект принадлежит этому классу.

Если так поступить, то будет ошибка при цепочке наследования классов из 3 элементов типа foo -> bar -> zot.

Проиллюстрируем ее на примере:

function foo() {}
foo.prototype.identify = function() {
	return "I'm a foo";
}

function bar() {}
extend(bar, foo)
bar.prototype.identify = function() {
	return "I'm a bar and " +
	this.constructor.superclass.identify.apply(this, arguments);
}

function zot() {}
extend(zot, bar)
zot.prototype.identify = function() {
	return "I'm a zot and " +
	this.constructor.superclass.identify.apply(this, arguments);
}

f = new foo();

alert(f.identify()); // "I'm a foo"

b = new bar();

alert(b.identify()); // "I'm a bar and I'm a foo"

z = new zot();

alert(z.identify()); // stack overflow

Последний вызов приведет к ошибке "too much recursion" из-за того, что this.constructor.superclass, к которому идет обращение в функции bar.identity обращается к дочернему классу zot. В результате bar.identity вызывает сама себя в бесконечной рекурсии.

Правильный способ заключается в явном обозначении класса, т.е Rabbit.superclass...

Оператор instanceOf проверяет принадлежность объекта классу, проходя по цепочке его прототипов, и используя для сравнения свойство prototype.

Логику его работы можно описать так:

function instanceOf(object, constructor) {
   var o=object

   while (o.__proto__ != null) {
      if (o.__proto__ === constructor.prototype) return true
      o = o.__proto__
   }
   return false
 }

Поэтому при правильной структуре прототипов он всегда корректно работает.

У этого оператора есть неприятная особенность при использовании нескольких окон: в разных окнах объекты классов (окружение) разное, поэтому массив из одного окна(фрейма) не будет опознан как Array в другом фрейме.

Впрочем, такая ситуация возникает довольно редко.

Для окончательной организации удобного javascript-наследования на классе, пригодится функция копирования свойств из объекта src в другой dst:

// копирует все свойства из src в dst,
// включая те, что в цепочке прототипов src до Object
function mixin(dst, src){
	// tobj - вспомогательный объект для фильтрации свойств,
	// которые есть у объекта Object и его прототипа
	var tobj = {}
	for(var x in src){
		// копируем в dst свойства src, кроме тех, которые унаследованы от Object
		if((typeof tobj[x] == "undefined") || (tobj[x] != src[x])){
			dst[x] = src[x];
		}
	}
	// В IE пользовательский метод toString отсутствует в for..in
	if(document.all && !document.isOpera){
		var p = src.toString;
		if(typeof p == "function" && p != dst.toString && p != tobj.toString &&
		 p != "\nfunction toString() {\n    [native code]\n}\n"){
			dst.toString = src.toString;
		}
	}
}

В полном примере мы создадим класс Animal c методом walk и его насленика Bird, который умеет летать: fly. Функции walk и fly принимают время ходьбы/полета и соответственно увеличивают свойство distance - расстояние до животного:

// ---- родительский класс ----

function Animal(name, walkSpeed) {
	this.name = name
	this.walkSpeed = walkSpeed
}

// добавляем методы объекта
mixin(Animal.prototype, {

	// пример переменной
	distance: 0,

	// пример метода
	walk: function(time) {
		this.distance = this.distance + time*this.walkSpeed
	},

	toString: function() {
		return this.name+" на расстоянии "+this.distance
	}
})

// ---- класс наследник ----

function Bird(name, walkSpeed, flySpeed) {
	// вызов родительского конструктора
	Bird.superclass.constructor.call(this, name, walkSpeed)

	this.flySpeed = flySpeed
}
extend(Bird, Animal)

mixin(Bird.prototype, {
	fly: function(time) {
		this.distance = this.distance + time*this.flySpeed
	}
})

Пример создания объекта-наследника:

bird = new Bird("Птыц", 1, 10)

bird.walk(3)

alert(bird) // => Птыц на расстоянии 3

bird.fly(2)

alert(bird) // => Птыц на расстоянии 23

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

При наследовании можно организовать "настоящие" приватные члены класса. Для этого, однако, придется объявлять все методы не отдельно от конструктора, а внутри него:

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(name, walkSpeed) {

	// объявить приватную переменную
	var speed = walkSpeed

	// объявить открытую переменную
	this.distance = 0

	// добавить метод, использующий private speed
	this.walk = function(time) {
		this.distance = this.distance + time*speed
	}

	// добавить метод, использующий private name
	this.toString = function() {
		return name+" на расстоянии "+this.distance
	}
}


// ---- класс наследник ----

function Bird(name, walkSpeed, flySpeed) {
	// вызов родительского конструктора
	Bird.superclass.constructor.call(this, name, walkSpeed)

	this.fly = function(time) {
		this.distance = this.distance + time*flySpeed
	}
}
extend(Bird, Animal)


bird = new Bird("Птыц", 1, 10)

bird.walk(3)

alert(bird) // => Птыц на расстоянии 3

bird.fly(2)

alert(bird) // => Птыц на расстоянии 23

Приватными являются все свойства, которые доступны только из внутренних методов объекта через механизм замыкания (см. статью о функциях javascript).

Это свойства, явно объявленные через var, плюс аргументы конструктора.

При таком способе объявления - все свойства и методы записываются не в прототип объекта, а в сам объект.

Поэтому, если объектов создается очень много, то это сопряжено с дополнительными расходами памяти на хранение множества копий методов - свой код функции для каждого объекта, а не один в прототипе на всех. Обычно же эти расходы можно во внимание не принимать.

Если Вы использовали ООП в других языках программирования, то наверняка знаете, что чаще делаются не private свойства, а protected, т.е такие, к которым могут получить доступ наследники. Javascript не предоставляет синтаксиса для создания protected свойств, поэтому их просто помечают подчеркиванием в начале.

Например,

function Animal(name) {

	var privateVariable = 0

	this._protectedName = name

	this._protectedMethod = function(..) {
		... alert(privateVariable)..
	}

	this.publicMethod = function() { ... }
}

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

Ограничение доступа к таким "защищенным" свойствам не жесткое и остается на совести программиста.

Есть альтернативный способ наследования, который вообще не требует вызова new.

При этом объекты создаются с помощью "фабричных функций" (factory function). Например, в следующем примере это функция Animal, которая производит некие действия и возвращает объект.

function Animal(name) {
	var speed = 10
	return {
		name: name,
		run: function(distance) { 
			return distance / speed
		}
	}
}
pet1 = Animal()
pet2 = Animal()

Через замыкания (о функциях и замыканиях) организуются "приватные" члены класса. В примере выше доступ к переменной speed возможен только из функции run .

Задача фабрики объектов - создать объект и инициализировать его.

Для создания потомка фабрика объектов просто модифицирует объект, создаваемой функцией-родителем.

Рассмотрим пример с созданием Rabbit - потомка Animal:

function Rabbit(name) {

	// вызвать конструктор родителя, 
	// получить родительский объект в me
	var me = Animal(name)  
	
	// добавить приватную переменную
	var jumps = 0   

	/* добавить новые методы к me */
	me.jump = function() { jumps++ }
	me.getJumps = function() { return jumps }

	// поставить правильное свойство конструктора
	// (делаем вид, что объект создали мы, а не Animal)
	me.constructor = arguments.callee

	return me
}

При создании потомка фабричная функция делает следующее:

  1. Создает объект родительского класса
  2. Присваивает ему публичные свойства и методы
  3. Меняет свойство constructor объекта на себя
    • этот шаг можно пропустить (см дальше)

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

Фактически, функция берет другой объект и добавляет ему свои методы. Из-за этого такую реализацию наследования иногда называют паразитическим наследованием.

Почему этот способ мой любимый?

Во-первых, потому, что именно такой стиль ООП, как мне кажется, наиболее соответствует по духу яваскрипт.

Он прост и понятен.

В нем есть приватные переменные, которые замечательно сжимаются javascript-минификатором, что существенно сокращает объем скачиваемого посетителем кода и увеличивает производительность, т.к интерпретатор javascript быстрее работает с короткими именами переменных.

К сожалению, метод instanceof при таком наследовании не работает, так как он завязан на [[prototype]] объекта. Фактически, вызов instanceof A просто проверяет, есть ли в цепочке прототипов объекта класс A.

Свойство constructor, которое было присвоено на 3м шаге, как раз и служит для эмуляции instanceof:

alert(rabbit.constructor === Rabbit) // => true
Более сложная реализация наследования не присваивает constructor, а добавляет его в специальный список. Функция-аналог instanceof проверяет всю цепочку и выдает, есть ли в ней искомый класс.
При таком наследовании часто применяют альтернативный подход к instanceOf: проверку на нужный метод.

Как часто описывают такой способ - ".. Если объект умеет крякать, значит это утка. Кому какое дело что он на самом деле..":

if (arr.splice) {
  // умеет splice, значит это массив
  // вообще, какая разница, что это за объект на самом деле,
  // то, что надо, он умеет - пользуем..
    .. arr.splice(..) ..
}

В наследовании через классы свойства родителя доступны через superclass.

Здесь - чтобы получить доступ к методу родительского объекта, его обычно копируют куда-нибудь в замыкание. Например:

function Rabbit(name) {
	var me = animal(name)  

	var jumps = 0   

	me.jump = function() { jumps++ }
	me.getJumps = function() { return jumps }

	// скопировать метод run родителя в замыкание
	var super_run = me.run

	/* перекрыть метод */
	me.run = function(distance) {
		this.jump()	

		// вызвать родительский метод
		return super_run.call(this, distance)
	}
	return me
}

Таким образом нужно сделать "бэкап" всех нужных свойств.

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

В этой статье мы подробно разобрали, как в Javascript можно организовать наследование

  • с вызовом конструкторов родителей
  • с private/protected/public доступом
  • с перекрытием методов и вызовом методов родителей
  • с проверкой принадлежности объекта классу

..А также посмотрели внутренние механизмы наследования в javascript.

Пишите в комментарии, если что.


Автор: Adam Smith (не зарегистрирован), дата: 6 сентября, 2024 - 11:41
#permalink

buy sprinklez
torch world exotics
buy sprinklez brand
where to buy sprinklez in new jersey
7up pound cake
sprinklez strain
buy sprinklez weed strain
sweet berry
blue m&m sprinklez
m&m sprinklez strain
m&m weed strain
m&ms strain
m&m sprinklez brand
sprinklez
blueberry shortcake strain
blueberry shortcake weed strain
sprinklez weed
boston cream pie
boston cream pie strain
boston cream pie weed
brooklyn blackout cake
brooklyn blackout cake strain
brooklyn blackout cake weed strain
cookies&cream milkshake strain
sprinklez weed
sprinklez strain
cinnamon roll bread pudding strain
cinnamon roll bread pudding weed strain
funfetti pound cake strain
funfetti pound cake weed strain
confetti cookies dough cream strain
sprinklez for sale
sprinklez mint chip strain
sprinklez mint weed strain
sprinklez munyun strain
sprinklez munyun weed strain
sprinklez pumpkin strain
sprinklez pumpkin weed strain
sprinklez peach perfection strain
sprinklez peach perfection weed strain
sprinklez new York cheesecake strain
sprinklez weed strain
Arnold palmer strain
torchworld strain
basque burnt cheesecake strain
torchiez weed strain
torchiez weed
brite crawlers strain
gumdropz strain
brite crawlers
all american
all American strain
banana split
banana split strain
birthdaycake marshmallow
buy marshmallow birthdaycake online
marshmallow birthdaycake strain
blueberry pie marshmallow
blueberry pie marshmallow strain
marshmallow
bubblegum
bubblegum strain
bubblegum rainbow
bubblegum rainbow strain
cherry ripe marshmallow
cherry ripe marshmallow strain
choko whip swirl marshmallow
choko whip swirl marshmallow strain
choko covered banana mardsmallow
choko covered banana marshmallow strain
Hawaiian guava ake
jasmine pana cotta
sprinklez strain
buy sprinklez online
lemonberry kush
sprinklez for sale
buy sprinklez strain
sprinklez weed strain for sale
lucky charms
lucky charms cereal
sprinklez weed for sale
bud packs
m&m cookie
buy sprinklez online
milk and cookies
buy sprinklez weed
marble pound cake
marijuana strain
black mamba seeds
mud pie
mississipi mud pie
cannabis seed
psychedelic
sprinklez
sprinklez brand
sprinklez strain
sprinklez weed
original sprinklez brand
oreoz
runtz
sprinklez weed strain
pink vanilla butter cake
og kush
butter cake og
pink vanilla og
la kush cake
wedding cake strain
kush flavoured
rainbow confetti
birthday cake
rainbow sherbet cotton candy
rasoberry cheesecake truffles online
buy sprinklez nyc
buy sprinklez queen
red velvet cheesecake
southern buttermilk pie
sprinklez apple pie
buy sprinklez apple pie online
sprinklez blueberry pancakes
buy blueberry pancakes nyc
sprinklez bubblegum cotton candy
sprinklez candy apple cannabis strain
buy sprinklez candy apple cannabis strain
sprinklez weed strain
buy candy apple strain
sprinklez cherry lemonade
buy sprinklez cherry lemonade
original sprinklez exotic strain
sprinklez brand
sprinklez
sprinklez strain
buy sprinklez exotic online
sprinklez cotton candy
where to buy candy sprinkles
cannabis
sprinklez strain
sprinklez brand
sprinklez fruitty pebles
alien kush strain
sprinklez
sprinklez marshmallow madness
sprinklez for sale
sprinklez brand
cali exotic cannabis strain
original sprinklez brand exotic
sprinklez strain
sprinklez miami
buy sprinklez miami
sprinklez weed strain
buy sprinklez
marijuana strain
brite crawlers
buy gumdropz online
gumdrops mint mojito
gumdropz for sale
Arnold palmer
torchworld
gumdropz logo shorts
fresh baked apple crisp
grapefruit cooler
sprinklez
sprinklez strain
hot fudge sundae
chocolate kush
hot honey butter blondies
sprinklez strain
original torchiez
buy original torchiez
spiced ginger bread choco chunk
torchiez blazin banana
torchiez chili mango
torchiez fire plum crumble
torhiez fuego berry
torchiez jalapeno plum
torchiez red hotz
cookie butter
cookie monster
creamy coconut tres leches
dulce de leche
fantastic funfetti
fresas con crema
frosty snowman marshmallow
fruit gushers
fruity pebblez marshmallow
funfetti marshmallow
honey vanilla
jolly rancher


Автор: Adam Smith (не зарегистрирован), дата: 6 сентября, 2024 - 12:08
#permalink

buy sprinklez
torch world exotics
buy sprinklez brand
where to buy sprinklez in new jersey
7up pound cake
sprinklez strain
buy sprinklez weed strain
sweet berry
blue m&m sprinklez
m&m sprinklez strain
m&m weed strain
m&ms strain
m&m sprinklez brand
sprinklez
blueberry shortcake strain
blueberry shortcake weed strain
sprinklez weed
boston cream pie
boston cream pie strain
boston cream pie weed
brooklyn blackout cake
brooklyn blackout cake strain
brooklyn blackout cake weed strain
cookies&cream milkshake strain
sprinklez weed
sprinklez strain
cinnamon roll bread pudding strain
cinnamon roll bread pudding weed strain
funfetti pound cake strain
funfetti pound cake weed strain
confetti cookies dough cream strain
sprinklez for sale
sprinklez mint chip strain
sprinklez mint weed strain
sprinklez munyun strain
sprinklez munyun weed strain
sprinklez pumpkin strain
sprinklez pumpkin weed strain
sprinklez peach perfection strain
sprinklez peach perfection weed strain
sprinklez new York cheesecake strain
sprinklez weed strain
Arnold palmer strain
torchworld strain
basque burnt cheesecake strain
torchiez weed strain
torchiez weed
brite crawlers strain
gumdropz strain
brite crawlers
all american
all American strain
banana split
banana split strain
birthdaycake marshmallow
buy marshmallow birthdaycake online
marshmallow birthdaycake strain
blueberry pie marshmallow
blueberry pie marshmallow strain
marshmallow
bubblegum
bubblegum strain
bubblegum rainbow
bubblegum rainbow strain
cherry ripe marshmallow
cherry ripe marshmallow strain
choko whip swirl marshmallow
choko whip swirl marshmallow strain
choko covered banana mardsmallow
choko covered banana marshmallow strain
Hawaiian guava ake
jasmine pana cotta
sprinklez strain
buy sprinklez online
lemonberry kush
sprinklez for sale
buy sprinklez strain
sprinklez weed strain for sale
lucky charms
lucky charms cereal
sprinklez weed for sale
bud packs
m&m cookie
buy sprinklez online
milk and cookies
buy sprinklez weed
marble pound cake
marijuana strain
black mamba seeds
mud pie
mississipi mud pie
cannabis seed
psychedelic
sprinklez
sprinklez brand
sprinklez strain
sprinklez weed
original sprinklez brand
oreoz
runtz
sprinklez weed strain
pink vanilla butter cake
og kush
butter cake og
pink vanilla og
la kush cake
wedding cake strain
kush flavoured
rainbow confetti
birthday cake
rainbow sherbet cotton candy
rasoberry cheesecake truffles online
buy sprinklez nyc
buy sprinklez queen
red velvet cheesecake
southern buttermilk pie
sprinklez apple pie
buy sprinklez apple pie online
sprinklez blueberry pancakes
buy blueberry pancakes nyc
sprinklez bubblegum cotton candy
sprinklez candy apple cannabis strain
buy sprinklez candy apple cannabis strain
sprinklez weed strain
buy candy apple strain
sprinklez cherry lemonade
buy sprinklez cherry lemonade
original sprinklez exotic strain
sprinklez brand
sprinklez
sprinklez strain
buy sprinklez exotic online
sprinklez cotton candy
where to buy candy sprinkles
cannabis
sprinklez strain
sprinklez brand
sprinklez fruitty pebles
alien kush strain
sprinklez
sprinklez marshmallow madness
sprinklez for sale
sprinklez brand
cali exotic cannabis strain
original sprinklez brand exotic
sprinklez strain
sprinklez miami
buy sprinklez miami
sprinklez weed strain
buy sprinklez
marijuana strain
brite crawlers
buy gumdropz online
gumdrops mint mojito
gumdropz for sale
Arnold palmer
torchworld
gumdropz logo shorts
fresh baked apple crisp
grapefruit cooler
sprinklez
sprinklez strain
hot fudge sundae
chocolate kush
hot honey butter blondies
sprinklez strain
original torchiez
buy original torchiez
spiced ginger bread choco chunk
torchiez blazin banana
torchiez chili mango
torchiez fire plum crumble
torhiez fuego berry
torchiez jalapeno plum
torchiez red hotz
cookie butter
cookie monster
creamy coconut tres leches
dulce de leche
fantastic funfetti
fresas con crema
frosty snowman marshmallow
fruit gushers
fruity pebblez marshmallow
funfetti marshmallow
honey vanilla
jolly rancher


Автор: Nate T (не зарегистрирован), дата: 6 сентября, 2024 - 16:08
#permalink

Simple divine and astonishing content. Ideas behind the inspiration was spectacular uncle frogs mushroom gummies for sale, road trip gummies coupon code, polkadot chocolate coupon code, polkadot bars real vs fake, polkadot chocolate coupon code, polkadot gummies ingredient, buy dermal fillers usa, dog supplements where to buy. Venture into a world of unique artistry and sound mind with calmness and relaxation. Intriguing thoughts tho!


Автор: Joseph (не зарегистрирован), дата: 6 сентября, 2024 - 16:23
#permalink

Автор: Гость (не зарегистрирован), дата: 6 сентября, 2024 - 16:25
#permalink

Great content. Ideas behind the inspiration was spectacular buy uncle frogs mushroom gummies, buy road trip mushroom gummies, buy polkadot chocolate, buy dermal fillers online, buy dog supplements online. Venture into a world of unique artistry and sound mind with calmness and relaxation. Intriguing thoughts tho!


Автор: Adam Smith (не зарегистрирован), дата: 11 сентября, 2024 - 10:39
#permalink

buy sprinklez
torch world exotics
buy sprinklez brand
where to buy sprinklez in new jersey
7up pound cake
sprinklez strain
buy sprinklez weed strain
sweet berry
blue m&m sprinklez
m&m sprinklez strain
m&m weed strain
m&ms strain
m&m sprinklez brand
sprinklez
blueberry shortcake strain
blueberry shortcake weed strain
sprinklez weed
boston cream pie
boston cream pie strain
boston cream pie weed
brooklyn blackout cake
brooklyn blackout cake strain
brooklyn blackout cake weed strain
cookies&cream milkshake strain
sprinklez weed
sprinklez strain
cinnamon roll bread pudding strain
cinnamon roll bread pudding weed strain
funfetti pound cake strain
funfetti pound cake weed strain
confetti cookies dough cream strain
sprinklez for sale
sprinklez mint chip strain
sprinklez mint weed strain
sprinklez munyun strain
sprinklez munyun weed strain
sprinklez pumpkin strain
sprinklez pumpkin weed strain
sprinklez peach perfection strain
sprinklez peach perfection weed strain
sprinklez new York cheesecake strain
sprinklez weed strain
Arnold palmer strain
torchworld strain
basque burnt cheesecake strain
torchiez weed strain
torchiez weed
brite crawlers strain
gumdropz strain
brite crawlers
all american
all American strain
banana split
banana split strain
birthdaycake marshmallow
buy marshmallow birthdaycake online
marshmallow birthdaycake strain
blueberry pie marshmallow
blueberry pie marshmallow strain
marshmallow
bubblegum
bubblegum strain
bubblegum rainbow
bubblegum rainbow strain
cherry ripe marshmallow
cherry ripe marshmallow strain
choko whip swirl marshmallow
choko whip swirl marshmallow strain
choko covered banana mardsmallow
choko covered banana marshmallow strain
Hawaiian guava ake
jasmine pana cotta
sprinklez strain
buy sprinklez online
lemonberry kush
sprinklez for sale
buy sprinklez strain
sprinklez weed strain for sale
lucky charms
lucky charms cereal
sprinklez weed for sale
bud packs
m&m cookie
buy sprinklez online
milk and cookies
buy sprinklez weed
marble pound cake
marijuana strain
black mamba seeds
mud pie
mississipi mud pie
cannabis seed
psychedelic
sprinklez
sprinklez brand
sprinklez strain
sprinklez weed
original sprinklez brand
oreoz
runtz
sprinklez weed strain
pink vanilla butter cake
og kush
butter cake og
pink vanilla og
la kush cake
wedding cake strain
kush flavoured
rainbow confetti
birthday cake
rainbow sherbet cotton candy
rasoberry cheesecake truffles online
buy sprinklez nyc
buy sprinklez queen
red velvet cheesecake
southern buttermilk pie
sprinklez apple pie
buy sprinklez apple pie online
sprinklez blueberry pancakes
buy blueberry pancakes nyc
sprinklez bubblegum cotton candy
sprinklez candy apple cannabis strain
buy sprinklez candy apple cannabis strain
sprinklez weed strain
buy candy apple strain
sprinklez cherry lemonade
buy sprinklez cherry lemonade
original sprinklez exotic strain
sprinklez brand
sprinklez
sprinklez strain
buy sprinklez exotic online
sprinklez cotton candy
where to buy candy sprinkles
cannabis
sprinklez strain
sprinklez brand
sprinklez fruitty pebles
alien kush strain
sprinklez
sprinklez marshmallow madness
sprinklez for sale
sprinklez brand
cali exotic cannabis strain
original sprinklez brand exotic
sprinklez strain
sprinklez miami
buy sprinklez miami
sprinklez weed strain
buy sprinklez
marijuana strain
brite crawlers
buy gumdropz online
gumdrops mint mojito
gumdropz for sale
Arnold palmer
torchworld
gumdropz logo shorts
fresh baked apple crisp
grapefruit cooler
sprinklez
sprinklez strain
hot fudge sundae
chocolate kush
hot honey butter blondies
sprinklez strain
original torchiez
buy original torchiez
spiced ginger bread choco chunk
torchiez blazin banana
torchiez chili mango
torchiez fire plum crumble
torhiez fuego berry
torchiez jalapeno plum
torchiez red hotz
cookie butter
cookie monster
creamy coconut tres leches
dulce de leche
fantastic funfetti
fresas con crema
frosty snowman marshmallow
fruit gushers
fruity pebblez marshmallow
funfetti marshmallow
honey vanilla
jolly rancher


Автор: Matt (не зарегистрирован), дата: 11 сентября, 2024 - 10:42
#permalink

buy sprinklez
torch world exotics
buy sprinklez brand
where to buy sprinklez in new jersey
7up pound cake
sprinklez strain
buy sprinklez weed strain
sweet berry
blue m&m sprinklez
m&m sprinklez strain
m&m weed strain
m&ms strain
m&m sprinklez brand
sprinklez
blueberry shortcake strain
blueberry shortcake weed strain
sprinklez weed
boston cream pie
boston cream pie strain
boston cream pie weed
brooklyn blackout cake
brooklyn blackout cake strain
brooklyn blackout cake weed strain
cookies&cream milkshake strain
sprinklez weed
sprinklez strain
cinnamon roll bread pudding strain
cinnamon roll bread pudding weed strain
funfetti pound cake strain
funfetti pound cake weed strain
confetti cookies dough cream strain
sprinklez for sale
sprinklez mint chip strain
sprinklez mint weed strain
sprinklez munyun strain
sprinklez munyun weed strain
sprinklez pumpkin strain
sprinklez pumpkin weed strain
sprinklez peach perfection strain
sprinklez peach perfection weed strain
sprinklez new York cheesecake strain
sprinklez weed strain
Arnold palmer strain
torchworld strain
basque burnt cheesecake strain
torchiez weed strain
torchiez weed
brite crawlers strain
gumdropz strain
brite crawlers
all american
all American strain
banana split
banana split strain
birthdaycake marshmallow
buy marshmallow birthdaycake online
marshmallow birthdaycake strain
blueberry pie marshmallow
blueberry pie marshmallow strain
marshmallow
bubblegum
bubblegum strain
bubblegum rainbow
bubblegum rainbow strain
cherry ripe marshmallow
cherry ripe marshmallow strain
choko whip swirl marshmallow
choko whip swirl marshmallow strain
choko covered banana mardsmallow
choko covered banana marshmallow strain
Hawaiian guava ake
jasmine pana cotta
sprinklez strain
buy sprinklez online
lemonberry kush
sprinklez for sale
buy sprinklez strain
sprinklez weed strain for sale
lucky charms
lucky charms cereal
sprinklez weed for sale
bud packs
m&m cookie
buy sprinklez online
milk and cookies
buy sprinklez weed
marble pound cake
marijuana strain
black mamba seeds
mud pie
mississipi mud pie
cannabis seed
psychedelic
sprinklez
sprinklez brand
sprinklez strain
sprinklez weed
original sprinklez brand
oreoz
runtz
sprinklez weed strain
pink vanilla butter cake
og kush
butter cake og
pink vanilla og
la kush cake
wedding cake strain
kush flavoured
rainbow confetti
birthday cake
rainbow sherbet cotton candy
rasoberry cheesecake truffles online
buy sprinklez nyc
buy sprinklez queen
red velvet cheesecake
southern buttermilk pie
sprinklez apple pie
buy sprinklez apple pie online
sprinklez blueberry pancakes
buy blueberry pancakes nyc
sprinklez bubblegum cotton candy
sprinklez candy apple cannabis strain
buy sprinklez candy apple cannabis strain
sprinklez weed strain
buy candy apple strain
sprinklez cherry lemonade
buy sprinklez cherry lemonade
original sprinklez exotic strain
sprinklez brand
sprinklez
sprinklez strain
buy sprinklez exotic online
sprinklez cotton candy
where to buy candy sprinkles
cannabis
sprinklez strain
sprinklez brand
sprinklez fruitty pebles
alien kush strain
sprinklez
sprinklez marshmallow madness
sprinklez for sale
sprinklez brand
cali exotic cannabis strain
original sprinklez brand exotic
sprinklez strain
sprinklez miami
buy sprinklez miami
sprinklez weed strain
buy sprinklez
marijuana strain
brite crawlers
buy gumdropz online
gumdrops mint mojito
gumdropz for sale
Arnold palmer
torchworld
gumdropz logo shorts
fresh baked apple crisp
grapefruit cooler
sprinklez
sprinklez strain
hot fudge sundae
chocolate kush
hot honey butter blondies
sprinklez strain
original torchiez
buy original torchiez
spiced ginger bread choco chunk
torchiez blazin banana
torchiez chili mango
torchiez fire plum crumble
torhiez fuego berry
torchiez jalapeno plum
torchiez red hotz
cookie butter
cookie monster
creamy coconut tres leches
dulce de leche
fantastic funfetti
fresas con crema
frosty snowman marshmallow
fruit gushers
fruity pebblez marshmallow
funfetti marshmallow
honey vanilla
jolly rancher


Автор: Гость (не зарегистрирован), дата: 22 сентября, 2024 - 17:51
#permalink

1688fafa
ufabetjc
paris99th
paris8888
1ufabet
ee88vn
ufa168
ufahub168
ufa168
fafa789
fafa999
zeed24hr
ufaallin
ufasocial
ak1688
fafa100
ufabetlucky
pgslots999
pgslots1688
fafa555
ufa888
ufav8
ufa3333
betflixhub
betflixslots
UFA888
ctfda
gscviagratmn
hailoosport
ufabet
emlaksearch
ufa168
ufa747
ufa365
ufa911
fafa345
ufathai
ufamax888
ufamax
สล็อตเว็บตรง
สล็อตเว็บตรง
บาคาร่า
แทงบอล
1ufabet ปรากฏการณ์ใหม่ของวงการพนัน อัปเดตรูปแบบใหม่ufa168 ทันสมัยกว่าเดิม ให้คุณได้สัมผัสกับฟังก์ชันที่ใช้งานง่าย สล็อตเว็บตรงสะดวกรวดเร็ว และระบบความปลอดภัยขั้นสูงสุดมากกว่าที่เคย สล็อตเว็บตรงยังคงรักษาระดับมาตรฐานและบริการเอาไว้ได้อย่างยอดเยี่ยม ฝาก-ถอนแบบอัตโนมัติบาคาร่าคอมมิชชั่นและราคาค่าน้ำดีที่สุด โปรโมชั่น สิทธิพิเศษจัดเต็มufabet


Автор: ufabet (не зарегистрирован), дата: 22 сентября, 2024 - 17:52
#permalink

1688fafa
ufabetjc
paris99th
paris8888
1ufabet
ee88vn
ufa168
ufahub168
ufa168
fafa789
fafa999
zeed24hr
ufaallin
ufasocial
ak1688
fafa100
ufabetlucky
pgslots999
pgslots1688
fafa555
ufa888
ufav8
ufa3333
betflixhub
betflixslots
UFA888
ctfda
gscviagratmn
hailoosport
ufabet
emlaksearch
ufa168
ufa747
ufa365
ufa911
fafa345
ufathai
ufamax888
ufamax
สล็อตเว็บตรง
สล็อตเว็บตรง
บาคาร่า
แทงบอล
1ufabet ปรากฏการณ์ใหม่ของวงการพนัน อัปเดตรูปแบบใหม่ufa168 ทันสมัยกว่าเดิม ให้คุณได้สัมผัสกับฟังก์ชันที่ใช้งานง่าย สล็อตเว็บตรงสะดวกรวดเร็ว และระบบความปลอดภัยขั้นสูงสุดมากกว่าที่เคย สล็อตเว็บตรงยังคงรักษาระดับมาตรฐานและบริการเอาไว้ได้อย่างยอดเยี่ยม ฝาก-ถอนแบบอัตโนมัติบาคาร่าคอมมิชชั่นและราคาค่าน้ำดีที่สุด โปรโมชั่น สิทธิพิเศษจัดเต็มufabet


Автор: ufathai (не зарегистрирован), дата: 22 сентября, 2024 - 17:54
#permalink

1688fafa
ufabetjc
paris99th
paris8888
1ufabet
ee88vn
ufa168
ufahub168
ufa168
fafa789
fafa999
zeed24hr
ufaallin
ufasocial
ak1688
fafa100
ufabetlucky
pgslots999
pgslots1688
fafa555
ufa888
ufav8
ufa3333
betflixhub
betflixslots
UFA888
ctfda
gscviagratmn
hailoosport
ufabet
emlaksearch
ufa168
ufa747
ufa365
ufa911
fafa345
ufathai
ufamax888
ufamax
สล็อตเว็บตรง
สล็อตเว็บตรง
บาคาร่า
แทงบอล
1ufabet ปรากฏการณ์ใหม่ของวงการพนัน อัปเดตรูปแบบใหม่ufa168 ทันสมัยกว่าเดิม ให้คุณได้สัมผัสกับฟังก์ชันที่ใช้งานง่าย สล็อตเว็บตรงสะดวกรวดเร็ว และระบบความปลอดภัยขั้นสูงสุดมากกว่าที่เคย สล็อตเว็บตรงยังคงรักษาระดับมาตรฐานและบริการเอาไว้ได้อย่างยอดเยี่ยม ฝาก-ถอนแบบอัตโนมัติบาคาร่าคอมมิชชั่นและราคาค่าน้ำดีที่สุด โปรโมชั่น สิทธิพิเศษจัดเต็มufabet


Автор: ufa168 (не зарегистрирован), дата: 22 сентября, 2024 - 17:56
#permalink

1688fafa
ufabetjc
paris99th
paris8888
1ufabet
ee88vn
ufa168
ufahub168
ufa168
fafa789
fafa999
zeed24hr
ufaallin
ufasocial
ak1688
fafa100
ufabetlucky
pgslots999
pgslots1688
fafa555
ufa888
ufav8
ufa3333
betflixhub
betflixslots
UFA888
ctfda
gscviagratmn
hailoosport
ufabet
emlaksearch
ufa168
ufa747
ufa365
ufa911
fafa345
ufathai
ufamax888
ufamax
สล็อตเว็บตรง
สล็อตเว็บตรง
บาคาร่า
แทงบอล
1ufabet ปรากฏการณ์ใหม่ของวงการพนัน อัปเดตรูปแบบใหม่ufa168 ทันสมัยกว่าเดิม ให้คุณได้สัมผัสกับฟังก์ชันที่ใช้งานง่าย สล็อตเว็บตรงสะดวกรวดเร็ว และระบบความปลอดภัยขั้นสูงสุดมากกว่าที่เคย สล็อตเว็บตรงยังคงรักษาระดับมาตรฐานและบริการเอาไว้ได้อย่างยอดเยี่ยม ฝาก-ถอนแบบอัตโนมัติบาคาร่าคอมมิชชั่นและราคาค่าน้ำดีที่สุด โปรโมชั่น สิทธิพิเศษจัดเต็มufabet


Автор: ufabet (не зарегистрирован), дата: 22 сентября, 2024 - 17:56
#permalink

1688fafa
ufabetjc
paris99th
paris8888
1ufabet
ee88vn
ufa168
ufahub168
ufa168
fafa789
fafa999
zeed24hr
ufaallin
ufasocial
ak1688
fafa100
ufabetlucky
pgslots999
pgslots1688
fafa555
ufa888
ufav8
ufa3333
betflixhub
betflixslots
UFA888
ctfda
gscviagratmn
hailoosport
ufabet
emlaksearch
ufa168
ufa747
ufa365
ufa911
fafa345
ufathai
ufamax888
ufamax
สล็อตเว็บตรง
สล็อตเว็บตรง
บาคาร่า
แทงบอล
1ufabet ปรากฏการณ์ใหม่ของวงการพนัน อัปเดตรูปแบบใหม่ufa168 ทันสมัยกว่าเดิม ให้คุณได้สัมผัสกับฟังก์ชันที่ใช้งานง่าย สล็อตเว็บตรงสะดวกรวดเร็ว และระบบความปลอดภัยขั้นสูงสุดมากกว่าที่เคย สล็อตเว็บตรงยังคงรักษาระดับมาตรฐานและบริการเอาไว้ได้อย่างยอดเยี่ยม ฝาก-ถอนแบบอัตโนมัติบาคาร่าคอมมิชชั่นและราคาค่าน้ำดีที่สุด โปรโมชั่น สิทธิพิเศษจัดเต็มufabet


Автор: Adam Smith (не зарегистрирован), дата: 24 сентября, 2024 - 12:18
#permalink

buy sprinklez
torch world exotics
buy sprinklez brand
where to buy sprinklez in new jersey
7up pound cake
sprinklez strain
buy sprinklez weed strain
sweet berry
blue m&m sprinklez
m&m sprinklez strain
m&m weed strain
m&ms strain
m&m sprinklez brand
sprinklez
blueberry shortcake strain
blueberry shortcake weed strain
sprinklez weed
boston cream pie
boston cream pie strain
boston cream pie weed
brooklyn blackout cake
brooklyn blackout cake strain
brooklyn blackout cake weed strain
cookies&cream milkshake strain
sprinklez weed
sprinklez strain
cinnamon roll bread pudding strain
cinnamon roll bread pudding weed strain
funfetti pound cake strain
funfetti pound cake weed strain
confetti cookies dough cream strain
sprinklez for sale
sprinklez mint chip strain
sprinklez mint weed strain
sprinklez munyun strain
sprinklez munyun weed strain
sprinklez pumpkin strain
sprinklez pumpkin weed strain
sprinklez peach perfection strain
sprinklez peach perfection weed strain
sprinklez new York cheesecake strain
sprinklez weed strain
Arnold palmer strain
torchworld strain
basque burnt cheesecake strain
torchiez weed strain
torchiez weed
brite crawlers strain
gumdropz strain
brite crawlers
all american
all American strain
banana split
banana split strain
birthdaycake marshmallow
buy marshmallow birthdaycake online
marshmallow birthdaycake strain
blueberry pie marshmallow
blueberry pie marshmallow strain
marshmallow
bubblegum
bubblegum strain
bubblegum rainbow
bubblegum rainbow strain
cherry ripe marshmallow
cherry ripe marshmallow strain
choko whip swirl marshmallow
choko whip swirl marshmallow strain
choko covered banana mardsmallow
choko covered banana marshmallow strain
Hawaiian guava ake
jasmine pana cotta
sprinklez strain
buy sprinklez online
lemonberry kush
sprinklez for sale
buy sprinklez strain
sprinklez weed strain for sale
lucky charms
lucky charms cereal
sprinklez weed for sale
bud packs
m&m cookie
buy sprinklez online
milk and cookies
buy sprinklez weed
marble pound cake
marijuana strain
black mamba seeds
mud pie
mississipi mud pie
cannabis seed
psychedelic
sprinklez
sprinklez brand
sprinklez strain
sprinklez weed
original sprinklez brand
oreoz
runtz
sprinklez weed strain
pink vanilla butter cake
og kush
butter cake og
pink vanilla og
la kush cake
wedding cake strain
kush flavoured
rainbow confetti
birthday cake
rainbow sherbet cotton candy
rasoberry cheesecake truffles online
buy sprinklez nyc
buy sprinklez queen
red velvet cheesecake
southern buttermilk pie
sprinklez apple pie
buy sprinklez apple pie online
sprinklez blueberry pancakes
buy blueberry pancakes nyc
sprinklez bubblegum cotton candy
sprinklez candy apple cannabis strain
buy sprinklez candy apple cannabis strain
sprinklez weed strain
buy candy apple strain
sprinklez cherry lemonade
buy sprinklez cherry lemonade
original sprinklez exotic strain
sprinklez brand
sprinklez
sprinklez strain
buy sprinklez exotic online
sprinklez cotton candy
where to buy candy sprinkles
cannabis
sprinklez strain
sprinklez brand
sprinklez fruitty pebles
alien kush strain
sprinklez
sprinklez marshmallow madness
sprinklez for sale
sprinklez brand
cali exotic cannabis strain
original sprinklez brand exotic
sprinklez strain
sprinklez miami
buy sprinklez miami
sprinklez weed strain
buy sprinklez
marijuana strain
brite crawlers
buy gumdropz online
gumdrops mint mojito
gumdropz for sale
Arnold palmer
torchworld
gumdropz logo shorts
fresh baked apple crisp
grapefruit cooler
sprinklez
sprinklez strain
hot fudge sundae
chocolate kush
hot honey butter blondies
sprinklez strain
original torchiez
buy original torchiez
spiced ginger bread choco chunk
torchiez blazin banana
torchiez chili mango
torchiez fire plum crumble
torhiez fuego berry
torchiez jalapeno plum
torchiez red hotz
cookie butter
cookie monster
creamy coconut tres leches
dulce de leche
fantastic funfetti
fresas con crema
frosty snowman marshmallow
fruit gushers
fruity pebblez marshmallow
funfetti marshmallow
honey vanilla
jolly rancher


Автор: Sprinklez (не зарегистрирован), дата: 24 сентября, 2024 - 12:18
#permalink

buy sprinklez
torch world exotics
buy sprinklez brand
where to buy sprinklez in new jersey
7up pound cake
sprinklez strain
buy sprinklez weed strain
sweet berry
blue m&m sprinklez
m&m sprinklez strain
m&m weed strain
m&ms strain
m&m sprinklez brand
sprinklez
blueberry shortcake strain
blueberry shortcake weed strain
sprinklez weed
boston cream pie
boston cream pie strain
boston cream pie weed
brooklyn blackout cake
brooklyn blackout cake strain
brooklyn blackout cake weed strain
cookies&cream milkshake strain
sprinklez weed
sprinklez strain
cinnamon roll bread pudding strain
cinnamon roll bread pudding weed strain
funfetti pound cake strain
funfetti pound cake weed strain
confetti cookies dough cream strain
sprinklez for sale
sprinklez mint chip strain
sprinklez mint weed strain
sprinklez munyun strain
sprinklez munyun weed strain
sprinklez pumpkin strain
sprinklez pumpkin weed strain
sprinklez peach perfection strain
sprinklez peach perfection weed strain
sprinklez new York cheesecake strain
sprinklez weed strain
Arnold palmer strain
torchworld strain
basque burnt cheesecake strain
torchiez weed strain
torchiez weed
brite crawlers strain
gumdropz strain
brite crawlers
all american
all American strain
banana split
banana split strain
birthdaycake marshmallow
buy marshmallow birthdaycake online
marshmallow birthdaycake strain
blueberry pie marshmallow
blueberry pie marshmallow strain
marshmallow
bubblegum
bubblegum strain
bubblegum rainbow
bubblegum rainbow strain
cherry ripe marshmallow
cherry ripe marshmallow strain
choko whip swirl marshmallow
choko whip swirl marshmallow strain
choko covered banana mardsmallow
choko covered banana marshmallow strain
Hawaiian guava ake
jasmine pana cotta
sprinklez strain
buy sprinklez online
lemonberry kush
sprinklez for sale
buy sprinklez strain
sprinklez weed strain for sale
lucky charms
lucky charms cereal
sprinklez weed for sale
bud packs
m&m cookie
buy sprinklez online
milk and cookies
buy sprinklez weed
marble pound cake
marijuana strain
black mamba seeds
mud pie
mississipi mud pie
cannabis seed
psychedelic
sprinklez
sprinklez brand
sprinklez strain
sprinklez weed
original sprinklez brand
oreoz
runtz
sprinklez weed strain
pink vanilla butter cake
og kush
butter cake og
pink vanilla og
la kush cake
wedding cake strain
kush flavoured
rainbow confetti
birthday cake
rainbow sherbet cotton candy
rasoberry cheesecake truffles online
buy sprinklez nyc
buy sprinklez queen
red velvet cheesecake
southern buttermilk pie
sprinklez apple pie
buy sprinklez apple pie online
sprinklez blueberry pancakes
buy blueberry pancakes nyc
sprinklez bubblegum cotton candy
sprinklez candy apple cannabis strain
buy sprinklez candy apple cannabis strain
sprinklez weed strain
buy candy apple strain
sprinklez cherry lemonade
buy sprinklez cherry lemonade
original sprinklez exotic strain
sprinklez brand
sprinklez
sprinklez strain
buy sprinklez exotic online
sprinklez cotton candy
where to buy candy sprinkles
cannabis
sprinklez strain
sprinklez brand
sprinklez fruitty pebles
alien kush strain
sprinklez
sprinklez marshmallow madness
sprinklez for sale
sprinklez brand
cali exotic cannabis strain
original sprinklez brand exotic
sprinklez strain
sprinklez miami
buy sprinklez miami
sprinklez weed strain
buy sprinklez
marijuana strain
brite crawlers
buy gumdropz online
gumdrops mint mojito
gumdropz for sale
Arnold palmer
torchworld
gumdropz logo shorts
fresh baked apple crisp
grapefruit cooler
sprinklez
sprinklez strain
hot fudge sundae
chocolate kush
hot honey butter blondies
sprinklez strain
original torchiez
buy original torchiez
spiced ginger bread choco chunk
torchiez blazin banana
torchiez chili mango
torchiez fire plum crumble
torhiez fuego berry
torchiez jalapeno plum
torchiez red hotz
cookie butter
cookie monster
creamy coconut tres leches
dulce de leche
fantastic funfetti
fresas con crema
frosty snowman marshmallow
fruit gushers
fruity pebblez marshmallow
funfetti marshmallow
honey vanilla
jolly rancher


Автор: James Hawk (не зарегистрирован), дата: 17 октября, 2024 - 10:50
#permalink

Отправить комментарий

Приветствуются комментарии:
  • Полезные.
  • Дополняющие прочитанное.
  • Вопросы по прочитанному. Именно по прочитанному, чтобы ответ на него помог другим разобраться в предмете статьи. Другие вопросы могут быть удалены.
    Для остальных вопросов и обсуждений есть форум.
P.S. Лучшее "спасибо" - не комментарий, как все здорово, а рекомендация или ссылка на статью.
Содержание этого поля является приватным и не предназначено к показу.
  • Адреса страниц и электронной почты автоматически преобразуются в ссылки.
  • Разрешены HTML-таги: <strike> <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd> <u> <i> <b> <pre> <img> <abbr> <blockquote> <h1> <h2> <h3> <h4> <h5> <p> <div> <span> <sub> <sup>
  • Строки и параграфы переносятся автоматически.
  • Текстовые смайлы будут заменены на графические.

Подробнее о форматировании

CAPTCHA
Антиспам
7 + 8 =
Введите результат. Например, для 1+3, введите 4.
 
Текущий раздел
Поиск по сайту
Содержание

Учебник javascript

Основные элементы языка

Сундучок с инструментами

Интерфейсы

Все об AJAX

Оптимизация

Разное

Дерево всех статей

Последние комментарии
Последние темы на форуме
Forum