JavaScript OOP

Preview:

DESCRIPTION

Лекция Константина Кичинского по ООП в JavaScript в рамках курса по ООП. Каф. Вычислительная математика и программирование, МАИ

Citation preview

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

Recommended