All pastes #1880332 Raw Edit

Reaper

public javascript v1 · immutable
#1880332 ·published 2010-06-10 10:48 UTC
rendered paste body
// * Переменные *// $ это допустимый символ в имени переменной // [] это массив:var $data = [1, 2, 7, '1sdf'];// {} это хэшvar $hash = {  a : 1,  b : 'ZOO',  c : function() { }};// Переменные объявленные с использованием var имеют локальную // область действия, без - глобальную (в браузере принадлежат// объекту window)// * Функции *// Функция:function foo($str) {  alert($str);}// Функция как переменная:var $bar = function() {  foo('Hello');}; // объект справа от знака равенства является анонимной функцией // А также, функции в JavaScript являются объектами первого класса:var $buzz = function($lambda) {  return $lambda();};$buzz($bar);// В действительности запись:function foo() { }// является сокращённой записью:foo = function() { };// что является сокращением:foo = new Function("arg1", "arg2", "body");// Внутри функции можно получить ссылку на саму себя такvar factorial = function(x) {  return x == 1 ? 1 : x + factorial(x - 1); // используя имя функции}; // можно и так:var factorial = function(x) {  return x == 1 ? 1 : x + arguments.callee(x - 1); // arguments.callee}; // а можно и так:var factorial = function f(x) { // f позволяет сослаться на себя изнутри  return x == 1 ? 1 : x + f(x - 1);};// JavaScript поддерживает замыкания:function outer() {  var x = 7; // лексическая переменная захватывается замыканием  var $closure = function() {    alert(x);  }; // на замыканиях основана инкапсуляция данных }// Каждое выполнение функции хранит все переменные в специальном объекте// [[scope]] недоступном для программиста. При создании вложенной функции// её передаётся ссылка на внешний [[scope]]. Замыкание, это когда внешний // [[scope]] остаётся жить после завершения внешней функции. JavaScript // всегда хранит внешние [[scope]] до завершения работы внутренних функций, // чтобы внутренние функции могли найти переменные, если они не найдены // в собственных [[scope]]. Все функции в JavaScript являются замыканиями!// Переменные попадают в замыкание по ссылке, а не по значению,// поэтому в отличии от перлаfunction create() {  var $arr = [];  for(var $i=0; $i<100; $i++) {    $arr[i] = function() { alert(i * i); }; // => 10000  }  return $arr;}// этот код вернёт массив функций показывающих одно и тоже значение (100*100)// поэтому в подобных случаях делают двойное замыкание:function create() {  var $arr = [];  for (var $i=1; $i<n; $i++) {    arr[i] = function(x) { // в x копируется значение i      return function() { alert(x*x); };    } (i); // вызов свежесозданной функции   }  return arr;}// Функции можно добавить свойство:function Zeppelin() { }Zeppelin.$counter = 0;// используя свойства получаем аналог статических переменных внутри функций:function Zeppelin() {  var $self = arguments.callee;  $self.$counter++;  alert($self.$counter);}// * ООП *// Функция создаёт объект, если вызывается с new// В этом случае она является конструктором объектаfunction Lizard() {   this.$color = 'green';}var $speedy = new Lizard(); // Lizard - конструктор для $speedyalert($speedy.$color); // => green// this отличается от this в C#, C++, e.t.c// В JavaScript this привязывается к контексту вызова// есть 4 случая:// 1, если функция вызывается через new как конструктор объекта:// this  указывает на создаваемый объектfunction Lizard() {   this.$color = 'green';}// 2. Если функция запущена как метод объекта, this будет ссылкой на этот объектvar $lazy = {   $speed : 10 }; // это хэшvar $jazz = function() {  alert(this.$speed);}; // это функция$lazy.showSpeed = $jazz; // а это объекту создали меод showSpeed$lazy.showSpeed(); // => 30// 3. Если функция вызывается через call или apply, this указывает на объект// переданный в call / applyfunction sum(a, b) {  this.c = a + b;}var obj = {};sum.call(obj, 1, 2); // obj.c = 3sum.apply(obj, [1, 2]); // obj.c = 3// 4. в просто вызове функции, this становится равным глобальному объекту// в браузере это windows// Инкапсуляция// Инициализацию данных предпочтительно размещать в конструктореvar MyClass = function() {  this.public = function() { }; // доступное снаружи свойство  var private = function() { }; // закрытое свойство};var $obj = new MyClass();$obj.public(); // корректно$obj.private(); // ошибка// Наследование // ООП построено на наследовании объектов, а не классов. Для понимания наследования// надо знать, что такое прототип. Прототип - это объект ассоциированный с // функцией-конструктором объекта. Сама функция не использует ассоциированный прототип.// Но созданные функцией объекты содержат заданные в прототипе свойства:function Rock() { }Rock.prototype = [1, 5, 13]; // прототипом сейчас является массивvar $guitar = new Rock();alert($guitar[2]); // => 13var Blizzard = function() { };Blizzard.prototype = {  Z  : 7,  PI : 3.14159 };var diablo = new Blizzard(); // Blizzard это функция-конструкторalert(Blizzard.PI); // => "undefined"alert(diablo.Z); // => 7  // Прототип это скрытая ссылка [[prototype]] недоступная программисту, принадлежащая // созданному объекту. Значением этой ссылки является доступное для программиста // свойство prototype у функции-конструктора в момент вызова new. // Важным свойством прототипа является то, что если вызывается какое-то свойство // объекта и оно отсутствует у самого объекта, начинается рекурсивный поиск в // в объекте на который ссылается прототип. Соответственно, присвоив прототипу// базовый объект, получаем наследование:var Car = function() {  this.speed = 120;};var Supercar = function() { };Supercar.prototype = new Car; // Здесь мы сделали Supercar наследником Carvar $ford = new Supercar();alert($ford.speed); // => 120// Указанный способ реализации наследования имеет два недостатка:// 1 - необходимость создания базового объекта, 2 - вытекающий из первого,// необходимость учёта функцией-конструктором вариант своего использования  // только для назначения прототипа.// Гибкость концепции прототипов позволяет неколькими способами реализовать // наследование, ниже элегантный, но далеко не тривиальный для понимания способ // реализовать наследование, лишённый указанных выше недостатков:function extend(Child, Parent) {  var F = function() { };  F.prototype = Parent.prototype;  Child.prototype = new F();  Child.prototype.constructor = Child;  Child.super = Parent.prototype;}function Money(summ) {   this.$ammount = summ;}function Dollar() {   // вызов конструктора родителя с передачей ему аргументов  Dollar.super.constructor.call(this, arguments[0]);  // аргументы можно передавать как угодно: массивом arguments  // либо набором переменных, разделённых запятой - всё это   // придёт в базовый конструктор, в данном случае в Money( ... )}extend(Dollar, Money);var $money  = new Money(1000);var $dollar = new Dollar(3000);alert($money.$ammount); // => 1000alert($dollar.$ammount); // => 3000// Альтернативный способ создания объектов и наследования, основанный // на добавление нужных методов "наследника" в "базовый класс":function Base() {  this.$E = 2.718281828;  return {    compute  : function($arg) {      return $arg * $E;    }  };}// Потомокfunction Derived() {  var self = Base(); // <- здесь можно было бы передать аргументы                      // в конструктор "базового класса"  self.constructor = arguments.callee;  self.compute = function($arg) { // это пример перекрытия метода в "наследнике"    return $arg * $E * $E;  };  return self;}var $d = new Derived;$d.compute(7); // => 51.7233// Различие способов состоит в том, что во втором способе теряется// функциональность оператора instanceоf, который проверяет // принадлежность объекта проходя по цепочке его прототипов.// Это далеко не единственные способы организации наследования// в JavaScript, существует масса различных комбинаций указанных подходов// Пара слов о манипуляции с obj.constructor// Function.prototype.constructor или object.constructor// это ссылка на конструктор объекта, т.е. на функцию вызванную// с оператором new. object.constructor устанавливается исxодя// из Function.prototype.constructor на момент вызова new.// // Для каждой функции свойство prototype.constructor должно указывать // на саму функцию, тогда создаваемые объекты будут иметь правильное свойство// constructor. Если свойство prototype для функции переназначается// на новый объект, надо обязательно установить и свойство constructor:function NewYork() { }NewYork.prototype = { population : 10000000, crime : 'high' };NewYork.prototype.constructor = NewYork;// при установке прототипа функции через new этого делать не надоvar City = function() { };City.prototype = new NewYork; // автоматически конструктор                               // установится ссылкой на NewYork// Статические членыfunction Figure() { }Figure.prototype = {  active : true,  coords : [0, 0]};Figure.prototype.constructor = Figure;var fig1 = new Figure();var fig2 = new Figure();fig1.active = false;fig1.coords[0] = 10;alert(fig2.active); // => truealert(fig2.coords[0]); // => 10 (!)// причина изменения coords[0] у второго объекта (fig2), в том, что при // изменении элемента массива coords первым объектом (fig1), сначала // был произведён поиск массива для доступа к его элементу внутри объекта // fig1, массив не был найден и далее был произведён поиск в прототипе, // где он и был найден. Элемeнты находящиеся в прототипе функции разделяются // всеми созданными функцией объектами. Причина же неизменности переменной // active у fig2 заключается в том, что при присвоении свойству в объекте, // если свойство отсутствует, оно просто создаётся.// fig1.active = false; создало свойство в fig1,// alert(fig2.active); вывело свойство из прототипа.// Итак, все нестатические члены классов желательно создавать в конструкторе// а все статические члены, обращение к которым является не присваиванием, т.е.// всё, что не является примитивными типами, размещаетса в прототипе:function Square() {  this.color  = 'green';  this.active = true;  this.coords = [0, 0];}Square.prototype = {  getSomeSharedFlag : function() { return true; },  someSharedHash : { foo : 1, bar : 2 }};Square.prototype.constructor = Square;// Полиморфизм// Полиморфизм в JavaScript обеспечивается перегрузкой методов в механизме наследования:function Ship() {  this.getVolume = function() { return 1000; };}function LargeShip() {  this.getVolume = function() { return 100000; };}LargeShip.prototype = new Ship;function count(ship) {  alert(ship.getVolume());}var ship1 = new Ship;var ship2 = new LargeShip;count( ship1 ); // => 1000count( ship2 ); // => 100000