Подтвердить что ты не робот

Почему JavaScript позволяет хранить массив и функцию в одной переменной?

Я натолкнулся на это по ошибке однажды, программируя игру:

var foo = function() { alert("Hello, World!"); }

foo[0] = "Zero";
foo[1] = "One";
foo[2] = "Two";
foo[3] = "Three";

foo(); // Alerts "Hello, World!"
alert(foo[2]); // Alerts "Two"

Почему JavaScript позволяет вам это делать? Это глюк?

4b9b3361

Ответ 1

Здесь нет массивов. Например, Array.isArray(foo) имеет значение false.

Вместо этого вы предоставляете foo четыре свойства с именами "0", "1", "2" и "3" [1] с разными значениями. Это вполне справедливо, поскольку функции являются объектами; вы всегда можете прикреплять свойства с любым именем, которое вам нравится, к объектам.

Вы также можете сделать:

foo.otherProp = "hi!";

alert(foo.otherProp);

[1]: Обратите внимание, что имена свойств всегда преобразуются в строки перед их настройкой; выполнение foo[{ toString: function () { return "baz"; }] = 5 будет таким же, как foo["baz"] = 5 или foo.baz = 5. Аналогично, выполнение foo[0] = "whatever" аналогично выполнению foo["0"] = "whatever".

Ответ 2

Почему JavaScript позволяет хранить массив и функцию в одной переменной?

Это не так. Он позволяет хранить ссылку на функцию в переменной. Функции являются первоклассными объектами в JavaScript, поэтому они могут иметь свойства, в том числе имена с именами типа "0", "1" и т.д.

Здесь вы видите пару вещей:

  • Что я сказал выше, функции - это объекты, поэтому вы можете добавлять к ним свойства.

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

  • В JavaScript вы можете ссылаться на свойство с использованием точечной нотации и литерала (foo.bar), либо с использованием скобок и строки (foo["bar"]).

  • Когда вы используете нотацию в квадратных скобках, вещь в скобках приводится к строке, если она еще не одна, поэтому [0] на самом деле ["0"].

И да, это последнее верно, даже когда вы имеете дело с массивами, потому что стандартные массивы в JavaScript на самом деле не массивы. (Есть последние дополнения, Int32Array и такие, которые являются истинными массивами, но Array нет.)

Ответ 3

Ответ довольно прост и может быть выражен как силлогизм (для моего удовольствия).

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


1 Технически не все объекты. Объекты "Закаленные" не могут быть мутированы ненадежным кодом, а объекты, предоставленные приложением, внедряющие среду выполнения JavaScript, могут настроить их поведение, чтобы предотвратить это. Однако это краевые случаи. Незакрепленные объекты, созданные из среды выполнения JavaScript, должны демонстрировать эту функциональность, и большую часть времени это объекты, которые пытается мутировать.

Ответ 4

foo[0] в этом случае присваивает значение свойству с именем "0". Функция является объектом и может иметь свойства, добавленные к ней.

Таким же образом эти два являются равнозначными:

foo["abc"]
foo.abc

Ответ 5

Почти все (включая функции) в Javascript является объектом, поэтому вы можете дать ему дополнительные свойства, такие как 0 и 1.

Ответ 6

Прежде всего, ваш вопрос не имеет ничего общего с вашим названием.

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

В теле вашего вопроса вы задаетесь вопросом, почему вам разрешено устанавливать атрибуты функции, как если бы это был объект или массив.

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

Но да, JavaScript покрыт острыми краями и точками, поэтому вы должны быть осторожны.

Ответ 7

В JavaScript функции являются объектами первого класса.

function foo() {}
var isObject = foo instanceof Object;
// isObject is true

Это сделка с назначением функций переменной, передачей их в качестве аргументов, возвращением их из функций и т.д.

Ссылка на MDN

Ответ 8

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

Это, конечно, функция OOP/prototyping в целом, но она имеет дополнительное преимущество, позволяя вам вызывать функцию напрямую, а не как свойство более общего объекта.