Объекты обладают неограниченным уровнем вложенности. Обращение к свойству объекта происходит через точку или квадратные скобки:
var obj = { level1: { level2: {level3: 42} } };
obj.level1.level2.level3; // > 42
obj['level1']['level2']['level3']; // > 42
При использовании dot-синтаксиса свойство объекта должно соответствовать правилам именования идентификаторов (не быть зарезервированным словом и не начинаться с цифры, не содержать знаков препинания). В противном случае используется []-синтаксис и название свойства заключается в кавычки (исключение возможно только если название свойства - число, так обеспечивается массивоподобный синтаксис).
var obj2 = { 1: { 2: { 3: 42 } } };
obj2[1][2][3]; // > 42
Вместо строки, содержащей имя метода, можно использовать переменную:
var l1 = "level1", l2 = "level2", l3 = "level3";
obj[l1][l2][l3]; // > 42
Внимание! Любая переменная, которая используется в качестве индекса при []-синтаксисе преобразуется к строковму литералу при помощи метода .toString(). Поэтому, например:
var x = {}, y = {};
var y[x] = 1;
console.log(y); // { '[object: Object'] : 1 }
Внимание! Любая переменная, которая используется в качестве индекса при []-синтаксисе преобразуется к строковму литералу при помощи метода .toString(). Поэтому, например:
var x = {}, y = {};
var y[x] = 1;
console.log(y); // { '[object: Object'] : 1 }
Попытка обратиться к несуществующему свойству вернёт undefined. Для установки значений по умолчанию можно использовать ||:
x = obj.level5 || null;
y = obj.property || "(none)";
Попытка обращения к вложенному свойству несуществующего свойства (свойству объекта undefined по сути) вызовет ошибку "TypeError":
x = obj.level4.level5; // > TypeError
Для предотвращения ошибки можно использовать &&. Благодаря ленивым вычислениям конструкция вернет undefined и не дойдет до ошибки:
x = obj.level4 && obj.level4.level5; // > undefined
Значение свойства объекта устанавливается операцией присваивания. Если свойство уже есть значение замещается, если свойства нет - объект расширяется путем добавления нового свойства.
Объекты всегда передаются по ссылке и никогда не копируются. Единственный способ скопировать объект - создать новый и скопировать все его свойства (причем каскадно, потому что свойства объекты также объекты и вместо их копирования произойдет создание новых ссылок).
var x = { a: 1, b: 2, c: { d: 5 } }
var y = { m: 10, n: 20 }
y.p = x.c;
x.c.d = 100;
y.p.d; // > 100 (!)
Функции служат для повторного использования кода, сокрытия данных, композиции, а также определяют поведение объектов. Объекты, созданные как объектные литералы, создаются на основе Object.prototype. Функции - на основе Function.prototype.
При создании функции (как литерала или при помощи new Function) автоматически происходит следующее присваивание:
this.prototype = { constructor: this };
Т.е. фукция получает свойство prototype, которому присваивается объект с единственным свойством constructor ссылающимся на создаваемую функцию. Свойство prototype получает любая функция т.к. заранее неизвестно какая из них будет использована как конструктор.
Функция, которая вызывается не как метод объекта, выполняется как обычная функция. В этом случае this, используемый в ее теле, сопоставляется с глобальным объектом (или с undefined в строгом режиме). Данное ограничение языка затрудняет использование вложенных функций. Даже если внешняя функция выполняется как метод в контексте какого-либо объекта, ее внутренние функции утрачивают этот контекст при обращении к this. Для этого используют замену this = that и используют во внутренних вспомогательных функциях метода that.
Каррирование - замена функции от нескольких аргументов на функцию с меньшим числом аргументов. Вместо аргументов, значения которых заведомо известны, используются константы.
Так, функцию возведения в произвольную степень можно каррировать до функции, возводящей в какую-то конкретную степень - квадрат, куб и т.д.
function curry(exp) {
return function(base) {
return Math.pow(base, exp);
}
}
var square = curry(2);
var cube = curry(3);
square(10); // > 100
cube(10); // > 1000
Создание объектов в каскадно-функциональном стиле:
Обычно создание объекта сводится к объявлению его конструкторов, определению свойств и методов прототипа, а также объектов, от которых описываемый "класс" будет наследовать свойства и методы. Вместо императивного стиля можно определить несколько методов для прототипа объекта Function которые позволят производить описание новых объектов в каскадном стиле:
// Метаметод, позволяющий добавлять новые методы любым функциям.
// Метод добавляется только если его не существовало ранее.
Function.prototype.method = function (name, func) {
if (!this.prototype[name]) {
this.prototype[name] = func;
}
return this;
};
// От какого объекта создаваемый объект будет наследовать методы и свойства.
// Должен вызываться до определения собственных свойств и методов.
Function.method('inherits', function (Parent) {
this.prototype = new Parent( );
return this;
});
Использование этих функций сведётся к следующей цепочке вызовов:
var Cat = function (name) {
this.name = name;
this.saying = 'meow';
}.
inherits(Mammal).
method('purr', function (n) {
var i, s = '';
for (i = 0; i < n; i += 1) {
if (s) {
s += '-';
}
s += 'r';
}
return s;
}).
method('get_name', function() {
return this.says() + ' ' + this.name + ' ' + this.says();
});
Вместо конструкторов наследование можно реализовать по альтернативной, т.н. функциональной схеме (не имеет ничего общего с функциональным программированием). Этот способ имеет ряд преимуществ, среди которых подлинно приватные свойства и методы, а также возможность использование суперметодов (методов объекта от которого мы наследуем):
Объекты будут создаваться при помощи функции, однако она не должна использовать префикс new и следовательно не обязана начинаться с большой буквы (признак обычных конструкторов). Эта функция будет выполнять следующие действия:
1. Любым способом создаётся новый объект. Это может быть использование конструктора и new, Object.create, объектного литерала {} или функции, возвращающей объект. Именно этот объект будет результатом выполнения функции.
2. При необходимости объявляются приватные переменные и функции (как обычные vars и functions). Доступ к этим переменным и функциям извне невозможен.
3. Новый объект (созданный на шаге 1) расширяется методами и свойствами. Эти методы и свойства имеют эксклюзивный доступ к переменным и функциям (приватным свойствам и методам), созданным на шаге 2.
4. Возвращаем объект.
var constructor = function (init_data, object_to_extend) {
// init_data - объект, содержащий набор пар ключ-значение для
// инициализации создаваемого объекта.
// object_to_extend - опциональный объект, который можно расширить
// вместо создания нового объекта.
var that = object_to_extend || {};
// Эти свойство и метод будут доступны только внутри объекта.
var priv_prop = init_data.prop || 42;
var priv_func = function() { return priv_prop * 10; };
// Эти свойство и метод пользуются приватным свойством и методом,
// но будут сделаны публичными, став свойствами возвращаемого
// объекта. "Двухходовое" присваивание позволяет использовать
// внутри объекта методы и свойства по их внутренним именам.
var pub_prop = 100;
var pub_func = function() {
return pub_prop + priv_prop + priv_func(); };
that.pub_prop = pub_prop;
that.pub_func = pub_func;
return that;
};
var obj = constructor({prop: 8});
obj.pub_func(); // > 188
var obj2 = { name: "Dan" };
obj2 = constructor({}, obj2);
obj2.pub_func(); // > 562
console.log(obj2); // > { name: 'Dan', pub_prop: 100,
// pub_func: [Function] }
В наследующем объекте бывает нужно переопределить метод с некоторым именем, при этом в методе объекта-потомка может использоваться результат вызова метода родителя. В этом случае возможны следующие варианты:
var enhanced_constructor = function(init_data) {
var that = constructor(init_data);
var superior_func = that.pub_func; // 1
var superior_func = that.superior('pub_func'); // 2
var superior_func = that.pub_func.bind(that); // 3
}
При создании функции (как литерала или при помощи new Function) автоматически происходит следующее присваивание:
this.prototype = { constructor: this };
Т.е. фукция получает свойство prototype, которому присваивается объект с единственным свойством constructor ссылающимся на создаваемую функцию. Свойство prototype получает любая функция т.к. заранее неизвестно какая из них будет использована как конструктор.
Функция, которая вызывается не как метод объекта, выполняется как обычная функция. В этом случае this, используемый в ее теле, сопоставляется с глобальным объектом (или с undefined в строгом режиме). Данное ограничение языка затрудняет использование вложенных функций. Даже если внешняя функция выполняется как метод в контексте какого-либо объекта, ее внутренние функции утрачивают этот контекст при обращении к this. Для этого используют замену this = that и используют во внутренних вспомогательных функциях метода that.
Каррирование - замена функции от нескольких аргументов на функцию с меньшим числом аргументов. Вместо аргументов, значения которых заведомо известны, используются константы.
Так, функцию возведения в произвольную степень можно каррировать до функции, возводящей в какую-то конкретную степень - квадрат, куб и т.д.
function curry(exp) {
return function(base) {
return Math.pow(base, exp);
}
}
var square = curry(2);
var cube = curry(3);
square(10); // > 100
cube(10); // > 1000
Создание объектов в каскадно-функциональном стиле:
Обычно создание объекта сводится к объявлению его конструкторов, определению свойств и методов прототипа, а также объектов, от которых описываемый "класс" будет наследовать свойства и методы. Вместо императивного стиля можно определить несколько методов для прототипа объекта Function которые позволят производить описание новых объектов в каскадном стиле:
// Метаметод, позволяющий добавлять новые методы любым функциям.
// Метод добавляется только если его не существовало ранее.
Function.prototype.method = function (name, func) {
if (!this.prototype[name]) {
this.prototype[name] = func;
}
return this;
};
// От какого объекта создаваемый объект будет наследовать методы и свойства.
// Должен вызываться до определения собственных свойств и методов.
Function.method('inherits', function (Parent) {
this.prototype = new Parent( );
return this;
});
Использование этих функций сведётся к следующей цепочке вызовов:
var Cat = function (name) {
this.name = name;
this.saying = 'meow';
}.
inherits(Mammal).
method('purr', function (n) {
var i, s = '';
for (i = 0; i < n; i += 1) {
if (s) {
s += '-';
}
s += 'r';
}
return s;
}).
method('get_name', function() {
return this.says() + ' ' + this.name + ' ' + this.says();
});
Вместо конструкторов наследование можно реализовать по альтернативной, т.н. функциональной схеме (не имеет ничего общего с функциональным программированием). Этот способ имеет ряд преимуществ, среди которых подлинно приватные свойства и методы, а также возможность использование суперметодов (методов объекта от которого мы наследуем):
Объекты будут создаваться при помощи функции, однако она не должна использовать префикс new и следовательно не обязана начинаться с большой буквы (признак обычных конструкторов). Эта функция будет выполнять следующие действия:
1. Любым способом создаётся новый объект. Это может быть использование конструктора и new, Object.create, объектного литерала {} или функции, возвращающей объект. Именно этот объект будет результатом выполнения функции.
2. При необходимости объявляются приватные переменные и функции (как обычные vars и functions). Доступ к этим переменным и функциям извне невозможен.
3. Новый объект (созданный на шаге 1) расширяется методами и свойствами. Эти методы и свойства имеют эксклюзивный доступ к переменным и функциям (приватным свойствам и методам), созданным на шаге 2.
4. Возвращаем объект.
var constructor = function (init_data, object_to_extend) {
// init_data - объект, содержащий набор пар ключ-значение для
// инициализации создаваемого объекта.
// object_to_extend - опциональный объект, который можно расширить
// вместо создания нового объекта.
var that = object_to_extend || {};
// Эти свойство и метод будут доступны только внутри объекта.
var priv_prop = init_data.prop || 42;
var priv_func = function() { return priv_prop * 10; };
// Эти свойство и метод пользуются приватным свойством и методом,
// но будут сделаны публичными, став свойствами возвращаемого
// объекта. "Двухходовое" присваивание позволяет использовать
// внутри объекта методы и свойства по их внутренним именам.
var pub_prop = 100;
var pub_func = function() {
return pub_prop + priv_prop + priv_func(); };
that.pub_prop = pub_prop;
that.pub_func = pub_func;
return that;
};
var obj = constructor({prop: 8});
obj.pub_func(); // > 188
var obj2 = { name: "Dan" };
obj2 = constructor({}, obj2);
obj2.pub_func(); // > 562
console.log(obj2); // > { name: 'Dan', pub_prop: 100,
// pub_func: [Function] }
В наследующем объекте бывает нужно переопределить метод с некоторым именем, при этом в методе объекта-потомка может использоваться результат вызова метода родителя. В этом случае возможны следующие варианты:
var enhanced_constructor = function(init_data) {
var that = constructor(init_data);
var superior_func = that.pub_func; // 1
var superior_func = that.superior('pub_func'); // 2
var superior_func = that.pub_func.bind(that); // 3
}
Комментариев нет :
Отправить комментарий