ООП в Javascript: наследование
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 :
Ссылка [[prototype]] работает так:
- Любое запрошенное свойство ищется сначала в
rabbit
- Если свойство там не найдено, то оно ищется в
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 = {}
Получается такая картинка:
Это хорошо, потому что у класса 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 - нужно
- Описать
Animal
- Описать
Rabbit
- Унаследовать кролика от объекта Animal:
Rabbit.prototype = new Animal()
Однако, у такого подхода есть два недостатка:
- Для наследования создается совершенно лишний объект
new Animal()
- Конструктор
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 ).
Этот синтаксис - ни что иное как удобная форма записи для:
F = new Function()
Эта строка cоздает новый объект класса Function (встроенный класс javascript).
Конструктор Function хранит ссылку на Function.prototype, который содержит общие свойства функций: call , apply , constructor , toString и т.п. Поэтому F.[[prototype]] = Function.prototype.
Кстати, за счет такого прототипа все функции и имеют доступ к методам call , apply и т.д.
Создание объекта F можно изобразить так:
На картинке также изображено свойство prototype , которое автоматически устанавливается в new Object() . Свойство constructor также генерируется интерпретатором и показывает обратно, так что по кругу prototype.constructor для функции можно идти бесконечно: F.prototype.constructor === F .
Следующая строка устанавливает свойство F.prototype :
F.prototype = Parent.prototype
До второй строки свойства имели такие значения:
Свойство F.prototype указывало на объект new Object() (справа снизу на рисунке).
Теперь значение поменялось, и старый new Object() перестал быть доступен - ни одна ссылка на него не ведет. Поэтому сборщик мусора убивает его.
Вот так изменения отразятся на картине:
Следующая строка устанавливает свойство 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]
Это присвоение можно изобразить на картинке вот так:
В правом-нижнем углу - как раз и находится 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
}
При создании потомка фабричная функция делает следующее:
- Создает объект родительского класса
- Присваивает ему публичные свойства и методы
- Меняет свойство
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.
Пишите в комментарии, если что.
|
Полезная статья.
Лично я отказался от прототайпа и использую следующий тип наследования...
Чрезвычайно простая и наглядная методика, не требующая дополнительных сущностей, и к тому же, позволяющая множественное наследование, по необходимости.
Метод родительского класса также можно вызвать весьма нехитрым способом. Кроме того, можно на лету менять контент класса, что, например, позволяет вызывать родительский метод как неперегруженный.
Круто. спс!
Разъясните логику поведения. Там в call точно не пропущены '_'?
Этот код не работает, если поля, к которым обращаются методы, заданные в прототипе, определены в конструкторе классов
Зачем в этом коде tobj?
О каких полях идет речь в комментарии по поводу extend ?
Про tobj добавил комментарий развернутый в текст статьи, спасибо.
Под полями я имел свойства. Если свойства, к которым обращаются методы родителя определены в конструкторе родителя (а не в прототипе, что, как вы сами сказали, делать не очень корректно), то код наследуемой функции, не работает. Пример:
А по поводу tobj, да, интересный прием. Я об этом не подумал Но не короче ли записывать просто {}[x] вместо того, что б создавать переменную tobj?
В Вашем коде вызов
man.hasTail()
возвращаетundefined
, потому что вMan()
не вызывается конструктор суперкласса.Чтобы вызвать конструктор родителя (
Animal
), нужноMan
объявить, например, так:После этого код сработает, как Вы и ожидаете, хвост вырастет - мама не горюй
Что касается временного объекта
tobj
- в данном случае это небольшая оптимизация. Действительно, можно убрать эту переменную, и подставить вместо нее{}
.Использование {}[x] вместо tobj[x] не рационально, т. к. каждый раз будет создаваться анонимный объект класса Object.
1)
2)
На каждой интерации создается 2 анонимных объекта.
Интересно. Не думал что javascript настолько функционален.
Я не понимаю, зачем так усложнять организацию наследования ? по мойму, данный метод хороший, но новечку не понятный
я всегда делаю примерно так
Спасибо, удобный и рабочий вариант.
Нечто среднее между наследованием на классах (объект создается
new
) и фабричным подходом: аналогичным образом можно сделать override, приватные переменные.Та же проблема с instanceof, что и у фабричного подхода, т.к нет цепочки прототипов:
Спасибо большое!!!!!!!!!
Перечитал несколько раз но так и не понял почему не работает такая конструкция
Выдает ошибку "g.getBar is not a function"
Потому что в потомке не вызван конструктор родителя.
Не в тему сказано, но может кто нибудь из профессионалов подскажет, возможно ли получить ссылку на родительский объект. К примеру:
Дале к примеру используем ссылку по ее назначению
и, например, в некоторой ф-ии, при щелчке на ссылке получаем
ссылку на нее (то есть на this.child).
Так вот вопрос, как получить значение this.url?
Например так:
function a() {
var object = this;
this.child = document.createElement("a");
this.child.onclick = function() {
alert(object.url);
return;
}
this.url = "http://www.google.com";
}
var obj = new a();
этот способ добраться до методов класса не используя this очень выручает, но если мы запускаем таймер для какого-либо метода класса, то как быть в этом случае? как обойти обращение к объекту экземпляра класса?
function a() {
var object = this;
this.child = document.createElement("a");
this.child.onclick = function() {window.setInterval("alert(obj.url)",1000);
return false;
}
this.url = "http://www.google.com";
}
var obj = new a();
Меня интерескет другой вопрос. Вот код:
Как в методах объекта url докопаться до свойств родительскрго объекта, т.е. String ?
так: String.prototype.toLowerCase
Статья замечательная. Очень понравилось. С одной стороны.
С другой же стороны, после Java, C++ и C#, у меня дрожь по коже от извращений JavaScript
Ну после С+++ джава проще никак не может быть. Но если с пониманием подходить то осилить не сложно. Тем более сейчас материалов на тему более чем достаточно. А криптозащита без джавы ну никак не может
Это все замечательно, только миллион способов типизировать методы у объекта никак не помогают с областью видимости переменных, а указывать var myVar в том же "составном операторе", где и метод this.setMyVar очень не удобно. Может, как-нибудь можно добиться корректной работы такой конструкции?
function Child() {
Parent.call(this);
};
Thank you))))
yes! thank you! fence builders medford oregon
все очень подробно расписаны основы ООП, зачет
У меня не очень много опыта в JS. Но для себя я проблему наследования решил таким образом, хотя по сути это вообще не наследование, а простое расширение возможностей базового объекта.
Это и есть фабричный метод, по сути.
thank you! fencing portland oregon
мне очень нравится позиция о "real-life" автора статьи. Я в короткие сроки освоил JavaScript благодаря javascript.ru, не вдаваясь в ненужные подробности. До этого меня тошнило от JavaScript, та как небыло подходящих книг/статей. Ничего подобного(javascript.ru) я не находил. Автору надо памятник ставить
- Thank you
Мои пару замечаний:
1) Почему нельзя сделать так , как в FullCircle.prototype = Circle.prototype), а именно :
Сразу замечу, что приведенный код работает.
2) Сказано по моему ошибочно :
Нужно так :
Уважаемый kefi,
1) Простое присвоение прототипа - это не наследование. Вместо отдельно методов для родителя (в прототипе родителя) и отдельно - методов для потомка (в его прототипе) - у вас будет один общий прототип. О наследовании тут и речи нет.
2) Видимо, опечатка. Поправил.
2 Илья Кантор > О наследовании тут и речи нет.
Рад, столь быстрому ответу, но -
Хм... Но :
1) В выше приведенной мной ссылке говорится как раз об обратном, - что наследование реализуется имено так , как я описал, при этом таким образом организуется цепочка прототипов от потомков к предкам. Прокомментируйте ,плз., мою ссылку на параграф из справочника WEB-разработчика Ю. Лукача (кроме того, гляньте параграф 3.7.2.3. Наследование
на той же странице ).
2) Приведенный мной способ работает точно также , как и Ваш, только он проще синтаксически.
Да, глянул. Скажите, как методы добавляются в объект при ООП-подходе Лукача?
Есть два способа:
Из текста справочника я так понял, что Лукач использует оба, это слегка некорректно: надо уж либо так либо эдак, структура наследования разная выходит. Скажите, какой мы возьмем для рассмотрения ?
Вы уверены, что цепочка наследования из трех объектов возможна при выбранном подходе и наследовании через приравнивание прототипа?
P.S. Предлагаю вам зарегистрироваться на сайте для продолжения переписки.
Есть два способа: И я так понимаю, ОБА они нужны :
Circle.prototype.area = ... - Это даст нам Не Private члены для Circle ( protected и public )
либо this.area = ... в конструкторе Circle - а Это даст нам Private члены для Circle .
Вы уверены, что цепочка наследования из трех объектов возможна при выбранном подходе и наследовании через приравнивание прототипа? >
Не уверен, но я пытаюсь пока только понять. А Вы можете привести отличие , когда подход , на котрый я ссылаюсь, сработает не так , как Ваш и будет в чем-то хуже ?
PS Хотелось бы где-ни увидеть максимально четкие построенные модели OOПодхода в JavaScript с описанием реализованных концепций, пока все только не очень хорошо формализованный набор разных идей...
Вы не так понимаете. Оба подхода дают public члены класса. Просто в одном случае методы хранятся в прототипе, а в другом - непосредственно в объекте. Ну и, разумеется, объявление через
this
образует замыкания и дает доступ методу к переменным, объявленным черезvar
внутри конструктора (и это уже реальные "приватные" свойства).Просто при использовании приравнивания прототипов для наследования в одном подходе будут одни глюки а в другом - другие.
На предмет глюков смотрите а) как работает override б) как работает цепочка A -> B -> C
Первый пункт. Хорошо, а почему бы тогда просто не сделать так:
Child.prototype={prototype:Parent.prototype};
?
Заранее спасибо.
Прототип объекта (он же [[prototype]],__proto__) и свойство prototype это разные вещи.
Прототип ставится по свойству prototype функции-конструктора.
Ой, точно, понял, что сказал глупость. Приношу извинения.
А если так:
prototype это не функция, его нельзя "вызвать"
Более корректно будет
Но этот (очень простой) подход можно практиковать только если конструктор Parent() не принимает никаких аргументов
[url=http://javascript.ru/tutorial/object/inheritance#protected-chlieny]Здесь[/url]
[quote]protected, т.е такие, к которым могут получить доступ наследники[/quote]
Сказано верно, но вывод не аккуратный (если, хотите, неверный) :
Наследником в классическом ООП считается класс Bird (,а не экземпляр типа Animal ), имеющий наследуемые свойства от предка-класса Animal. Следовательно для упомянутых Вами наследников-классов protected членами будут также свойства объекта Animal.prototype, которые, конечно, будут доступны и в остальных классах-НЕнаследниках Animal , но вот тут уж ничего не поделаешь, кроме уже сказанного Вами - обозначить их подчерком _.
Т.е. для javascript с помощью подчерка можно натянуть три уровня доступа к членам класса ( классы верхнего уровня будут public ) :
private - ( var объявления и внутренние функции ) доступ только изнутри класса(т.е. его конструктора) Animal.
protected - (Animal.propotype._члены с подчерком и this._члены с подчерком) доступ изнутри конструкторов классов-наследников,
но и, поскольку, одновременно, отовсюду через свойства создаваемых экземпляров Animal (new Animal()) , то НЕ рекомендуется использовать НЕ в конструкторе наследника класса Animal подчеркнутое свойство Animal.propotype._член или (new Animal())._член .
public - (Animal.propotype.члены без подчерка и this.члены ) - доступ отовсюду, но для this.члены только через созданые экземпляры класса Animal (new Animal()).член .
I recently embarked on a journey into online casinos, selecting a site renowned for its broad selection of games https://bizzocasinopokies.com/ and excellent user experience. My first experience was with a slot game titled “Cosmic Quest,” which featured an outer space theme with stunning visuals and a captivating soundtrack. The game’s special features, such as “galactic bonuses” and “starship jackpots,” added excitement and variety to each spin.
Во-первых цель(копировать КРОМЕ Object) так не достичь ( если б не во-вторых ) ,
Во-вторых , ее достигать и не требуется, т.к. ВСЕ свойства Object.prototype ( как,кстати, и Object,Fucntion,Function.prototype ) DontEnum .
Да, действительно, IE не видит в for in toString свойство, ну так это можно сделать куда проще и правильнее :
Суть этого метода - в избавлении от лишних свойств, добавленных непосредственно в Object.prototype.
При этом если свойство было унаследовано, то оно копируется. Этим проверка отличается от hasOwnProperty.
Суть этого метода - в избавлении от лишних свойств, добавленных непосредственно в Object.prototype.
1) Ну так для for in и объекта var tobj = {} оператор :
if((typeof tobj[x] == "undefined") || (tobj[x] != src[x]))
совершенно эквивалентен:
if( tobj[x] != src[x] )
2) И потом, зачем же Вы хотите избавиться от свойств, добавленных в Object.prototype пользователем? Нелогично это - А почему только Вы это хотите для Прародителя, а почему не захотеть того же для одного из потомков в цепи наследования src <- Object.prototype?
А ну впрочем, если src есть первый потомок Object.prototype, то - понятно.
Но тогда непонятно это :
При этом если свойство было унаследовано, то оно копируется. Этим проверка отличается от hasOwnProperty.
Вопрос : От кого унаследовано? Если src первый потомок Object.prototype, то достаточно проверять :
if( ! Object.prototype.hasOwnProperty(x) )
Если не первый, то см.выше.
PS . Насчет toString свойства Вы не ответили.
> почему не захотеть того же для одного из потомков в цепи наследования
Свойства 'Object.prototype' и так доступны 'tobj', не обязательно из доступных наследуемых свойств насильно делать свои. Иные же прототипы в цепи возможных прототипов объекта 'src' скорее всего не доступны объекту 'tobj', их свойства подхватываются через 'for-in'.
> toString
Там не только с toString проблема.
https://developer.mozilla.org/en/ECMAScript_DontEnum_attribute
Гм... Второй день не могу засунуть в голову этот текст. Подозреваю, мне подробностей маловато. Но это фигня - почитаю стандарт. Но вот одного я понять не могу - а чего всё так сложно-то? Почему нельзя сделать так:
Вроде всё просто, понятно и работает. Точно так же создаётся один вспомогательный объект, только работает не так хитрожопо.
Пасибо, статью я читал.
По поводу оных пунктов:
1. а) Не понимаю, что в нём лишнего - общие свойства в любом случае где хранятся, так?; б) "Child.prototype = new F()" - это, типа, не объект?
2. На кой ляд ему что-то предусматривать, если это делается функцией Extend в обоих случаях?
Разберем на примере. Вот определение объекта Parent:
Parent
нельзя унаследовать его черезChild.prototype = new Parent()
, потому что в Parent не предусмотрено создание недородителя, годного лишь на прототип. Ошибка будет в браузере.Если мы все же предусмотрим такой вызов, то объект new Parent() все равно будет "лишним", потому что этот Parent нам нужен не для практических целей, для которых он задуман, а только для наследования. В нормальном ООП, которое мы хотим иметь, такого быть не должно.
Такой вот замысловатый код решает следующие проблемы
1) реализует наследование на основе прототипов
2) не создает "лишних" объектов родителей
3) сохраняет функционал instanceof
4) работает во всех современных браузерах
Пример взят из книги http://www.bhv.ru/books/full_contents.php?id=184616
Разумеется, это только идея. Настоящий код обернут в функции create и derive
Илья, спасибо за статью! Хорошая точка отправки для понимания прототипного ООП в javascript.
Предложение
Неясен термин "недородитель, годного лишь на прототип", в статье - ""недоживотное", годное лишь на прототип". Возможно, это и яснее тем, кто новичок в ООП и в javascript. По моему мнению выражение "...в Parent не предусмотрен конструктор без аргументов." или "...в Parent не предусмотрен случай, когда конструктор вызывается без аргументов." будет понятнее людям, имеющим опыт в ООП.
Вопрос
Обязательна ли подпрограмма mixin? Разве не достаточно условиться, что методы в prototype потомка будут добавлены только после вызова extend?
Т.е. можно ли в примере
вызов mixin заменить на
и т.д.?
Заранее благодарю за ответ. Надеюсь, Вы заглядываете в комментарии к старым статьям. :-)
Last week, I took a plunge into online casinos for the first time, opting for a site that had a classic, casino-style design. I began with an online slot game set in an exhilarating space race theme. The game’s futuristic graphics and energetic soundtrack created a thrilling atmosphere. Features like “rocket bonuses” and “space station jackpots” made each spin feel like a race through the cosmos.
After enjoying the slots, I decided to https://verde-casino-pl.pl/ explore live casino games and tried live dealer blackjack. While I had played blackjack before, the live version brought a new level of excitement. The real-time interaction with the dealer and the live dealing of cards made the game more engaging. The strategic elements and the suspense of each hand were greatly enhanced by the live experience.
И всё таки в рассказе про extend есть путаница между [[prototype]] и prototype.
Написано поиск свойств будет идти по цепочке:
Эта строка вводит в заблуждение. Алгоритм получения свойств описан в 8.7.1, 8.6.2.1
И если написать точнее, то свойство ищется по цепочке
Если написать так, то по моему, становится намного понятней.
А так же понятно почему надо использовать mixin(..., ...).
И ясно, что функцию mixin(..., ...) надо использовать после extend (..., ...)
A few weeks ago, I decided to explore online casinos for the first time, choosing a site with a clean, modern aesthetic. My initial game was an online slot with a vibrant treasure island theme. The game’s colorful https://ninewinuk.co.uk/ graphics and adventurous soundtrack created an immersive experience. Features like “treasure chest bonuses” and “island jackpots” made each spin exciting.
After some time with the slots, I moved to the live casino section and tried live dealer poker. While I was familiar with poker, playing it live was a new experience. The interaction with the dealer and the real-time play added a new dimension to the game. The live dealer’s commentary and the suspense of each hand made the game more thrilling.
После заголовка Вторая строка. Меняем F.prototype становится непонятно, зачем вообще это все делается, с какой целью?
Хорошо рассказано о prototype, очень доступно, с примерами. Что же такое constructor понять из контекста статьи сложно.
Несколько примечаний.
1. Класс F можно описать как "Parent, годный лишь на прототип".
2. Условно можно написать extend так:
3. Интересно, что
. И тем не менее, при ненахождении нужного свойства/метода в Object.prototype, интерпретатор не зацикливается.
4. Неужели в JS нет стандартной функции объединения объектов?.. Очень жаль.
4. а ему оно и не нужно ;-)
.ня
Можно ли показать реальный и простой пример использования прототипов... а то я не совсем понимаю зачем нужны "дополнительные свойства". Здесь не всем понятен компьютерный сленг...
Я плохо осознал, зачем в JavaScript притащена такая несвойственная для него сущность как класс?
Пожалуйста, поясните, где и в каком порядке нужно вызывать функции extend и mixin? Из текста статьи я этого не понял. После таких объектно-ориентированных языков как C++ и C# сложно разбираться в искусственном создании наследования. Если можно, приведите пример и объявите одну функцию, реализующую наследование.
Например:
//Объявляем базовый класс любым известным способом
function Parent(varA) {
//переменные класса объявляются здесь
this.myVarA = varA
//Методы класса объявляются здесь
this.methodA = function() {
document.write("methodA" + this.myVarA + "")
}
}
//Объявляем производный класс любым известным способом
function Child(varB) {
//Вызываем конструктор базового класса
Child.superclass.constructor.call(this, varA)
//переменные класса объявляются здесь
this.myVarB = varB
//Методы класса объявляются здесь
this.methodB = function() {
document.write("methodB" + this.myVarB + "")
}
//Наследование обеспечивается вызовом функции extend
extend(Child, Parent)
Вопрос: если пример написан правильно, то тогда зачем вообще нужна функция mixin и где ее вызывать?
Как можно увидеть свойства прототипов встроенных классов?
Ни один из способов не работает. Даже без hasOwnProperty().
или
или сразу
а есть разница как вызывать родительские конструктор Rabbit.superclass.constructor.apply(this, arguments);
или Animal.apply(this);
Разница в том, что ты указываешь от какого ты обьекта наследуешся, если завтра ты захочешь наследоваться от класса Animal11, а у тебя создано5 обьектоа где используеться вместо superclass полное имя - от прийдеться менять везде имя.. а так только в одном месте..
у меня и так и так с работало
не могу понять с наследованием.
Вот код
по идее Cat наследник Animal. Тогда вызов new Cat('barsic') должен создать объект со свойством name равным 'barsic', но alert(cat.name); выводит cat, а не barsic. Не пойму (
Потому что автоматического вызова конструктора суперкласса не происходит. У вас же
function Cat()
пустая.Правильный вариант для вашего примера:
Вот, имеем функцию, которая обеспечивает наследование одного объекта другим
Описание оч сложное, сам читал его три раза.
Экспериментировал оч долго, но так и не добился какихто результатов.
Хочу чтобы было так:
Такое возможно? Если да, то подскажите как это можно сотворить?
Хотел спросить, а не лучше ли в extend вот так делать?
Encouraged by his success with slots, Ryan tried live dealer games and chose live dealer blackjack. The live https://lukkicasino1.com/ dealer format added an extra layer of excitement and realism to the game, making it a favorite for him. The ability to interact with the dealer and other players made the experience more engaging.
Хотел спросить, а не лучше ли в extend вот так делать?
Этот код не работает
Вот походу изучения у меня возник вопрос...
Смотрите:
Существует некий объект
co = {
some : function(){ /*some code*/} // в нем функция
}
и есть некая функция Co1
вопрос: можно ли сделать функцию co1, принадлежащую функцие Some
то есть вызов её выглядел бы так: "Co.Some.Co1"
Приведите пожалуйста пример наследования/заимствования методов/свойств встроенных типов объектов, ну скажем от Image().
Фабричный метод в корне ошибочен (хотя он и рабочий). Вы похоже не до конца поняли смысл prototype. Поясню на примере.
Рассморим два варианта кода:
и
И первый код и второй мы можем использовать одинаковым способом:
и все отлично будет работать.
Однако, второй вариант - безответственное транжирство ресурсов, так как по сути одна и та же функция area будет заново определяться для каждого экземпляра класса. При использовании prototype ничего подобного происходить не будет.
В фабричном методе у вас происходит нечто подобное второму варианту кода, т.е. будет много лишних повторяющихся определений, что отразится на использовании памяти и скорости работы не в лучшую сторону.
Вообще говоря, метод экземпляра - это по сути статический метод класса, аргументом которого выступает экземпляр этого класса. Кстати, таким образом можно без prototype организовывать корректное ООП, однако выйдет многословнее и менее удобно, чем с использованием prototype.
в ответ получаю
Ошибка: Depart.getFisrstDate is not a function
Наверно стоит сначала объявить класс и его прототип, а потом уже создавать экземпляры?
Трудно было по статье понять, что [[prototype]] и prototype - разные вещи, что свойство prototype имеет смысл только для функций, что единственное его назначение - устанавливать истинный прототип объекта при использовании оператора new, что единственный способ создавать объекты - использовать функции и new, что в функции extend свойство superclass - это пользовательское свойство, а не встроенное (как prototype). Все это следовало бы указать.
Очень помогло в понимании прототипов начало 4-й главы переведенного стандарта ecma.
Вообще, глава очень непонятна - новичку тут делать нечего, а тот, кто знаком с другими ОО-языками, увидит много недоговорок. Думаю, что Вам следует пересмотреть всю статью и более структурированно и точно всё изложить.
Илья, а подскажи пожалуйста, есть ли какие-либо недостатки у следующего метода...
Отличия:
- в качестве расширяемого класса передаем не функцию-конструктор а объект со свойствами-методами будущего класс, свойством "constructor" объявляем функцию-конструктор.
- основное отличие - в конструкторе класса-наследника не надо вызывать конструктор класса-родителя - это делается автоматически (подразумевается что он всегда есть)
Явного криминала, вроде, не вижу Автоматический и безусловный вызов конструктора родителя - это конечно нехорошо, но можно ведь добавить параметр в extend, который это дело запрещает...
Если оно работает как надо, то вроде все гуд..
---
Илья
Кто пользовался функцией extend?, я после ее использования не могу вызвать конструктор базового типа
function extend(Child, Parent) {
var F = function() { }
F.prototype = Parent.prototype
Child.prototype = new F()
Child.prototype.constructor = Child
Child.superclass = Parent.prototype
}
Дебаг показывает что в Parent.prototype.constructor лежит конструктор Object, это нормально?
Я пользовался и пользуюсь.
А как зовешь конструктор?
как же все таки создателям этой херни было нечем заняться...
была же джава. так нет...
нубам же нужно создавать быдло-сайтеги, с шевелящимися сиськастыми баннерами
а джава для них сложно слишком
сделали гавноскрипт.
теперь нормальным людям сложно слишком....
Что за возмущения? Не нравится - не используй... Как говорится, не любишь баб, не муча орган.
"JavaScript и Java
Общим заблуждением является то, что JavaScript аналогичен или тесно связан с Java, это не так[17]. Оба языка имеют C-подобный синтаксис, являются объектно-ориентированными и как правило широко используются в клиентских веб-приложениях, на этом их сходство заканчивается:
* Java реализует ООП подход, основанный на классах, JavaScript на прототипах;
* Java имеет статическую типизацию, JavaScript динамическую типизацию;
* Java загружается из скомпилированного байт-кода; JavaScript интерпретируется напрямую из файла (но часто с незаметной JIT-компиляцией)."(wiki)
Оба языка создавались разными людми, и практически параллельно.
Michael, a retired military officer from Seattle, had little experience with gambling but was drawn to online casinos https://kingbillycasinopokies.com/ by their innovative game offerings. He started with a slot game called "Battlefield Riches," which featured a military-themed adventure with impressive visuals and bonus features. The slot’s engaging gameplay, including scatter symbols and bonus rounds, kept him entertained and intrigued.
Почему-то мне кажется, что создатели языка не такие уж и нубы в программировании.
К тому ж при таком подходе можно сказать и про создателей Java. "Вот ведь делать нехер было, придумали свой язык, С++ им не хватало!"
Подскажите, а то понять не могу:
Почему требует ";" перед фиг. скобкой? При этом правильно не работает.
потому что new используете и одновременно пытаетесь описать функцию
Поясните, пожалуйста, почему добавление методов в прототип предпочтительнее?
> Поясните, пожалуйста, почему добавление методов в прототип предпочтительнее?
Потому, что при создании нескольких объектов будет создаваться много копий одних и тех же методов!
Пример
Какая же мутная вещь эти прототипы. Спасибо конечно автору за статью, но я так ничего и не понял Попроще бы как-нибудь это разложить по полочкам для совсем тупых вроде меня. А то я даже сидел и переписывал код, заменял Animal на fAnimal (функция) а animal на oAnimal (объект), что бы не путаться, и всёравно запутался
Какая же мутная вещь эти прототипы. Спасибо конечно автору за статью, но я так ничего и не понял Попроще бы как-нибудь это разложить по полочкам для совсем тупых вроде меня. А то я даже сидел и переписывал код, заменял Animal на fAnimal (функция) а animal на oAnimal (объект), что бы не путаться, и всёравно запутался
И ещё вопрос - в чём преимущество использования прототипов, если они такие мутные и неудобочитаемые. Т.е. вот приведу пример: есть у меня 36 игральных карт, у каждой есть свойства (масть и номер) и функции (положить на стол, дать игроку и т.п.)... Так вот объясните, чем лучше/удобнее
карта.дать_игроку(масть, номер)
чем
дать_игроку(карта, масть, номер)
Зачем встраивать функции в объекты, если можно задавать объекты в аргументах функций и не парить себе мозги?
Разобрался сам! простите за дурацкие вопросы
Вот так я сделал птицу животным:
Заранее извиняюсь за вопрос так как предчувствую что напишу глупость но понять не могу так что...
Никак не пойму зачем нужен этот код.
В ie ваша функция mixin(); прекрасно работает и без нее.
Посещал ваши лекции в Харькове. Очень помогли, спасибо. В благодарность за это дарю, так сказать, метод который я придумал после лекции и активно используем в нашем фреймворке.
когда делаем extend сохраняем перебопределяемую функцию в саму функцию как свойстово parent
что позволяет запросто обойтись без прямых указаний на предка и позволяет легко рефакторить код (вставлять или удалять предков)
вызывается метод предка так
Как вам такая идея?
Добрый вечер!
Не особо ясна роль
, это аналогичная ссылку prototype в Gecko? В чем основное отличие? Если можно на простом примере.
Заранее благодарен!
пример с разделением данных не работает. почему всёравно у хомяков общий массив food??
у меня пример вполне работает.
Единственное что если вы вдруг сделаете
delete someHamster.food
у вас все равно останется родительский food
Конечно маловероятный случай.
Но то что "просто для информации" надо удалить как по мне
Опечатка: В наиболее распространенных
Здравствуйте, коллеги!Статья заслуживает внимания, но позвольте обратить ваше внимание на неточность в статье. Неточность допущена при обьяснении четвёртой строки авторской версии функции extend:
Выдержка из цитаты:
Четвертая строка. Поправить свойство constructor
Свойство
Child.prototype.constructor
осталось старое, и его нужно поправить строкой …Неточность заключается в следующем.Сама операция правильная. Однако поведение операции раскрыто не верно:
В результате выполнения данной операции будет создано новое свойство
Child.prototype.constructor
в контексте обьекта(new F)
. Старое же свойство(new F).[[prototype]].constructor
останется без изменений.Иначе, если следовать поведению согласно цитате, теоретически возникла бы петля: зацикливание при вызове конструктора суперкласса из конструктора потомка. Практически, такое поведение не возможно. JavaScript не разрешает потомкам подменять какие-либо свойства прототипов. Именно подменять.Основанием для вышеизложенных утверждений послужили:
Спасибо автору за хорошую статью.Спасибо всем за внимание.
КАКОЙ РАХИТ придумал этот язык!!! Руки ему оторвать надо, вместе с головой.Уже мозг кипит от этой Javascript. Если говорят что в PHP бардак - то здесь я даже слова не могу подобрать....Полностью согласен, что это гавноскрипт, от которого к сожалению никуда не подеваться. Любят сейчас юзеры всякую прыгающую и мигающую хрень. Если кто то знает нормальную альтернативу этому дерьму - подскажите...
Аргументы будут или считать это пердежом на воду?
А разве описанных в статье извращений недостаточно?)
Где там извращения? Вынужден огорчить, но из Вашей неспособности понять наследование в JS не следует, что язык плох.
Вашей глупости и нетерпения достаточно
Вопрос такой, а при реализации функции наследования, почему-бы в ней не вызывать конструктор родителя сразу?
такое не будет работать, если в иерархии 3 или больше классов. Свойство this.superclass будет одно для всех, таким образом конструктор непосредственного родителя терминального класса будет вызывать постоянно сам себя и зациклится.
Немного изменил функцию наследования, чтобы ее можно было вызывать после определения прототипа, также во внимание принимаются getter'ы и setter'ы:
Пример использования:
Вникаю понемногу в наследование. Не могу разобраться, почему алерты происходят в последовательности 0-3-1-2. В целом, хочу понять, как работает наследование в jQuery
var classWin=(function(){
var classWin=function(){
return new classWin.fn.init()
};
classWin.fn={
constructor: classWin,
init:function(){
alert(3)
},
test:function(){
alert(2)
}
};
classWin.fn.init.prototype = classWin.fn
return classWin
})();
alert(0)
var objWin=new classWin()
alert(1)
Как из test2 вызвать свойство rec родителя?
test2.rec;
только вот у меня extend не заработал почему то...
Статья написана с учётом того, что читатель уже весьма хорошо знает ООП на других языках.
Очень полезная статья.
Но функция extend выносит мозг
Видимо я не все пока понимаю...
1) Почему extend нельзя сократить до:
Что меняет этот трюк с F??
2) Запись:
Добавляет в объект вызова Child свойство superclass
ссылающееся на "абстрактный" класс родителя.
Это мало что дает... ((
А вот как эмулировать __proto__?
Я правильно понимаю, что:
Будет менять ссылку для всех экземпляров объектов Child
Неужели никак нельзя сделать ссылку из объекта на свой прототип-объект?
Спасибо!
Отличная статья.
Прототип действительно напрягает, можно ли как-то обойтись без него?
Какие грабли меня ждут если я буду делать наследование вот таким образом:
Ошибка
Комментарии типа "А что будет, если прототип функции сделать числом и почему статья этого не описывает, это неправильная статья" - не принимаются. Не делают такого в real-life.
Я даже растерялся малость. С таким апломбом, да такая, простите, глупость сказана.
Честно говоря, прототип всегда и безусловно число и только число. Любая ссылка - всего лишь unsigned integer и никак иначе. Выражаясь еще доходчивей, это номер ячейки RAM.
Что, так прям и пишут:
a.prototype = 7 или 0x154Ah ?
Уважаемый, не ведите себя как м... чудак, здравомыслящим людям понятен контекст о котором говорил автор, более того думаю значительная часть посещающих эту страницу знакома с моделями организации памяти. Если вы хотели блеснуть знаниями "подноготной компутеров", милости просим ну хотя бы на wasm.ru, например, блесните там чем-нить и я буду с интересом наблюдать как вас "повозят по столу", иносказательно еснно, я против неоправданного насилия.
После С++ достаточно сложно вникнуть в механизмы наследования JavaScript. Мне не понятно, почему не работает следующий пример:
lada.speed определяется как undefined.
можно поправить ваш код так:
Результат:
Лада 4 100
100
100
200
100
Не подскажете, есть ли в яваскрипте возможность создания абстрактных методов с последующим их переопределением в наследниках?
Если да, то опишите, пожалуйста, пример, начиная с модели наследования.
Абстрактные методы содержатся в абстрактных классах, при использовании абстрактного класса напрямую (не через потомка) вызывается ошибка:
Т.е. достаточно вызвать ошибку в конструкторе абстрактного класса.
вы БЫДЛО!
Статья хорошая для новичков, но некоторые моменты выносят мозг.
1 - сперва автор пишит: объекты наследуют от объектов
после приводит пример:
function Rabbit(name) {
this.name = name
}
Rabbit.prototype = animal
Как я вижу что наследование тут происходит от обьекта "animal" к функции(класса) Rabbit. А не обьект к обьекту.
2 - сперва автор наследование показывает нам простым приравниванием:
Rabbit.prototype = animal
ниже уже через new
Rabbit.prototype = new Animal()
и как видно уже функция(класс) от функции(класса)Дак как правильно? Разъясните пожалуйста.
3 - непонятно чем отличается способ добавления новых методов:
Rabbit.prototype.run = function(..) { ... }
и
this.run= function() {
}
в Rabbit.4 - в коментариях прочитал кучу способов, что бы не засорять память и не использовать суперфункцию extend (которую ваще не понял).
Как все таки лучше новые обьтекты создавать что бы память не засорять?
Как вызвать метод родительского класса:
так не работает
function Rabbit(){
Rabbit.superclass.run.apply(this, ...)
}
Хорошая статья, но я полностью согласен с этим комментарием - объяснение функции extend только еще больше запутывает. К тому же, я считаю, объект должен знать своего родителя по имени. Также я предпочитаю выполнять инициализацию методов объекта в одном месте - в инициализаторе прототипа, который передается в функцию inherit.
Вывод:
c.a: 1
methodA1
c.b: 2
methodB2
c.c: 3
methodC3
A.a_cnt: 6
B.b_cnt: 4
C.c_cnt: 2
b is A: true
c is B: true
По поводу примечания, блок
Свойства-объекты или "иногда прототип это зло"
Что я делаю не так?)
переиначиваете пример из статьи, заменяя объектный тип (массив) базовым (строка)
самое сложное - понять где ты это будешь использовать:D
потом все становится просто
не ебите себе мозг с цепочками прототипов! используйте композицию в связке с call функцией.
Пробую реализовать написанную функцию extend но безрезультатно.
Подскажите, что я не так делаю..
Почему-то мне кажется, что JS вообще не предназначен для наследования "классов" "классами", и нечего на него примерять привычные нам в других языках подходы. Статья, может, и хорошая (хотя не очень иллюстративная и малопонятная — сфокусирована как раз на том, как сэмулировать привычное наследование), но мне кажется, что люди, ушедшие кодить иерархию классов после прочтения этой статьи, становятся жуткими говнокодерами.
Согласен абсолютно, по-моему вся фишка наследования в Javascript заключается в прототипировании
Хочу высказать один как по мне большой недостаток данного метода наследования. Ну или точнее его следствие.
Он не подразумавает вызов цепочек методов настледованных объектов.
Например у нас есть 3 объекта расширяющие друг друга.
SphinxCat -> Cat -> Animal,
пользуясь вышеописанным extend получаем
SphinxCat.superclass = Cat
Cat.superclass = Animal
Допустим по интерфейсу у нас есть у каждого класса метод configure который принимает options, каждый объект в что-либо дописывет в эти options, а последний Animal делает что типа this.options = options.
получается такой код
...SphinxCat...
configure: function(options) {
options.sphinxCat = 1;
// тут пользуемся вызовом парента
this.superclass.configure.call(this, options);
}
...Cat...
configure: function(options) {
options.cat = 1;
// тут пользуемся вызовом парента
this.superclass.configure.call(this, options);
}
...Animal...
configure: function(options) {
options.animal = 1;
// он знает что поледний и делает такое
this.options = options;
}
Пока вроде все логично, но роковая ошибка уже есть.
Попробуем использовать этот код
var myCat = new SphinxCat();
myCat.configure({name: 'My Super Puper Cat'});
по логике у myCat должен теперь быть
this.options = {
name: 'My Super Puper Cat',
SphinxCat: 1,
Cat: 1,
Animal: 1
}
Но на самом деле получим вечный цикл )
Метод вызова парентов через this.superclass.blablabla.apply(this, ...) в чистом виде не будет работать с таким юзкейсом (а согласитесь это реальная задача).
Проблема кроется в скоупах.
Рассмотри подробнее:
Шаг 1
SphinxCat дописал в options SphinxCat = 1 и вызвал configure у this.superclass, но в своем скоупе.
Шаг 2
Cat дописывает в options Cat = 1 и вызывает configure у this.superclass в скоупе SphinxCat (потому что this = SphinxCat)
Шаг 3
Тут вы подумаете что Animal что то там дописывает в options, но нет
Опять Cat начинает работать.
Все потому что на шаге 2 у метода Cat.configure this ссылается на SphinxCat а следовательно this.superclass на Сat.
Интересно было как то решить эту проблему.
В общем-то все верно, работать и не должно. Контекст вызова будет принадлежать объекту, созданному через new или {}, в данном случае new myCat. Достаточно сделать правильный вызов родительских методов через superclass (кто от кого наследуется известно же) и все заработает:
function extend(Child, Parent) {
var F = function() { };
F.prototype = Parent.prototype;
Child.prototype = new F();
Child.prototype.constructor = Child;
Child.superclass = Parent.prototype;
}
Animal = function() {
}
Animal.prototype = {
configure: function(options) {
options.animal = 1;
// он знает что поледний и делает такое
this.options = options;
}
}
Cat = function() {
}
extend(Cat, Animal);
Cat.prototype = {
configure: function(options) {
options.cat = 1;
// тут пользуемся вызовом парента
Cat.superclass.configure.call(this, options);
}
}
SphinxCat = function() {
}
extend(SphinxCat, Cat);
SphinxCat.prototype = {
configure: function(options) {
options.sphinxCat = 1;
// тут пользуемся вызовом парента
SphinxCat.superclass.configure.call(this, options);
}
}
var myCat = new SphinxCat();
myCat.configure({name: 'My Super Puper Cat'});
var opt = myCat.options;
alert(opt.name +" " + opt.animal + opt.cat + opt.sphinxCat);
Если функцию объекта зацепить за событие, то this в функции объекта слетает...
Например:
Или:
Этот код работает
а этот нет
можно узнать почему?
Слетает конструктор у класса Animal.
Если его прописать явно, то заработает:
Animal.prototype.constructor = Animal;
Всё это конечно замечательно, но не раскрыта тема как прототипное объявление методов соединить с приватными методами класса. Проще говоря -- как вызывать приватные методы класса (в частности объявленные через замыкание) в методах объявленных через прототип того же класса.
Статья интересная. Хотя мне и не все понятно... Недавно стал интересоваться ООП как таковым. Не легче ли создавать объекты таким образом?
Статья хорошая и в меру полная, но возникла пара вопросов:
1) в почему бы в коде к разделу "Почему не this.constructor?" строки
не заменить на
,
это должно убрать зацикленность;
2) как правильно instanceof или instanceOf? В тексте встречается и так и так.
Проверил, работает и так, как я написал выше, и так:
Непонятно, зачем было передавать в метод предка контекст потомка?
Спасибо за статью!
Одна из лучших статей в нете, но везде сильно "пахнет" классами.
При понимании данной темы нужно очистить голову от классов.
Сейчас как раз плотненько занялся наследованием прототипов, для себя немного модифицировал функцию extend:
Таким образом, объект всегда имеет свойство __proto__, все объекты наследуются от одного базового объекта, в свою очередь в базовый объект-прототип добавляю следующие методы и свойства:
А теперь самое интересное: НАСЛЕДОВАНИЕ:
Ну конечно, зря я использую наименование extend два раза, нормальное наименование еще не придумал, может кто подскажет?
Работает в IE8, FF и Opera последних версий.
При изучении данной темы понял очень важную вещь, которую трудно понять при переходе с классов:
во-первых, прототип - это просто объект,
во-вторых, если прототипы рассматривать как уровни, то при создании объекта конструктором, объект состоит из двух уровней (тех которые от нас) это сам объект (его свойства устанавливаются внутри конструктора по ссылке this), как нулевой уровень и его прототип как первый уровень.
При изменении объектом своих свойств ( типа this.prop='new prop' ) прототип не изменяется, свойство записывается в нулевой уровень, т.е. prop можно установить в прототипе как свойство по умолчанию, а для того, чтобы изменить прототип я и создал метод getProto. Метод getProto позволяет не только изменить свойство прототипа, но и вызвать перекрытые методы прототипа в контексте нулевого уровня:
Страдаю дефицитом времени, если кому интересно, по-подробнее распишу.
Контрольный вопрос: кто скажет, что будет, если вызвать function extend( Child, new Parent() ) ?
Нашел ошибки.
В IE не работает наследование больше двух прототипов, правильная функция:
Необходимо проверять Child.prototype.hasOwnProperty('__proto__'), т.к. в IE __proto__ - это просто свойство, которое наследуется от прототипов.
Также, в методе getProto, нужно правильно проверять this.type:
function Animal(){
this.property_1 = "A1";
this.property_2 = "A2";
}
function Fish(){
this.property_1 = "F1";
this.property_2 = "F2";
}
function Humster(){
this.property_3 = "H3"
}
var animal = new Animal;
Humster.prototype = animal;
var h1 = new Humster;
var h2 = new Humster;
h2.prototype = new Fish;
alert(h1.property_1 == h2.property_1);function Animal(){
this.property_1 = "A1";
this.property_2 = "A2";
}
function Fish(){
this.property_1 = "F1";
this.property_2 = "F2";
}
function Humster(){
this.property_3 = "H3"
}
var animal = new Animal;
Humster.prototype = animal;
var h1 = new Humster;
var h2 = new Humster;
h2.prototype = new Fish;
alert(h1.property_1 == h2.property_1);
//какой будет результат?
Крайне благодарен автору за статью и за весь цикл учебников javascript.ru! Вырос на этих учебниках - автору реально надо ставить памятник Спасибо - ты помогаешь прогрессировать!
эээ!!!
При вызове animal.move, интерпретатор находит нужный метод в прототипе animal: Animal.prototype.move и выполняет его, устанавливая this в "текущий" объект.
не this устанавливается в текущий объект , а свойство distance устанавливается для текущего объекта (а не для прототипа).
А this будет являться то что перед точкой то есть если у нас
animal.begemot.motor.move() то this в этом методе будет ссылаться на объект animal.begemot.motor
поправьте текст, или исправьте меня
ну или разжуйте поподробней сей факт. а то новичкам приходится по 16 раз перечитывать. из разных источников. Потому что: 1) терминологией не владеют, 2) авторы иногда кое что опускают как само собой разумеющееся, и интуитивно понятное
Запишем свойство canWalk напрямую в объект Rabbit:
animal.canWalk = false
small.canWalk = true
alert(big.canWalk) // false
alert(small.canWalk) // true
У разных кроликов получилось разное значение canWalk, независимое от родителя.
Скажите почему в консоль выводится следующее? 2 свойства canWalk ???
свойства name тоже по два. Видимо, это просто специфично для хромной консоли выводить помимо свойств объекта еще и свойства прототипа. В фаэрфоксе, например, отображается по одному свойству.
animal.canWalk = false
small.canWalk = true
alert(big.canWalk) // false
alert(small.canWalk) // true
У разных кроликов получилось разное значение canWalk, независимое от родителя.
возможно опечатка в первой строчке. Нужно big.canWalk, иначе фраза "независимое от родителя" относится не к двум объектам а только ко второму, первый продолжает брать это свойство из прототипа родителя
Люди, кто может нормально объяснить в чём разница между:
или же если сразу сделать
На простом примере работает и тот и другой способ... Простой пример:
Далее смотрю объект chld через консоль Хрома. При использовании 2-го способа вижу, что у объекта два свойства с !одинаковым! именем some: у одного нормальное значение, которое ему присвоено через вызов конструктора супер-прототипа, а у второго - undefined!)
Также заметил, что строка:
никакой роли не играет - без неё конструкторы обоих прототипов срабатывают также, ка к и с ней. Единственное, что у объекта chld не появляется свойство chld.constructor...
Копаю дальше! Создал класс Top, наследующий от Child!
И вот тут 2-й способ отказался работать нормально, а 1-й - всё чётко наследует
Так почему же так необходимо использовать посредник в виде F?? В чём же разница, кто знает?
Некропост, но все же.
Во 2ом случае
Child.prototype = new Parent();
происходит явный вызов конструктора Parent с пустыми аргументами, поэтому typeof Child.prototype.some == "undefined" для всех экземпляров child, тк контекст вызова this = new Parent(). В самом конструкторе Child вызов Child.superclass.constructor.call( this, someArg ) осуществляется с правильным this ребенка и typeof child.some == "string".
При дальнейшем наследовании в качестве прототипа Top.prototype уже выступает new Child() опять без параметров и some с anotherArg в Top.prototype
будут undefined.
Кстати в jsfiddle такой Top работает.
Основные проблемы такого "наследования" - дублирование переменных и new Parent() может возвратить бяку, что чревато потерей Parent.prototype
var Parent = function() {
return {
"some": "Хрен тебе, а не Parent"
}
}
ПС. Подсветка синтаксиса что-то не работает..
Не понимаю, почему в дочернем объекте нет полей родительского объекта?
И как следствие - невозможно задать параметры для дочернего класса =(
Странно у меня этот пример нормально работает.
И ещё вопрос, есть ли разница как объявлять объект?
var parent = function(){};
или function parent(){};
Если в функцию extend добавить строку
то можно использовать такой синтаксис:
Присваивание целого объекта в качестве прототипа затирает свойство constructor. Ну а "допиленная" версия extend это исправляет.
Всем привет, вот хочу поделиться функцией для наследования. Данная наработка совмещает defineClass Дэвида Фленгана, Klass Стояна Стефанова, комментариев Николаса Закаса и данной статьи. Пока не нашел способ как делать красиво привантые функции
Нашел решение с привант данными, единственное из за того, что данные находятся довольно далеко в цепочке замыканий лучше будет кешировать перед использованием.
это не работает, так как если создать больше двух объектов, то они будут разделять одни и те же приват данные
Олег Архангельский, учиться кататься на велосипедах поощряется, однако использовать стоит то, что уже годами доказало свою эффективность.
Здравствуйте, как можно реализовать следующее: у меня есть несколько функций, например animals(животные), pets(домашние животные) dog(собака). каждая имеет свои методы. Организовать цепочку наследования animals->pets-> dog.
В итоге нужно, чтобы объект создавался следующим способом: var dog1=new animals.pets.dog("Шарик"); (т.е. без создания дополнительных "промежуточных" переменных).
P.S. Видел, подобное реализовано в движке Pixi.js (напр. var t1=new PIXI.Texture.FromImg("img.jpg"););
Заранее спасибо.
я так и не поняла почему метод надо присваивать через прототип? почему нельзя Animal.move = function(n){...} ??
Не могу понять зачем делается
F.prototype = Parent.prototype
Получается, что наследуется прототип родителя, а не родитель. Но зачем нужен именно прототип родителя.
Не удобнее ли наследовать от родителя, а не от его прототипа?
Или я чтото не понял?
И как тогда обратится к свойству именно родителя, а не его прототипа?
Здравствуйте! Замечательный сайт! Моя настольная книга.
Вопрос: поддерживается ли св-во prototype у DOM-объектов?
Хочу присвоить доп свойства всем элементам на странице, через прототип корневого элемента. Это возможно?
При наследовании через прототип, например, мы заранее создаем объект родительского класса и потом присваиваем его прототипам потомков, но если создать объект потомка, то родительский класс при этом не инициализируется заново, т.к. его объект мы уже создали и присвоили прототипам потомков. Как сделать так, чтобы при создании объекта потомка родитель тоже инициализировался. Понятно, что перед каждым созданием объекта потомка можно вызывать
Но может есть решение более элегантное?
Попробовал функцию extend, поччему-то не заработала.
наверное, можно сделать и так:
тест:
тогда свойства и методы, которые должны будут быть переопределены, просто не будут копироваться из предка.
Главное, что наследование определено сразу в конструкторе данного класса и логически предполагает невозможность чего-то типа:
Мне кажется каша у вас в голове.
Зачем объекту порождаемому конструктором давать принудительно свойство prototype? Это свойство есть у функции конструктора.
Наследования здесь нет, вы просто таким изощренным способом добавляете свойства объекта одного конструктора другому.
Попробуйте Майкрософтовский TypeScript, для ООП на JavaScript
он подходит больше всего. Он позволяет строить абстрактные описания
иерархии типов и классов прозрачно и понятно. А так же генерирует
вполне читаемый JavaScript код.
Для чего используется __proto__ в итоге, если ее полный аналог prototype, который работает в IE 10- прекрасно?
В целом статья супер, начало проясняться.
ООП отлично подходит при разработках.
The article is really helpful. I greatly appreciate your contribution. buildnow gg
Anonse erotyczne kolobrzeg
Roksa weronika
Israel withdrew ufa1688 from Gaza in 2005 but, ufabet เข้า สู่ระบบ with the help of Egypt, clamps 1688.com down on the borders of the ทางเข้า Ufa 1688 enclave now governed by แทงบอลออนไลน์ Hamas Islamists. Dream Gaming Palestinian Big gamingauthorities have limited self-rule ลิงค์รับทรัพย์ in the West Bank บาคาร่าwhich is dotted หวยออนไลน์with Israeli settlements.
Shemale Sexkontakte ist eine bewährte Seite, um lokale geile Leute zu finden, die bumsen wollen. Das Beste daran ist, dass alle Frauen, die du hier finden kannst, lokal sind. Sie sind auf der Suche nach einem lokalen Treffen und sie sind ernsthaft daran interessiert, es mit jemandem zu treiben. Sie wissen, was sie wollen und haben keine Angst, es sich selbst zu holen.
Finally! I've been looking for this for a long time. The game which even more interesting than Wordle absurdle wordle
Существует два основных способа реализации ООП в Javascript: наследование и делегирование. Наследование означает, что один объект pokedle fnaf наследует все свойства другого объекта.
I am really happy to have found this site at last. Really informative and meaningful activities, Thanks for the post and effort! Please continue to share more as a blog. I have now saved it to my bookmarks so I can keep in touch with you. foodle
AO Nutten ist keine Escort-Seite im eigentlichen Sinne, aber sie ist definitiv eine gute Alternative zu Backpage und eine meiner neuen Lieblingsstellen für Sex.
Your article makes me feel very interested, the code you share is difficult code and I will also recommend it to my friends basketball stars
I want to say this web site very seriously self-confident me to really make it materialize! Many thanks, Fantastic put up. ทางเข้าสล็อต
I study via your website routinely, and I just believed I’d say sustain the outstanding do The work! It is in fact One of the most amazing weblogs for my part. ทางเข้าสล็อต
I want to say this web site very seriously self-confident me to really make it materialize! Many thanks, Fantastic put up. สล็อตเว็บตรง
I study via your website routinely, and I just believed I’d say sustain the outstanding do The work! It is in fact One of the most amazing weblogs for my part. หวยออนไลน์หวยออนไลน์">
Is there a way you are able to remove me from that service? An impressive share! I’ve just forwarded this onto a colleague who has been conducting a little homework on this. And he actually ordered me dinner simply because I discovered it for him… lol. So allow me to reword this…. Thanks for the meal!! But yeah, thanx for spending time to talk about this topic here on your site. 사설토토
Hey there! I simply wish to give you a huge thumbs up for the great information you’ve got right here on this post. I am coming back to your website for more soon. I enjoy reviewing a blog post that will make individuals think. I genuinely assume this website needs far more factor to consider. I’ll intend be back once more to find out more as well as thanks once again for the info. Thank you for sharing such a great bog it is very 사설토토
I hadn’t discovered such a solution like this. I can at this time look ahead to my future. Thanks a lot so much for your impressive and amazing help. I won’t be reluctant to recommend your web site to any individual who requires direction on this subject matter. 메이저사이트
Thanks for an interesting blog. What else may I get that sort of info written in such a perfect approach? I have an undertaking that I am just now operating on, and I have been on the lookout for such info. Fabulous post, you have denoted out some fantastic points, I likewise think this s a very wonderful website. I will visit again for more quality contents and also, recommend this site to all. Thanks. Very good points you wrote here..Great stuff...I think you've made some truly interesting points.Keep up the good work. It is my first visit to your blog, and I am very impressed with the articles that you serve. Give adequate knowledge for me. Thank you for sharing useful material. I will be back for the more great post. 스포츠토토
This unique blog is without a doubt cool and informative. I have discovered many interesting advices out of this source. I ad love to return again soon. Thanks a lot ! You ave made some good points there. I checked on the web for additional information about the issue and found most people will go along with your views on this website. This particular blog is no doubt entertaining and besides informative. I have picked a bunch of useful things out of this blog. I ad love to visit it again and again. Thanks a bunch! Merely wanna say that this is very helpful , Thanks for taking your time to write this. 먹튀검증
First You got a great blog .I will be interested in more similar topics. i see you got really very useful topics, i will be always checking your blog thanks Thanks for sharing this information. I really like your blog post very much. You have really shared a informative and interesting blog post . I have recently started a blog, the info you provide on this site has helped me greatly. Thanks for all of your time & work. Just saying thanks will not just be sufficient, for the fantasti c lucidity in your writing.
메이저놀이터
Yes i am totally agreed with this article and i just want say that this article is very nice and very informative article.I will make sure to be reading your blog more. You made a good point but I can't help but wonder, what about the other side? !!!!!!Thanks . pretty good post. I just stumbled upon your blog and wanted to say that I have really enjoyed reading your blog posts. Any way I'll be subscribing to your feed and I hope you post again soon. Big thanks for the useful info. 슬롯머신
The post is really superb. It’s varied accessory information that consists during a basic and necessary method. Thanks for sharing this text. The substance is genuinely composed. This web do my paper for me log is frequently sharing useful actualities. Keep sharing a lot of posts. I really loved reading your blog. It was very well authored and easy to understand. Unlike other blogs I have read which are really not that good. Thanks a lot! wow, great, I was wondering how to cure acne naturally. and found your site by google, learned a lot, now i’m a bit clear. I’ve bookmark your site and also add rss. keep us updated. 라이브카지노
I think this is good information, let's talk more about it.
Your article gave me a new idea. I wish I could sympathize with others as well as myself.먹튀사이트
I like the content of your article because it is informative, creative, and contains so many things I hadn't thought of. I think it would be nice if you would post good articles regularly. Good luck for your nice article.토토사이트
It's a lucky day. I was having a hard day, but reading this article gave me strength at the end. Thank you very much.메이저사이트
Статья очень подробная и понятная. Я узнал больше из вашего поста. Спасибо, что поделились ovo game
Nice article! I found many useful information in your blog, it was awesome to read, thanks for sharing this great content, keep sharing.. alchemy wellness spa, massage therapy programs
What a nice post! I'm so happy to read this. 토토사이트추천 What you wrote was very helpful to me. Thank you. Actually, I run a site similar to you. If you have time, could you visit my site? Please leave your comments after reading what I wrote. If you do so, I will actively reflect your opinion. I think it will be a great help to run my site. Have a good day.
A brilliant piece of writing can inspire change in the lives of many readers. I value this resource and ask that it be maintained at all times. I'll always be there for you. usps tracking number
I'm impressed, I must say. Actually rarely can i encounter a blog that's both educative and entertaining, and without a doubt, you could have hit the nail about the head. Your idea is outstanding; the thing is something that too few individuals are speaking intelligently about. We are delighted that we came across this around my try to find some thing with this. songbac
I would like to thank you for the efforts you have put in penning this site. I’m hoping to view the same high-grade content by you later on as well. In truth, your creative writing abilities has motivated me to get my own, personal website now. 먹튀검증
I am impressed that there is so much information about this subject that has been uncovered and you’ve defeated yourself this time 안전놀이터
I will bookmark your weblog and check again here regularly. I’m quite certain I will learn lots of new stuff right here! 먹튀검증
Nice post! Acadecraft, one of the most skilled typesetting companies, comprises a team of designers who efficiently format creative engaging books for primary, secondary, and senior secondary levels to deliver professional typesetting services. 사설토토사이트
It's an insightful tool that offers a glimpse into the experiences of those with color vision deficiency. 토토사이트
Hello exceptional website! Does running a blog similar to this take a large amount of work? I’ve absolutely no knowledge of computer programming however I had been hoping to start my own blog in the near future. https://totovera.com/
It's an insightful tool that offers a glimpse into the experiences of those with color vision deficiency. taylordle
Discover the world of Ayurveda in Hindi at AyurHindi, your ultimate online resource for holistic wellness. Explore ancient healing techniques, herbal remedies, and lifestyle tips rooted in Ayurvedic wisdom. Embrace the power of Ayurveda to achieve balance, rejuvenation, and a harmonious life. Unlock the secrets of natural health and well-being with AyurHindi today!
Set aside my effort to peruse every one of the remarks, however, I truly delighted in the article. It ended up being Very useful to me and I am certain to all the analysts here.. 먹튀검증
Thanks for sharing this information with us. taylordle
I found many useful information in your blog
fantastic points altogether, you just gained a brand new reader. taylordle
I benefited from this site from various articles and topics. Thank you to everyone for their efforts, and thanks to the author of the article.
https://busd-store.com/
В JavaScript объектно-ориентированное программирование (ООП) основано на прототипном наследовании. Вместо классов My Singing Monsters JavaScript использует объекты, которые могут служить прототипами для создания других объектов. Наследование в JavaScript осуществляется путем связывания объектов прототипами.
Interesting post. I Have Been wondering about this issue. so thanks for posting. Pretty cool post.It 's really very nice and Useful post.Thanks
Warenautomat
MyAsianTV is its extensive and multifaceted library of Asian content
The Platform where people of Trinidad can See the Results of Play Wh is PlayWheResult.Net
Aashiqana TV Serial is a mesmerizing tale of love and emotions that has left an indelible mark on the hearts of its viewers. With its captivating storytelling, unforgettable characters, and emotional depth, the show has carved a special place in the hearts of TV audiences. Aashiqana celebrates the beauty and complexity of love, reminding us of the power of human emotions and the importance of cherishing our relationships.
The soul-stirring music of Aashiqana further elevates the show's emotional impact. From melodious love songs to heartrending tunes, the music weaves seamlessly into the storyline, tugging at the heartstrings of the audience.
Bigg Boss has managed to create a massive buzz on social media platforms, where discussions, memes, and trends related to the show are a regular occurrence. The hashtags associated with Bigg Boss episodes trend regularly, showcasing the show's immense popularity among the digital-savvy audience.
Its a great pleasure reading your post.Its full of information I am looking for and I love to post a comment that "The content of your post is awesome" Great work
유럽축구중계
This is actually the kind of information I have been trying to find. Thank you for writing this information 축구중계
This is actually the kind of information I have been trying to find. Thank you for writing this information 축구중계
Glad to chat your blog, I seem to be forward to more reliable articles and I think we all wish to thank so many good articles, blog to share with us 먹튀검증
Cool stuff you have and you keep overhaul every one of us
메이저놀이터
เว็บไซต์ ufa653 เป็นเว็บแทงบอลที่คุณสามารถเข้าถึงและใช้บริการได้ง่าย และคุณสามารถเดิมพันกับ ufabet แทงบอลออนไลน์ได้อย่างแน่นอน ไม่ว่าคุณจะชื่นชอบการแทงบอลทางออนไลน์หรือแทงบอลสด ที่ UFABET มีทุกอย่างที่คุณต้องการให้คุณเดิมพันและสนุกสนาน
เมื่อคุณเล่นกับเราที่ UFA653 คาสิโนเว็บตรง ไม่มีขั้นต่ำ ความสะดวกสบายในการทำรายการการเงินคือสิ่งที่เราให้ความสำคัญอย่างมาก คุณสามารถฝากเงินและถอนเงินได้ที่นี่โดยรวดเร็วและเร็วมาก ใช้เพียง 30 วินาทีเท่านั้น เรามีระบบคาสิโนที่ดีที่สุดที่ช่วยให้คุณสามารถทำรายการทางการเงินได้ง่ายดาย ไม่ต้องติดต่อทางไลน์หรือติดตั้งแอพพลิเคชั่นซับซ้อน คุณสามารถทำทุกอย่างด้วยตัวเอง
คาสิโนออนไลน์เว็บตรงการคาสิโนออนไลน์เป็นอีกหนึ่งวิธีในการสนุกสนานและแต่งงานเพลิดเพลินในเกมคาสิโนที่คุณชื่นชอบ ที่นี่เรามีเว็บไซต์ที่รวมเกมคาสิโนจากทุกค่ายเดิมพันที่น่าตื่นเต้นและสนุกสนานที่ไม่เคยหมดใจ! ความสำคัญสำคัญที่สุดในการเลือกคาสิโนออนไลน์คือความเชื่อถือและความมั่นคงของพื้นฐาน และเรามั่นใจได้ว่าเรามีสิ่งนี้อย่างแน่นอน
Thanks for every other informative blog. 야동
It is the outcome of the match that would determine which of the teams is better. 야동
Thanks for this post. I definitely agree with what you are saying. I have been talking about this subject a lot lately with my mother so hopefully this will get him to see my point of view. Fingers crossed
카지노놀이터
I would like to thank you for the efforts you’ve put in writing this website. I really hope to check out the same high-grade content by you in the future as well. In truth, your creative writing abilities has inspired me to get my own blog now. ufa168 เข้าสู่ระบบ
ChokoCovered Banana Marshmallow
Funfetti Marshmallow
Raspberry Ripple Marshmallow
Strawberry Cheesecake Marshmallow
Rainbow BubbleGum Marshmallow
Key Lime Marshmallow
Blueberry Pie Marshmallow
Fruity Pebblez Marshmallow
Watermelon Marshmallow
Birthdaycake Marshmallow
Blue M&M Sprinklez
Marshmallow Super Duper
Marshmallow Froot Loopz
Gumdropz Concord Grape
Sprinklez Gumdropz
Gumdropz poppin papaya
Gumdropz Mango Mayhem
Gumdropz Wacky Watermelon
Gumdropz Island Punch
Gumdropz Strawberry Splash
Gumdropz Berry Bonanza
Gumdropz Wild Berry
Torchiez Blazin Banana
Torchiez Red Hotz
Original Torchiez
Grapefruit Cooler
Sprinklez Marshmallow Madness
Sprinklez Creamy Peanut Butter
Sprinklez Peach Perfection
Sprinklez Cherry Lemonade
Sprinklez Sweet Tartz
Sprinklez Cotton Candy
Sprinklez Candy Apple Cannabis Strain
Original Sprinklez Brand
Sprinklez Mint Chip
Gumdropz kiwi cooler
Oreo Cookies Marshmallow
Sprinklez Pumpkin strain
RASPBERRY SWIRL MARSHMALLOW
Sprinklez Apple Pie
Torchiez Chili Mango
Marshmallow tripple stack smorez
Marshmallow Twisted
Gumdrops Mint Mojito
Sprinklez New York Cheesecake
Sprinklez Candied Yams
New York Marshmallow
Torchiez Fuego berry
Sprinklez Takeover
Gumdropz Cranberry blast
Torchiez Jalapeno plum
Lemon bomb Marshmallow
Sprinklez Blueberry Pancakes
Marshmallow Jet Puff
Marshmallow Coconut Royale
Gumdropz Tropical Snow
Marshmallow Cloudberry
Spiced Gingerbread Choco Chunk
Gumdropz Cantaloupe Dream
Choko Whip Swirl Marshmallow
Gumdropz Persian Peach
Sprinklez Oreo Cheesecake
Frosty Snowman Marshmallow
Sprinklez Bubblegum CottonCandy
Torchiez Fire Plum Crumble
Sprinklez MIllionaire Shortbread
Marshmallow Sparkling Champagne
Sprinklez Fruitty Pebles
Gumdropz Honeydrew Delight
Hot Honey Butter Blondies
Twisted Caribbean
Confetti Cookies Dough Cream
Cherry Ripe Marshmallow
Southern Buttermilk Pie
Peach Cobbler
Pink Vanilla Butter Cake
Brooklyn Blackout Cake
Hot Fudge Sundae
Caramel Apple Cheesecake
Mississipi Mud Pie
Fresh Baked Apple Crisp
Blueberry Shortcake
Juicy Yellow Watermelon
Strawberry Frosted Flakes Milkshake
Cookies & Cream Milkshake
Orange Creamsicle
White Chocolate Strawberry
Bubblegum
Dulce de Leche
Kiwi Strawberry Splash
Blazed Buttermilk Doughnuts
Deep Fried Oreo Cookies
Rainbow Sherbet Cotton Candy
Mandarin Lime
Strawberry Banana Pudding
Cookies Monster
M&M Cookie Dough Cream
Rainbow Confetti Birthday cake
S'mores Stuffed French Toast
Red Velvet Cheesecake
Fresas Con Crema
Fantastic Funfetti
Froot Loops Ice Cream
Neapolitan
Rainbow Bubblegum
Hawaiian Guava Cake
Banana Upsidedown Cake
Lemon Blueberry Cheesecake Bar
Sour Patch
Boston Cream Pie
Basque Burnt Cheesecake
Fruit Gushers
Gumdropz Cranberry blast
Gumdrops Mint Mojito
Gumdropz kiwi cooler
Gumdropz Concord Grape
Sprinklez Gumdropz
Gumdropz poppin papaya
Gumdropz Mango Mayhem
Gumdropz Wacky Watermelon
Gumdropz Island Punch
Gumdropz Strawberry Splash
Gumdropz Berry Bonanza
Gumdropz Wild Berry
Torchiez Jalapeno plum
Torchiez Fuego berry
Torchiez Chili Mango
Torchiez Red Hotz
Original Torchiez
Grapefruit Cooler
Sprinklez Blueberry Pancakes
Sprinklez Takeover
Sprinklez Candied Yams
Sprinklez New York Cheesecake
Sprinklez Apple Pie
Sprinklez Pumpkin strain
Sprinklez Mint Chip
Blue M&M Sprinklez
Sprinklez Marshmallow Madness
Sprinklez Creamy Peanut Butter
Sprinklez Peach Perfection
Sprinklez Cherry Lemonade
Sprinklez Sweet Tartz
Sprinklez Cotton Candy
Sprinklez Candy Apple
Original Sprinklez Brand
Marshmallow Coconut Royale
Marshmallow Jet Puff
Lemon bomb Marshmallow
New York Marshmallow
Marshmallow Twisted
Marshmallow tripple stack smorez
RASPBERRY SWIRL MARSHMALLOW
Cookies Marshmallow
ChokoCovered Banana Marshmallow
Funfetti Marshmallow
Raspberry Ripple Marshmallow
Strawberry Cheesecake Marshmallow
Rainbow BubbleGum Marshmallow
Key Lime Marshmallow
Marshmallow Raspberry Ripple
Blueberry Pie Marshmallow
Fruity Pebblez Marshmallow
Watermelon Marshmallow
Gumdropz Persian Peach
Choko Whip Swirl Marshmallow
Gumdropz Cantaloupe Dream
Spiced Gingerbread Choco Chunk
Marshmallow Cloudberry
Gumdropz Tropical Snow
Watermelon Marshmallow
Birthdaycake Marshmallow
Marshmallow Super Duper
Marshmallow Froot Loopz
Frosty Snowman Marshmallow
Torchiez Fire Plum Crumble
Sprinklez Oreo Cheesecake
Sprinklez Bubblegum CottonCandy
Sprinklez MIllionaire Shortbread
Marshmallow Sparkling Champagne
Sprinklez Fruitty Pebles
Gumdropz Honeydrew Delight
Hot Honey Butter Blondies
Twisted Caribbean
Confetti Cookies Dough Cream
Cherry Ripe Marshmallow
Southern Buttermilk Pie
Peach Cobbler
Pink Vanilla Butter Cake
Brooklyn Blackout Cake
Hot Fudge Sundae
Caramel Apple Cheesecake
Mississipi Mud Pie
Fresh Baked Apple Crisp
Blueberry Shortcake
Juicy Yellow Watermelon
Strawberry Frosted Flakes Milkshake
Cookies & Cream Milkshake
Orange Creamsicle
White Chocolate Strawberry
Bubblegum
Dulce de Leche
Blazed Buttermilk Doughnuts
Kiwi Strawberry Splash
Deep Fried Oreo Cookies
Rainbow Sherbet Cotton Candy
Mandarin Lime
Strawberry Banana Pudding
Cookies Monster
M&M Cookie Dough Cream
Rainbow Confetti Birthday cake
S'mores Stuffed French Toast
Fresas Con Crema
Red Velvet Cheesecake
Fantastic Funfetti
Froot Loops Ice Cream
Neapolitan
Bubblegum Rainbow
Banana Upsidedown Cake
Hawaiian Guava Cake
Sour Patch
Lemon Blueberry Cheesecake Bar
Basque Burnt Cheesecake
Boston Cream Pie
I was suggested this web site by my cousin. I am not sure whether this post is written by him as nobody else know such detailed about my problem. You’re wonderful! Thanks! betflix thai
귀하의 블로그가 너무 놀랍습니다. 나는 내가보고있는 것을 쉽게 발견했다. 또한 콘텐츠 품질이 굉장합니다. 넛지 주셔서 감사합니다!
"초기 당신은 멋진 블로그를 얻었습니다. 나는 결심에 더하기 균일 한 분을 포함합니다. 나는 당신이 정말로 매우 기능적인 문제를 가지고 있다고 생각합니다. 나는 항상 당신의 블로그 축복을 확인하고 있습니다. 먹튀검증
THE ONE CARGO ประกอบธุรกิจบริการ คาร์โก้ นำเข้าสินค้าจากจีน จัดหา จัดซื้อ และตรวจสอบสินค้าคุณภาพดีในราคาคุ้มค่า จากผู้ผลิตที่ได้มาตฐานในประเทศจีน พร้อมทั้งบริการหลังการขาย เพื่อเพิ่มความมั่นใจ และตอบสนองความต้องการของลูกค้าอย่างดีที่สุด
1688fafa
ufabetjc
paris99th
paris8888
1ufabet
ee88vn
ufa168
ufahub168
ufa168
fafa789
fafa999
zeed24hr
ufaallin
ufasocial
ยินดีต้อนรับเข้าสู่เว็บไซต์ UFABETJC
1688fafa
ufabetjc
paris99th
paris8888
1ufabet
ee88vn
ufa168
ufahub168
ufa168
fafa789
fafa999
zeed24hr
ufaallin
ufasocial
ak1688
แทงบอล1x2
แทงบอลสเต็ป
แทงบอลสด
//แทงบอลเต็ง
แทงบอลไทยลีก
แทงบอลคู่คี่
แทงบอลลูกเตะมุม
แทงบอลต่างประเทศ
แทงบอลไทยลีก-2
แทงบอลบนมือถือ
sa-gaming
sexy-baccarat
venus-casino
ebet
big-gaming
gdg
sicbo-online
dragon-tiger/
roulette
baccarat
slot-online
เกมยิงปลา เกมยิงปลา
เกมยิงปลา
น้ำเต้าปูปลา
หวยรัฐบาลออนไลน์
หวยหุ้นไทย
หวยยี่กี
หวยลาว
หวยฮานอย
หวยมาเลย์
แทงอเมริกันฟุตบอล
ไก่ชน-ufabet/
แทงมวยออนไลน์/
แทงบาสออนไลน์
แทงแบดมินตัน/
1688fafa
ufabetjc
paris99th
paris8888
1ufabet
ee88vn
ufa168
ufahub168
ufa168
fafa789
fafa999
zeed24hr
ufaallin
ufasocial
ak1688
fafa100
ufabetlucky
pgslots999
เพื่อให้เข้าถึงการเล่นได้อย่างมีประสิทธิภาพ และสามารถเอาชนะเดิมพันในการเล่นได้ง่ายขึ้น รับรองเลยว่า ถ้าคุณเข้าใจและเลือกทีมได้อย่างถูกต้อง คุณจะสามารถทำเงินจากการเล่นจำนวนมาก แต่อย่างไรก็ตาม แม้คุณจะเลือกเว็บในการเล่นได้ดี แต่ก็ไม่อาจการันตีได้ว่า คุณจะเอาชนะเดิมพันในการเล่นได้ เพราะฉะนั้นก็อยากให้คุณได้ศึกษาเทคนิคการเล่นให้ดี เพื่อให้เข้าถึงการเล่น ufabet เว็บตรง ได้อย่างมีประสิทธิภาพ
1688fafa
ufabetjc
paris99th
paris8888
1ufabet
ee88vn
ufa168
ufahub168
ufa168
fafa789
fafa999
zeed24hr
ufaallin
ufasocial
ak1688
fafa100
ufabetlucky
pgslots999
pgslots1688
fafa555
buy sprinklez
buy sprinklez brand
buy sprinklez brooklyn
buy sprinklez bronx
buy sprinklez cali
buy sprinklez flavors
buy sprinklez la
buy sprinklez long island
buy sprinklez los angeles
buy sorinklez manhattan
buy sprinklez near me
buy sprinklez new york
buy sprinklez nyc
buy sprinklez ny
buy sprinklez online
buy sprinklez queens
buy sprinklez staten island
buy sprinklez strain
buy sprinklez weed
buy sprinklez westchester
sprinklez for sale
sprinklez for sale bronx
sprinklez for sale brooklyn
sprinklez for sale near me
sprinklez for sale harlem
sprinklez for sale online
sprinklez for sale queens
sprinklez for sale ny
sprinklez for sale nyc
birthdaycake
birthdaycake marshmallow
buy marshmallow birthdaycake online
buy sprinklez marshmallow strain online
buy sprinklez strain online
marshmallow birthdaycake
marshmallow birthdaycake strain
marshmallow sprinklez brand
marshmallow strain for sale
marshmallow weed for sale new york
sprinklez brand for sale
sprinklez in stock
sprinklez near me
sprinklez new york
buy sprinklez weed
buy sprinklez weed bronx
buy sprinklez weed new jersey
buy sprinklez weed new york
buy sprinklez weed nyc
buy sprinklez weed queens
buy sprinklez weed online
buy sprinklez in alabama
buy sprinklez in arizona
buy sprinklez in arkansas
buy sprinklez in chicago
buy sprinklez in connecticut
buy sprinklez in delaware
buy sprinklez in Georgia usa
buy sprinklez in illinois
buy sprinklez in indiana
buy sprinklez in iowa usa
buy sprinklez in kentucky
buy sprinklez in louisiana
buy sprinklez in ma
buy sprinklez in maine
buy sprinklez in maryland
buy sprinklez in new hamsphire
buy sprinklez in nh
buy sprinklez in ohio usa
buy sprinklez in oklahoma
buy sprinklez in pennsylvania
buy sprinklez in south carolina
buy sprinklez tennessee
buy sprinklez in texas
buy sprinklez in west virginia
buy sprinklez in virginia
buy sprinklez michigan
buy sprinklez near me new jersey
buy sprinklez north carolina
order sprinklez brand
order sprinklez bronx
order sprinklez brooklyn
order sprinklez new york
order sprinklez online
order sprinklez weed online
sprinklez for sale new jersey
sprinklez for sale nj
sprinklez weed
sprinklez weed brooklyn
sprinklez weed for sale brooklyn
sprinklez weed for sale bronx
sprinklez weed for sale cali
sprinklez weed for sale michigan
sprinklez weed for sale new jersey
sprinklez weed for sale new york
sprinklez weed for sale nj
sprinklez weed for sale ny
sprinklez weed for sale nyc
sprinklez weed for sale online
sprinklez weed for sale queens
sprinklez weed strain
sprinklez brand new jersey
sprinklez connecticut
sprinklez flavor for sale
sprinklez florida
sprinklez for sale ohio
sprinklez strain for sale
sprinklez texas
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
buy sprinklez
buy sprinklez brand
buy sprinklez brooklyn
buy sprinklez bronx
buy sprinklez cali
buy sprinklez flavors
buy sprinklez la
buy sprinklez long island
buy sprinklez los angeles
buy sorinklez manhattan
buy sprinklez near me
buy sprinklez new york
buy sprinklez nyc
buy sprinklez ny
buy sprinklez online
buy sprinklez queens
buy sprinklez staten island
buy sprinklez strain
buy sprinklez weed
buy sprinklez westchester
sprinklez for sale
sprinklez for sale bronx
sprinklez for sale brooklyn
sprinklez for sale near me
sprinklez for sale harlem
sprinklez for sale online
sprinklez for sale queens
sprinklez for sale ny
sprinklez for sale nyc
birthdaycake
birthdaycake marshmallow
buy marshmallow birthdaycake online
buy sprinklez marshmallow strain online
buy sprinklez strain online
marshmallow birthdaycake
marshmallow birthdaycake strain
marshmallow sprinklez brand
marshmallow strain for sale
marshmallow weed for sale new york
sprinklez brand for sale
sprinklez in stock
sprinklez near me
sprinklez new york
buy sprinklez weed
buy sprinklez weed bronx
buy sprinklez weed new jersey
buy sprinklez weed new york
buy sprinklez weed nyc
buy sprinklez weed queens
buy sprinklez weed online
buy sprinklez in alabama
buy sprinklez in arizona
buy sprinklez in arkansas
buy sprinklez in chicago
buy sprinklez in connecticut
buy sprinklez in delaware
buy sprinklez in Georgia usa
buy sprinklez in illinois
buy sprinklez in indiana
buy sprinklez in iowa usa
buy sprinklez in kentucky
buy sprinklez in louisiana
buy sprinklez in ma
buy sprinklez in maine
buy sprinklez in maryland
buy sprinklez in new hamsphire
buy sprinklez in nh
buy sprinklez in ohio usa
buy sprinklez in oklahoma
buy sprinklez in pennsylvania
buy sprinklez in south carolina
buy sprinklez tennessee
buy sprinklez in texas
buy sprinklez in west virginia
buy sprinklez in virginia
buy sprinklez michigan
buy sprinklez near me new jersey
buy sprinklez north carolina
order sprinklez brand
order sprinklez bronx
order sprinklez brooklyn
order sprinklez new york
order sprinklez online
order sprinklez weed online
sprinklez for sale new jersey
sprinklez for sale nj
sprinklez weed
sprinklez weed brooklyn
sprinklez weed for sale brooklyn
sprinklez weed for sale bronx
sprinklez weed for sale cali
sprinklez weed for sale michigan
sprinklez weed for sale new jersey
sprinklez weed for sale new york
sprinklez weed for sale nj
sprinklez weed for sale ny
sprinklez weed for sale nyc
sprinklez weed for sale online
sprinklez weed for sale queens
sprinklez weed strain
sprinklez brand new jersey
sprinklez connecticut
sprinklez flavor for sale
sprinklez florida
sprinklez for sale ohio
sprinklez strain for sale
sprinklez texas
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
Experience the adrenaline rush of poly track , where players must complete a series of challenging tracks as quickly as possible. The goal is to navigate through obstacles and sharp turns, avoiding crashes. Success in the game requires mastering each track and perfecting your driving skills.
Отправить комментарий
Приветствуются комментарии:Для остальных вопросов и обсуждений есть форум.