Переменные в Javascript не имеют типа! Тип имеет только значение переменной, поэтому переменная, которая не была инициализирована конкретным значением, не может иметь тип. Более того, одна и та же переменная может принимать значения различных типов в разные моменты времени, однако всегда можно проверить, была ли переменная вообще инициализирована.
Javascript позволяет определять глобальные и локальные переменные. Отсутствует блочная видимость в том виде, как это реализовано в языках C и C++.
Пример:
function test(o) {
var i = 0; // i определена во всей функции if (typeof o == "object") {
var j = 0; // j определена везде, а не только в блоке
for(var k = 0; k < 10; k++) { // k определена везде, не только в цикле
document.write(k);
}
document.write(k); // k все еще определена: печатается 10
}
document.write(j); // j определена, но может быть не инициализирована
}
Основной принцип распространения видимости переменных следующий: переменные доступны в текущей функции и во всех вложенных функциях (то есть определённых в коде этой функции). Глобальные переменные, т.е. переменные, определенные вне функций, доступны везде.
var x = function(){
var i;
mul = function(b) { return i*b; } for(fact = i = 1; i < 10; i++) {
fact = mul(fact);
document.write(i + "! = " + fact + ”<br />");
}
}
Локальными переменными являются аргументы функций, а также переменные, не найденные в контексте выше, либо объявленные со служебным словом var.
Пример:
var scope = "глобальная"; function f() {
alert(scope); // Показывает "глобальная". scope = "локальная"; // Переменная глобальная. alert(scope); // Показывает "локальная"
}
f();
alert(scope); // Показывает "локальная"
Укажем внутри функции декларацию var scope.
var scope = "глобальная"; function f() {
alert(scope); // Показывает "undefined", а не "глобальная". var scope = "локальная"; // Переменная инициализируется здесь,
// но определена она везде в функции. alert(scope); // Показывает "локальная"
}
f();
alert(scope); // Показывает "глобальная".
Переменные могут не декларироваться, однако для того, чтобы объявить глобальную переменную необходимо либо объявить её со служебным словом var, либо присвоить значение.
Пример, который вызовет ошибку:
function f1() {
scope = "123"; // Переменная инициализируется здесь alert (scope);
}
function f2() {
alert(scope); // Переменная нигде не объявлена
}
Добавим переменную scope явно
var scope; function f1() {
scope = "123"; // Переменная инициализируется здесь alert(scope);
}
function f2() {
alert(scope);
}
f 2();// Показывает "undefined"
f1();// Показывает "123"
f 2();// Показывает "123"
Зададим переменной scope тип строки:
var scope = ""; function f1() {
scope = "123"; // Переменная инициализируется здесь alert(scope);
}
function f2() {
alert(scope);
}
f 2();// Показывает "" (пустая строка)
f1();// Показывает "123"
f 2();// Показывает "123"
Объекты и классы
Язык Javascript является объектно-ориентированным языком, но не имеет классов в понимании классов С++ или Java.
Любая переменная может получить значение-объект. Если ни одна переменная не хранит ссылку на этот объект, он будет удалён. Каждый из объектов конструируется самостоятельно. При этом объекту могут быть динамически назначены свойства и методы.
Рассмотрим пример:
// Определяем конструктор.
// Обратите внимание, как инициализируется объект с помощью "this". function Rectangle(w, h) { this.width = w; this.height = h;
}
// Вызываем конструкторы для создания двух объектов Rectangle.
// И проинициализируем оба новых объекта.
var rect1 = new Rectangle(2, 4); // rect1 = { width:2, height:4 };
var rect2 = new Rectangle(8.5, 11); // rect2 = { width:8.5, height:11 };
В роли конструктора объекта выступает функция Rectangle. При этом для добавления свойств объекта использован указатель this.
Добавить метод к конкретному объекту можно следующим образом:
r.area = function() { return this.width * this.height; }
// Теперь рассчитать площадь, вызвав метод объекта
var a = r.area();
Добавить метод так, чтобы объект получал его на этапе вызова конструктора можно следующим образом:
function Rectangle(w, h) { this.width = w; this.height = h;
this.area = function() { return this.width * this.height; }
}
Указанный метод не является оптимальным. В данном примере созданный объект имеет три свойства (area - тоже свойство, имеющее после создания объекта функциональный тип), причём любое из них может принять любое значение. Это не соответствует концепции класса с постоянными методами.
Для решения данной проблемы разработан механизм прототипов. Любой объект имеет указатель на прототип. В момент создания объекта устанавливает ссылка на конструктор прототипа, которым является функция-конструктор, создавшая объект. Ссылка на прототип объекта указывает на свойство prototype функции-конструктора. Методы и свойства, добавленные прототипу будут доступны любому объекту, созданному такой функцией-конструктором.
Пример создания класса Circle:
// Конструктор
function Circle(radius) {
// r - свойство экземпляра объекта, оно определяется // и инициализируется конструктором. this.r = radius;
}
// Circle.PI - свойство класса (общее для всех объектов), свойство конструктора. Circle.PI = 3.14159;
// Метод для экземпляра, который рассчитывает площадь круга.
// Аналог методов класса C++
Circle.prototype.area = function() { return Circle.PI * this.r * this.r; }
// Метод класса (аналог статических методов в C++)
// принимает два объекта Circle и возвращает объект с большим радиусом. Circle.max = function(a,b) { if (a.r > b.r) return a; else return b;
}
// Примеры использования каждого из этих полей:
var c = new Circle(1.0); // Создание экземпляра класса Circle
c.r = 2.2; // Установка свойства экземпляра r
var a = c.area(); // Вызов метода экземпляра area()
var x = Math.exp(Circle.PI); // Обращение к свойству PI класса
var d = new Circle(1.2); // Создание другого экземпляра класса Circle
var bigger = Circle.max(c,d); // Вызов метода класса max()