Сложный объект может содержать любое допустимое значение JavaScript. В следующем коде я создаю Object()
именем myObject
а затем добавляю свойства, представляющие большинство значений, доступных в JavaScript.
Сложные объекты
Образец: sample29.html
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
<!DOCTYPE html><html lang=»en»><body><script>
var myObject = {};
// Contain properties inside of myObject representing most of the native JavaScript values.
myObject.myFunction = function () { };
myObject.myArray = [];
myObject.myString = ‘string’;
myObject.myNumber = 33;
myObject.myDate = new Date();
myObject.myRegExp = /a/;
myObject.myNull = null;
myObject.myUndefined = undefined;
myObject.myObject = {};
myObject.myMath_PI = Math.PI;
myObject.myError = new Error(‘Darn!’);
console.log(myObject.myFunction, myObject.myArray, myObject.myString, myObject.myNumber, myObject.myDate, myObject.myRegExp, myObject.myNull, myObject.myNull, myObject.myUndefined, myObject.myObject, myObject.myMath_PI, myObject.myError);
/* Works the same with any of the complex objects, for example a function.
var myFunction = function () { };
myFunction.myFunction = function () { };
myFunction.myArray = [];
myFunction.myString = ‘string’;
myFunction.myNumber = 33;
myFunction.myDate = new Date();
myFunction.myRegExp = /a/;
myFunction.myNull = null;
myFunction.myUndefined = undefined;
myFunction.myObject = {};
myFunction.myMath_PI = Math.PI;
myFunction.myError = new Error(‘Darn!’);
console.log(myFunction.myFunction, myFunction.myArray, myFunction.myString, myFunction.myNumber, myFunction.myDate, myFunction.myRegExp, myFunction.myNull, myFunction.myNull, myFunction.myUndefined, myFunction.myObject, myFunction.myMath_PI, myFunction.myError);
</script></body></html>
|
Простая концепция, которую следует здесь изучить, состоит в том, что сложные объекты могут содержать или ссылаться на все, что вы можете номинально выразить в JavaScript. Вы не должны удивляться, когда увидите, что это сделано, поскольку все нативные объекты могут быть видоизменены. Это относится даже к значениям String()
, Number()
и Boolean()
в их объектной форме, т.е. когда они создаются с помощью оператора new
.
Инкапсуляция сложных объектов программно выгодным способом
Объекты Object()
, Array()
и Function()
могут содержать другие сложные объекты. В следующем примере я продемонстрирую это, настроив дерево Object()
с помощью Object()
.
Образец: sample30.html
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
<!DOCTYPE html><html lang=»en»><body><script>
// Encapsulation using objects creates object chains.
var object1 = {
object1_1: {
object1_1_1: {foo: ‘bar’},
object1_1_2: {},
},
object1_2: {
object1_2_1: {},
object1_2_2: {},
}
};
console.log(object1.object1_1.object1_1_1.foo);
</script></body></html>
|
То же самое можно сделать с объектом Array()
(он же многомерный массив) или с объектом Function()
.
Образец: sample31.html
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
|
<!DOCTYPE html><html lang=»en»><body><script>
// Encapsulation using arrays creates a multidimensional array chain.
var myArray = [[[]]];
/* Here is an example of encapsulation using functions: An empty function inside an empty function inside an empty function.
var myFunction = function () {
// Empty function.
var myFunction = function () {
// Empty function.
var myFunction = function () {
// Empty function.
};
};
};
// We can get crazy and mix and match too.
var foo = [{ foo: [{ bar: { say: function () { return ‘hi’;
console.log(foo[0].foo[0].bar.say());
</script></body></html>
|
Основная концепция, которую следует здесь отбросить, заключается в том, что некоторые из сложных объектов предназначены для инкапсуляции других объектов программно выгодным способом.
Получение, установка и обновление свойств объекта с использованием точечной нотации или нотации в скобках
Мы можем получить, установить или обновить свойства объекта, используя точечную или скобочную нотацию.
В следующем примере я демонстрирую точечную нотацию, которая выполняется с использованием имени объекта, за которым следует точка, а затем следует свойство для получения, установки или обновления (например, objectName.property
).
Образец: sample32.html
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
<!DOCTYPE html><html lang=»en»><body><script>
// Create a cody Object() object.
var cody = new Object();
// Setting properties.
cody.living = true;
cody.age = 33;
cody.gender = ‘male’;
cody.getGender = function () { return cody.gender;
// Getting properties.
console.log(
cody.living,
cody.age,
cody.gender,
cody.getGender()
);
// Updating properties, exactly like setting.
cody.living = false;
cody.age = 99;
cody.gender = ‘female’;
cody.getGender = function () { return ‘Gender = ‘ + cody.gender;
console.log(cody);
</script></body></html>
|
Точечная нотация является наиболее распространенной нотацией для получения, установки или обновления свойств объекта.
Обозначение в скобках, если не требуется, используется не так часто. В следующем примере я заменяю точечную запись, использованную в предыдущем примере, на скобочную запись. За именем объекта следует открывающая скобка, имя свойства (в кавычках), а затем закрывающая скобка:
Образец: sample33.html
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
<!DOCTYPE html><html lang=»en»><body><script>
// Creating a cody Object() object.
var cody = new Object();
// Setting properties.
cody[‘living’] = true;
cody[‘age’] = 33;
cody[‘gender’] = ‘male’;
cody[‘getGender’] = function () { return cody.gender;
// Getting properties.
console.log(
cody[‘living’],
cody[‘age’],
cody[‘gender’],
cody[‘getGender’]() // Just slap the function invocation on the end!
);
// Updating properties, very similar to setting.
cody[‘living’] = false;
cody[‘age’] = 99;
cody[‘gender’] = ‘female’;
cody[‘getGender’] = function () { return ‘Gender = ‘ + cody.gender;
console.log(cody);
</script></body></html>
|
Обозначение в скобках может быть очень полезно, когда вам нужен доступ к ключу свойства, и вам нужно работать с переменной, которая содержит строковое значение, представляющее имя свойства. В следующем примере я продемонстрирую преимущество скобочной записи перед точечной, используя ее для доступа к свойству foobar
. Я делаю это с помощью двух переменных, которые при объединении создают foobarObject
версию ключа свойства, содержащегося в foobarObject
.
Образец: sample34.html
01
02
03
04
05
06
07
08
09
10
|
<!DOCTYPE html><html lang=»en»><body><script>
var foobarObject = { foobar: ‘Foobar is code for no code’ };
var string1 = ‘foo’;
var string2 = ‘bar’;
console.log(foobarObject[string1 + string2]);
</script></body></html>
|
Кроме того, нотация в скобках может пригодиться для получения имен свойств, которые являются недопустимыми идентификаторами JavaScript. В следующем коде я использую число и зарезервированное ключевое слово в качестве имени свойства (допустимо в виде строки), к которому могут обращаться только скобочные обозначения.
Образец: sample35.html
01
02
03
04
05
06
07
08
09
10
11
|
<!DOCTYPE html><html lang=»en»><body><script>
var myObject = { ‘123’: ‘zero’, ‘class’: ‘foo’ };
// Let’s see dot notation do this!
console.log(myObject[‘123’], myObject[‘class’]);
// It can’t do what bracket notation can do, in fact it causes an error.
// console.log(myObject.0, myObject.class);
</script></body></html>
|
Поскольку объекты могут содержать другие объекты, время от времени можно cody.object.object.object.object
или cody['object']['object']['object']['object']
. Это называется цепочкой объектов. Инкапсуляция объектов может продолжаться бесконечно.
Объекты являются изменяемыми в JavaScript, что означает, что их получение, установка или обновление могут выполняться для большинства объектов в любое время. Используя обозначение в скобках (например, cody['age']
), вы можете имитировать ассоциативные массивы, найденные на других языках.
Если свойство внутри объекта является методом, все, что вам нужно сделать, это использовать операторы ( ()
(например, cody.getGender()
) для вызова метода свойства.
Удаление свойств объекта
Оператор delete
может быть использован для полного удаления свойств из объекта. В следующем фрагменте кода мы удаляем свойство bar
из объекта foo
.
Образец: sample36.html
1
2
3
4
5
6
7
|
<!DOCTYPE html><html lang=»en»><body><script>
var foo = { bar: ‘bar’ };
delete foo.bar;
console.log(‘bar’ in foo);
</script></body></html>
|
delete
не удалит свойства, которые находятся в цепочке прототипов.
Удаление — это единственный способ фактически удалить свойство из объекта. Установка свойства в undefined
или null
только изменяет значение свойства. Не удаляет свойство из объекта.
Как решаются ссылки на свойства объекта
Если вы попытаетесь получить доступ к свойству, которое не содержится в объекте, JavaScript попытается найти свойство или метод, используя цепочку прототипов. В следующем примере я создаю массив и пытаюсь получить доступ к свойству foo
, которое еще не определено. Вы можете подумать, что, поскольку myArray.foo
не является свойством объекта myArray
, JavaScript немедленно вернет undefined
. Но JavaScript будет искать в двух местах ( Array.prototype
а затем Object.prototype
) значение foo
прежде чем он вернет undefined
.
Образец: sample37.html
1
2
3
4
5
6
7
8
9
|
<!DOCTYPE html><html lang=»en»><body><script>
var myArray = [];
console.log(myArray.foo);
/* JS will look at Array.prototype for Array.prototype.foo, but it is not there.
</script></body></html>
|
недвижимость. Если оно имеет свойство, оно возвращает значение свойства, и наследование не происходит, поскольку цепочка прототипов не используется. Если экземпляр не имеет свойства, JavaScript будет искать его в объекте- prototype
функции конструктора объекта.
У всех экземпляров объекта есть свойство, которое является секретной ссылкой (иначе __proto__
) на функцию конструктора, которая создала экземпляр. Эта секретная ссылка может быть использована для получения функции конструктора, в частности, свойства prototype функции конструктора instance.
Это один из самых запутанных аспектов объектов в JavaScript. Но давайте рассмотрим это. Помните, что функция также является объектом со свойствами. Имеет смысл разрешить объектам наследовать свойства от других объектов. Как сказать: «Привет, объект B, я бы хотел, чтобы вы поделились всеми свойствами объекта A». JavaScript по умолчанию подключает все это к нативным объектам через объект- prototype
. Когда вы создаете свои собственные функции конструктора, вы также можете использовать цепочку прототипов.
То, как именно JavaScript выполняет это, сбивает с толку, пока вы не увидите его таким, какой он есть: просто набор правил. Давайте создадим массив для более тщательного изучения свойства prototype
.
Образец: sample38.html
1
2
3
4
5
6
7
8
|
<!DOCTYPE html><html lang=»en»><body><script>
// myArray is an Array object.
var myArray = [‘foo’, ‘bar’];
console.log(myArray.join());
</script></body></html>
|
Наш экземпляр Array()
является объектом со свойствами и методами. Получив доступ к одному из методов массива, таким как join()
, давайте спросим себя: есть ли у экземпляра myArray, созданного из конструктора Array()
, собственный метод join()
? Давайте проверим.
Образец: sample39.html
1
2
3
4
5
6
7
|
<!DOCTYPE html><html lang=»en»><body><script>
var myArray = [‘foo’, ‘bar’];
console.log(myArray.hasOwnProperty(‘join’));
</script></body></html>
|
Нет. Тем не менее, myArray имеет доступ к методу join()
как если бы это было его собственное свойство. Что здесь случилось? Ну, вы только что наблюдали цепочку прототипов в действии. Мы получили доступ к свойству, которое хотя и не содержится в объекте myArray, но может быть найдено в другом месте JavaScript. Это где-то еще очень специфично. Когда конструктор Array()
был создан JavaScript, метод join()
был добавлен (среди прочих) в качестве свойства свойства prototype
объекта Array()
.
Повторим, если вы попытаетесь получить доступ к свойству объекта, который его не содержит, JavaScript будет искать в цепочке prototype
это значение. Сначала он рассмотрит функцию конструктора, которая создала объект (например, Array
), и проверит его прототип (например, Array.prototype
), чтобы увидеть, можно ли там найти свойство. Если первый объект-прототип не имеет этого свойства, JavaScript продолжает поиск по цепочке в конструкторе за начальным конструктором. Он может делать это до самого конца цепочки.
Где заканчивается цепь? Давайте снова рассмотрим пример, вызвав метод toLocaleString()
в myArray
.
Образец: sample40.html
1
2
3
4
5
6
7
8
9
|
<!DOCTYPE html><html lang=»en»><body><script>
// myArray and Array.prototype contain no toLocaleString() method.
var myArray = [‘foo’, ‘bar’];
// toLocaleString() is actually defined at Object.prototype.toLocaleString
console.log(myArray.toLocaleString());
</script></body></html>
|
Метод toLocaleString()
не определен в объекте myArray
. Итак, правило цепочки прототипов вызывается, и JavaScript ищет свойство в свойстве прототипа конструкторов Array
(например, Array.prototype
). Его там тоже нет, поэтому правило цепочки вызывается снова, и мы ищем свойство в свойстве прототипа Object()
( Object.prototype
). И да, это там найдено. Если бы он не был там найден, JavaScript выдал бы ошибку, указав, что свойство не undefined
.
Поскольку все свойства прототипа являются объектами, последнее звено в цепочке — Object.prototype
. Нет другого свойства прототипа конструктора, которое можно было бы исследовать.
Впереди целая глава, которая разбивает цепочку прототипов на более мелкие части, поэтому, если это полностью потеряно для вас, прочитайте эту главу и затем вернитесь к этому объяснению, чтобы укрепить ваше понимание. Из этого краткого прочтения по данному вопросу, я надеюсь, вы понимаете, что когда свойство не найдено (и считается undefined
), JavaScript просмотрит несколько объектов-прототипов, чтобы определить, что свойство не undefined
. Поиск всегда происходит, и этот процесс поиска заключается в том, как JavaScript обрабатывает наследование, а также простой поиск свойств.
Использование hasOwnProperty
для hasOwnProperty
что свойство объекта не из цепочки прототипов
В то время как оператор in
может проверять свойства объекта, включая свойства из цепочки прототипов, метод hasOwnProperty
может проверять объект на наличие свойства, которое не принадлежит цепочке прототипов.
В следующем примере мы хотим узнать, содержит ли myObject
свойство foo
и не наследует ли оно свойство из цепочки прототипов. Для этого мы спрашиваем, имеет ли myObject
свое собственное свойство с именем foo
.
Образец: sample41.html
01
02
03
04
05
06
07
08
09
10
|
<!DOCTYPE html><html lang=»en»><body><script>
var myObject = {foo: ‘value’};
console.log(myObject.hasOwnProperty(‘foo’)) // Logs true.
// Versus a property from the prototype chain.
console.log(myObject.hasOwnProperty(‘toString’));
</script></body></html>
|
Метод hasOwnProperty
следует использовать, когда необходимо определить, является ли свойство локальным для объекта или унаследованным от цепочки прототипов.
Проверка, содержит ли объект заданное свойство, используя оператор in
Оператор in
используется для проверки (true или false), содержит ли объект заданное свойство. В этом примере мы проверяем, является ли foo
свойством в myObject
.
Образец: sample42.html
1
2
3
4
5
6
|
<!DOCTYPE html><html lang=»en»><body><script>
var myObject = { foo: ‘value’ };
console.log(‘foo’ in myObject);
</script></body></html>
|
Вы должны знать, что оператор in
проверяет не только свойства, содержащиеся в объекте, на который ссылаются, но также и любые свойства, которые объект наследует через цепочку prototype
. Таким образом, применяются те же правила поиска свойств, и свойство, если не в текущем объекте, будет искать в цепочке prototype
.
Это означает, что myObject в предыдущем примере фактически содержит метод свойства toString
через цепочку prototype
( Object.prototype.toString
), даже если мы не указали его (например, myObject.toString = 'foo'
).
Образец: sample43.html
1
2
3
4
5
6
|
<!DOCTYPE html><html lang=»en»><body><script>
var myObject = { foo: ‘value’ };
console.log(‘toString’ in myObject);
</script></body></html>
|
В последнем примере кода свойство toString не находится буквально внутри объекта myObject. Однако он унаследован от Object.prototype
, и поэтому оператор in
заключает, что myObject
действительно имеет унаследованный метод свойства toString()
.
Перечислять (зацикливать) свойства объекта, используя цикл for
in
Используя for in
, мы можем зацикливаться на каждом свойстве объекта. В следующем примере мы используем цикл for для извлечения имен свойств из объекта cody.
Образец: sample44.html
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
<!DOCTYPE html><html lang=»en»><body><script>
var cody = {
age: 23,
gender: ‘male’
};
for (var key in cody) { // key is a variable used to represent each property name.
// Avoid properties inherited from the prototype chain.
if (cody.hasOwnProperty(key)) {
console.log(key);
}
}
</script></body></html>
|
Цикл for in
имеет недостаток. Он не только получит доступ к свойствам конкретного зацикленного объекта. Он также будет включать в цикл любые свойства, унаследованные (через цепочку прототипов) объектом. Таким образом, если это не желаемый результат, и в большинстве случаев это не так, мы должны использовать простой оператор if
внутри цикла, чтобы убедиться, что мы получаем доступ только к свойствам, содержащимся в конкретном объекте, над которым мы зацикливаемся. Это можно сделать с помощью hasOwnProperty()
унаследованного всеми объектами.
Порядок доступа к свойствам в цикле не всегда соответствует порядку, в котором они определены в цикле. Кроме того, порядок, в котором вы определили свойства, не обязательно является порядком их доступа.
Только свойства, которые являются перечисляемыми (то есть доступными при цикле по свойствам объектов), отображаются с циклом for in
. Например, свойство конструктора не будет отображаться. Можно проверить, какие свойства перечислимы с помощью метода propertyIsEnumerable()
.
Хост-объекты и нативные объекты
Вы должны знать, что среда (например, веб-браузер), в которой выполняется JavaScript, обычно содержит так называемые хост-объекты. Хост-объекты не являются частью реализации ECMAScript, но доступны как объекты во время выполнения. Конечно, доступность и поведение хост-объекта полностью зависит от того, что предоставляет хост-среда.
Например, в среде веб-браузера объект window / head и все содержащиеся в нем объекты (за исключением того, что предоставляет JavaScript) считаются хост-объектами.
В следующем примере я проверяю свойства объекта window
.
Образец: sample45.html
1
2
3
4
5
6
7
|
<!DOCTYPE html><html lang=»en»><body><script>
for (x in window) {
console.log(x);
}
</script></body></html>
|
Возможно, вы заметили, что нативные объекты JavaScript не указаны среди основных объектов. Довольно часто браузер различает хост-объекты и нативные объекты.
Что касается веб-браузеров, то самым известным из всех размещенных объектов является интерфейс для работы с документами HTML, также известный как DOM . В следующем примере приведен метод для перечисления всех объектов, содержащихся в объекте window.document
предоставленном средой браузера.
Образец: sample46.html
1
2
3
4
5
6
7
|
<!DOCTYPE html><html lang=»en»><body><script>
for (x in window.document) {
console.log();
}
</script></body></html>
|
Здесь я хочу, чтобы вы узнали, что спецификация JavaScript не связана с хост-объектами и наоборот. Существует грань между тем, что обеспечивает JavaScript (например, JavaScript 1.5, ECMA-262, Edition 3 и Mozilla JavaScript 1.6 , 1.7 , 1.8 , 1.8.1 , 1.8.5 ) и тем, что обеспечивает среда хоста, и эти два не должны быть в замешательстве.
Хост-среда (например, веб-браузер), которая выполняет код JavaScript, обычно предоставляет объект заголовка (например, объект окна в веб-браузере), где собственные части языка хранятся вместе с хост-объектами (например, window.location
в веб-браузер) и определенные пользователем объекты (например, код, который вы пишете для запуска в веб-браузере).
Иногда производитель веб-браузера, как хост интерпретатора JavaScript, продвигает версию JavaScript или добавляет будущие спецификации в JavaScript, прежде чем они будут утверждены (например, Mozilla Firefox JavaScript 1.6, 1.7, 1.8, 1.8.1, 1.8. 5).
Улучшение и расширение объектов с помощью Underscore.js
JavaScript 1.5 не хватает, когда приходит время серьезно манипулировать объектами и управлять ими. Если вы используете JavaScript в веб-браузере, я бы хотел выделить жирным шрифтом здесь и предложить использовать Underscore.js, когда вам нужно больше функциональности, чем предусмотрено в JavaScript 1.5. Underscore.js предоставляет следующие функциональные возможности при работе с объектами.
Эти функции работают со всеми объектами и массивами:
-
each()
-
map()
-
reduce()
-
reduceRight()
-
detect()
-
select()
-
reject()
-
all()
-
any()
-
include()
-
invoke()
-
pluck()
-
max()
-
min()
-
sortBy()
-
sortIndex()
-
toArray()
-
size()
Эти функции работают на всех объектах:
-
keys()
-
values()
-
functions()
-
extend()
-
clone()
-
tap()
-
isEqual()
-
isEmpty()
-
isElement()
-
isArray()
-
isArguments
-
isFunction()
-
isString()
-
isNumber
-
isBoolean
-
isDate
-
isRegExp
-
isNaN
-
isNull
-
isUndefined
Вывод
Мне нравится эта библиотека, потому что она использует преимущества новых собственных дополнений к JavaScript, где браузеры поддерживают их, но также предоставляет те же функциональные возможности браузерам, которые этого не делают, и все это без изменения собственной реализации JavaScript, если только это не требуется.
Перед тем, как вы начнете использовать Underscore.js, убедитесь, что нужная вам функциональность еще не предоставлена библиотекой JavaScript или фреймворком, который может уже использоваться в вашем коде.