javascript oop
DESCRIPTION
Лекция Константина Кичинского по ООП в JavaScript в рамках курса по ООП. Каф. Вычислительная математика и программирование, МАИTRANSCRIPT
JavaScript
Общие сведения o JavaScript
JavaScript: The World's Most Misunderstood Programming Language
— Douglas Crockford,senior JavaScript Architect at
Yahoo!
JavaScript != Java
JavaScript ~ ECMAScriptJavaScript 1.5 ~ ECMAScript 3
JavaScript 2.0 ~ ECMAScript 5
Реализации JavaScript
— V8, Google Chrome— TraceMonkey, SpiderMonkey, Gecko,
Mozilla Firefox— Jscript, Trident, IE— Futhark, Presto, Opera— KJS, KHTML, Konqueror— JavaScriptCore, SquirreFish, WebKit,
Safari, Adobe AIR
Инструменты для работы
— Firebug, Firefox— DevTools (F12), IE8— Dragonfly, Opera
— Visual Studio, Expression Web— Aptana— Dreamweaver— …
Библиотеки на JavaScript
— Prototype— jQuery— Mootools— Dojo— Ext— Qooxdoo— YUI— ASP.NET Ajax— …
Nearly all of the books about JavaScript are quite awful. They contain errors, poor examples, and promote bad practices.
— Douglas Crockford,senior JavaScript Architect at
Yahoo!http://javascript.crockford.com/
OOP в JavaScript
Типы данных: примитивы + объекты. Примитивы в общем-то тоже объекты.
Примитивы
— Number — 1, 3, 1011, 11.12, 2e+3— String — “a”, “bla-bla-bla”, “0”— Boolean — true | false— null— undefined
Ссылочные типы
— Date — new Date(1211623944453);— Error — new Error(“Oops!”);— RegExp — /^web.*$/i;— Array — [ “apple”, “banana” ]— Function — function(x) { return
x*x; }
Boolean
Number
String
null
undefined
Date
Error
RegExp
Array
Function
Object
Примитивы Ссылочные типы
Объекты
— ассоциативные массивы, хеш-таблицы
— Ключ: Значение
Простой объект
var ufo= {};ufo.name = ‘Mars UFO’;ufo.stealsCows = true;
Простой объект
var ufo= {stealsCows: true,isCowsStealer: function() {
return this. stealsCows;}
};
ufo.isCowsStealer(); //true
Объекты как хеш
o.name = “Moon UFO”;
o[“name”] = “Moon UFO”;
for ( var key in o ) {alert( key + “: “ + o[key] );
}
Object literal notation
{ a: 1, b: “js” }
Массивы тоже объекты [, ]
typeof [ 1, 2, 3 ];// “object”
Array literal notation
[ 1, “js” ]
JavaScript Object Notation (JSON )
{ num: 1, str: “abc”, arr: [ 1, 2, 3 ] }
Функции
Функции являются объектами!—Имеют свойства—Имеют методы—Могут копироваться, удаляться, …—Особенность: их можно
вызвать/выполнить
Функции
function steal(what) {return what;
}
Функции
var steal = function (what) {return what;
};
* Здесь используется анонимная функция, об этом позже
Функции
var steal = function steal(what) {return what;
};
Функции являются объектами
steal.length// 1steal.name//”steal”
Функции являются объектами
var snaffle = steal;snaffle(“Cow”);//”Cow”snaffle.call(null, “Calf”);//”Calf”snaffle.apply(null, [ “Calf” ]);//”Calf”
Возвращаемое значение
• Все функции возвращают значение
• Если функция ничего не возвращает явным образом, возвращается undefined
• Функции могут возвращать объекты, включая другие функции
Вызов метода с нужной областью видимости
При вызове moo this ссылается на указанную область видимости scopefunction moo() {
this.BigSaucerEyes();};moo.call( scope, arg1, arg2, …);moo.apply( scope, [arg1, arg2, …] );
Тут начинается магия
Область видимости «по умолчанию» — Window
var UFO = function UFO() {this.name = “FO”;return this; // посмотрим, что это за this
};UFO // UFO()UFO() // Window… Oops!!! Window.name is “FO”new UFO() // Object name=“FO”
Тут продолжается магия
var ufo = new UFO() эквивалентно
var ufo = {};UFO.call(ufo, null)
ufo // Object name=“FO”
Если this возвращается вручную внутри конструктора (return this), тогда можно писатьvar ufo = UFO.call({}, null);
Замыкания (Closures)
• Соседние функции• Вложенные функции имеют
доступ к локальным переменным даже после выполнения внешней функции
Замыкания (Closures)
function outer(){
var count = 1;function inner() { count++; };return inner;
}var myClosure = outer();myClosure(); // count == 2myClosure(); // count == 3
Функции-конструкторы
• При вызове с оператором new функции возвращают объект, обозначаемый как this
• Этот объект можно изменять перед передачей из фунции
Функции-конструкторы
function UFO(name){this.name = name;this.getName = function() {
return this.name;};
};
Вызов функции-конструктора
var ufo = new UFO(“Mars UFO”);ufo.getName();//”Mars UFO”
instanceof
var ufo= new UFO(“Moon UFO”);ufo instanceof UFO;// true
Конструкторы — всего лишь функции
Свойство constructor
function UFO() {};var ufo = new UFO();ufo.constructor;// function();ufo.constructor === UFO;// true
UFO | function
ufo | object constructor
Свойство constructor
var o = {};o.constructor === Object// true[1, 2].constructor === Array;// true
Встроенные конструкторы
• Object• Array• Function• RegExp• Number• String• Boolean• Date• Error, SyntaxError, ReferenceError, …
Эффективность использования
Хорошо Плохо
var o = {}; var o = new Object();
var a = []; var a = new Array();
var re = /[a-z]/gmi; var re = new RegExp(‘[a-z]’, ‘gmi’);
var fn = function(a, b) {return a+b;
}
var fn = new Function(‘a, b’, ‘return a+b’);
Статические свойства и методы
Функции — объекты. Свойства и методы конструктора — работают как статические.
UFO.COUNT = “100500”;UFO.getCount = function() {
return UFO.COUNT;};
Private-члены
function UFO(name) {var _name = name; // private variablethis.getName = function() {
return _name;};
};
var ufo= new UFO(“Mars UFO”);alert( ufo.getName() );
Private-члены
function UFO(name) {var _name = name;// private methodvar _fixName = function() {return _name.toUpperCase();};this.getName = function() {return _fixName();};
};
Namespace
if (typeof FlyingObjects== “undefined”) { FlyingObjects = {};
}if (typeof FlyingObjects.Undefined ==
“undefined”) { FlyingObjects.Undefined = {};
}FlyingObjects.Undefined.UFO = function() {};var smallUFO = new
FlyingObjects.Undefined.UFO;
В JavaScript нет классов.
Наследование копированием
(примеси)
Два объекта
var roundShinyObject= { shiny: true, round: true
};
var ufo = {name: “UFO”,getName: function() {
return this.name;}
};
extend()
function extend(parent, child) {for (var key in parent) {
child[key] = parent[key];}
}
extend(roundShinyObject, ufo);ufo.round; // true
Прототипное наследование
Prototype
Специальное свойство функциональных объектов. prototype – это объект!var UFO = function () {};typeof UFO.prototype;//”object”
Изменение prototype
UFO.prototype.name = “UFO”;UFO.prototype.stealCow = function()
{};
Перезапись prototype
UFO.prototype= {name: “UFO”, cows: 2};
prototype используется при вызове функции-
конструктора
Использование prototype
var UFO = function(name) {this.name = name;
};UFO.prototype.stealCow = function() {
// steal a cow};
Использование prototype
var ufo= new UFO(“Mars UFO”);ufo.name;// “Mars UFO”ufo.stealCow();// Cows--
Использование prototype
stealCow() — метод объекта prototype, но ведет себя так, как будто является методом самого объекта ufo
ufo.hasOwnProperty(‘name’);// trueufo.hasOwnProperty(‘stealCow’);// falseufo.prototype.hasOwnProperty(‘stealCow’);// true
Привязка свойств и методов
// к объекту thisfunction Cow(name) {
this.moo = function() { alert(“Moo”) };};// к объекту prototypefunction Cow(name) {};Cow.prototype.moo =
function () { alert(“Moo”) };};
prototype используется при поиске метода или
свойства
ufo: UFO
FO
UFO
Поиск свойств и методов
ufo.stealCow
found?
found?
found?
undefined
no
no
no
yes
yes
yes
isPrototypeOf()
UFO.prototype.isPrototypeOf(ufo);// trueObject.prototype.isPrototypeOf(ufo);// true
__proto__
Объекты имеют «секретную» ссылку на прототип конструктора, который их создал
__proto__ может не поддерживаться в некоторых браузерах
var UFO = function(){};var ufo = new UFO;ufo.__proto__ == UFO.prototype //true
__proto__
ufo.__proto__.hasOwnProperty(‘stealCow’)// true
ufo. __proto__. __proto__.hasOwnProperty(‘toString’)
// true
prototype наследуются
«Живые» prototype
typeof ufo.isCowsStealer;// “undefined”UFO.prototype.isCowsStealer = true;ufo. isCowsStealer;// true
Наращивание prototype
• Затрагивает все новые объекты• Затрагивает все созданные объекты(!)• Позволяет модифицировать
существующие объекты
String.prototype.trim = function() {return this.replace(/^\s+/, “”);
};alert(“ ufo”.trim());
Наращивание prototype
Number.prototype.times = function(f) {for (var i = 0; i < this; i++)
f();};(5).times(function(){ alert(“Moo”); });
Родительский конструктор
function FlyingObject() {this.name = “Flying Object”;this.getName = function() {
return this.name;};
};
«Детский» конструктор
function UFO() {this.shiny = true;this.round = true;
};
Замена prototype
UFO.prototype = new FlyingObject;
var ufo = new UFO();ufo.name = “Mars UFO”;
ufo.round; // trueufo.getName(); // “Mars UFO”
Вызов «superclass»-конструктора
function FlyingObject(name) {this.name = name;
}function UFO(name) {
// super(name)FlyingObject.call( this, name );this.stealCow = function() {};
};UFO.prototype = new FlyingObject;
Переопределение методов
function UFO(){};UFO.prototype.stealCow = function() { /*Steal a
Cow*/ };
function MegaUFO(){};MegaUFO.prototype = new Dow;
MegaUFO.prototype. stealCow = function() {// super.stealCow();UFO.prototype.stealCow.call(this);alert(“Yahoo!”);
};
Абстрактные «классы»
function UFO() {if (this._id == UFO._id) {
throw new Error(“No UFOs, please!”);}
}UFO._id = “UFO”;UFO.prototype._id = “UFO”;var ufo = new UFO(); // Error
Улучшение наследования
При использовании абстрактного класса появляется ошибкаMegaUFO.prototype = new UFO; // Error
Решение: использовать пустой порождающий объект для создания наследования
Порождающий объект
function inherit(o) {function Dummy(){};Dummy.prototype = o.prototype;return new Dummy();
}
MegaUFO.prototype = inherit(UFO);
FP в JavaScript
Nested Functions: Globals
function stealCow(cow) {// steal a cow
}function stealCows() {
this.radar(“foundCow”, stealCow); }
stealCows();
Nested Functions: Variables
var stealCow = function(cow) {// steal a cow
}var stealCows = function() {
this.radar(“foundCow”, stealCow); }
stealCows();
Nested Functions: Function First-style
function stealCows() {function stealCow(cow) {
// steal a cow}this.radar(“foundCow”, stealCow);
}
stealCows();
Nested Functions: Pyramid Order
Переменная stealCow создается до инициализации конкретной функцией
function stealCows() {this.radar(“foundCow”, stealCow); function stealCow(cow) {
// steal a cow}
}
stealCows();
Nested Functions: Inline function
function stealCows() {this.radar(“foundCow”, function stealCow(cow) {
// steal a cow});
}
stealCows();
Nested Functions: Amomymous
function stealCows() {this.radar(“foundCow”, function(cow) {
// steal a cow});
}
stealCows();
Передача функций как параметров
function moo(m) {return m + “moo”;
}function twice(fn) {
return function(x) {return fn(fn(x));
}}
var moomoo = twice(moo);moomoo(“Save your bodies”);
Хранение функций в таблице
var mooTable = {“+moo”: function(x) { return x + “Moo!”; },“+mo2”: function(x) { return x + “Moo-Moo!”; }
};mooTable[“+moo”](“UFOs!”);mooTable[“+mo2”](“Fresh Grass!”);
Построение реестра
var mooTable = {};function register(name, fn) { FnTable[name] = fn; }function makeMoomer(moo) {
return function(x) { return x + moo; }}register(“+moo”, makeMoomer(“Moo!”));register(“+mo2”, makeMoomer(“Moo-Moo!”));
mooTable[“+moo”](“UFOs!”);mooTable[“+mo2”](“Fresh Grass!”);
Ручные стражи
for (var cow in cows) steal(cow);
function callIfFat(fn) {return function(x) {
return isFat(x) ? fn(x) : undefined;}
}var stealFat = callIfFat(steal);
for (var cow in cows) stealFat(cow);
Guard Construction
function guard(fn ,g) {return function(x) {return g(x) ? fn(x) : undefined;}
}
var stealFat = guard(steal, isFat);
for (var cow in cows) stealFat(cow);
Замыкания
function callLater(o, property, value){ return function(){
o[property] = value; };
}
var shine = callLater(ufo, “shiny”, false);
wowEffect=setTimeout(shine, 500);
«Кража» методов
var ufo = { shine: function() {
this.shiny = true; }
};var cow = {};
ufo.shine(); // ufo is shinyufo.shine.call(cow); // cow is shiny!ufo.shine.apply(cow, []); // cow is shiny!
Это лучше, чем cow.shine = ufo.shine; cow.shine();
argumentsarguments – специальное свойство внутри функции, но это не Array!
// не работает!function joinInHerd() {
var herd = arguments.join(“, “);}
// «кража» метода у Arrayfunction joinInHerd() {
var herd = [].join.call(arguments, “, “);}
// вытаскивание метода из prototypefunction joinInHerd() {
var herd = Array.prototype.join.call(arguments, “, “);}
Наращивание prototype
Function.prototype.twice = function() {var fn = this;return function() {
return fn.call(this, fn.apply(this, arguments));};
}
function moo(x) { return x + “Moo!”; }var mo2 = moo.twice();mo2(“Ufos!!!”);
Анонимные функции
var UFO = function(name){this.name = name;this.getName = function() {
return this.name;};
};UFO.name // “”
Анонимная функция
$&*# | function
UFO | function
Анонимные функции
function guard(fn ,g) {return function(x) {
return g(x) ? fn(x) : undefined;}
}
Анонимная функция
Анонимные функции
Анонимные функции позволяют ограничить область видимости и область выполнения — idempotent function
function() {var cow = { name: “Cow” };stealCow (cow);
}();
bind
function shine() {this.shiny = true;
}shine(); // Window is shinyy = shine.bind(cow);y(); // cow is shiny
bind (prototypejs)
function bind(context) { if (arguments.length < 2 &&
Object.isUndefined(arguments[0])) return this;
var __method = this, args = slice.call(arguments, 1);
return function() { var a = merge(args, arguments); return __method.apply(context, a); }
}
Слайд #100