Java Script

Описание: Наиболее перспективное направление развития компьютерных наук. Надо быть в теме!

dyvniy M
Автор темы, Администратор
Администратор
Аватара
dyvniy M
Автор темы, Администратор
Администратор
Возраст: 41
Репутация: 1
Лояльность: 1
Сообщения: 3579
Зарегистрирован: Ср, 10 октября 2012
С нами: 11 лет 5 месяцев
Профессия: Программист
Откуда: Россия, Москва
ICQ Сайт Skype ВКонтакте

#7 dyvniy » Вт, 14 апреля 2015, 12:26:37

ВВЕДЕНИЕ В JAVASCRIPT ДЛЯ МАГА
Стефан Кох (Stefan Koch)
http://www.helloworld.ru/texts/comp/lang/jscript/jscript/part8/part8.htm
Изображение

dyvniy M
Автор темы, Администратор
Администратор
Аватара
dyvniy M
Автор темы, Администратор
Администратор
Возраст: 41
Репутация: 1
Лояльность: 1
Сообщения: 3579
Зарегистрирован: Ср, 10 октября 2012
С нами: 11 лет 5 месяцев
Профессия: Программист
Откуда: Россия, Москва
ICQ Сайт Skype ВКонтакте

#8 dyvniy » Пт, 17 апреля 2015, 17:21:57

TCP Socket on JavaScript
http://stackoverflow.com/questions/5373819/tcp-client-in-javascript

Код: Выделить всё


var conn 
= new TCPSocket(hostname, port)
conn.onopen = function() { alert('connection opened!') }
conn.onread = function(data) { alert('RECEIVE: ' + data) }
conn.onclose = function(data) { alert('connection closed!') }
conn.send('Hello World');

Только что-то не работает...
Изображение

dyvniy M
Автор темы, Администратор
Администратор
Аватара
dyvniy M
Автор темы, Администратор
Администратор
Возраст: 41
Репутация: 1
Лояльность: 1
Сообщения: 3579
Зарегистрирован: Ср, 10 октября 2012
С нами: 11 лет 5 месяцев
Профессия: Программист
Откуда: Россия, Москва
ICQ Сайт Skype ВКонтакте

#9 dyvniy » Пн, 20 апреля 2015, 11:46:47

Оказывается, если не указывать в шапке пакета его размер, он всё равно дойдёт, но за большее время, порядка секунды.
php это делает автоматически, а вот в С++ реализациях может потребоваться писать ручками.

структура http запроса http://webgyry.info/obshhaya-struktura-http-zaprosov-i-otvetov
Изображение
Изображение

dyvniy M
Автор темы, Администратор
Администратор
Аватара
dyvniy M
Автор темы, Администратор
Администратор
Возраст: 41
Репутация: 1
Лояльность: 1
Сообщения: 3579
Зарегистрирован: Ср, 10 октября 2012
С нами: 11 лет 5 месяцев
Профессия: Программист
Откуда: Россия, Москва
ICQ Сайт Skype ВКонтакте

#10 dyvniy » Ср, 22 апреля 2015, 15:12:34

html5 Canvas во весь экран
http://stackoverflow.com/questions/4037212/html-canvas-full-screen
Спойлер

Код: Выделить всё

function init()
{
    canvas = document.getElementById("mainCanvas");
    canvas.width = document.body.clientWidth; //document.width is obsolete
    canvas.height = document.body.clientHeight; //document.height is obsolete
    canvasW = canvas.width;
    canvasH = canvas.height;

    if( canvas.getContext )
    {
        setup();
        setInterval( run , 33 );
    }


Код: Выделить всё

$(window).bind("resize", function(){
    var w = $(window).width();
    var h = $(window).height();

    $("#mycanvas").css("width", w + "px");
    $("#mycanvas").css("height", h + "px"); 
});

//using HTML5 for fullscreen (only newest Chrome + FF)
$("#mycanvas")[0].webkitRequestFullScreen(Element.ALLOW_KEYBOARD_INPUT); //Chrome
$("#mycanvas")[0].mozRequestFullScreen(); //Firefox

//...

//now i want to cancel fullscreen
document.webkitCancelFullScreen(); //Chrome
document.mozCancelFullScreen(); //Firefox     

http://www.pixelcom.crimea.ua/html5-fullscreen.html
Спойлер
HTML5 fullscreen
08.07.2013 ВИКТОР КЛИМ ДОБАВИТЬ КОММЕНТАРИЙ
Новая технология HTML5 — fullscreen API (полно-экранный режим) — это простой способ отобразить содержимое веб-страницы в полно-экранном режиме. В этой статье подробная информация о новой технологии HTML5 fullscreen. Основное преимущество и возможности технологии HTML5 fullscreen это смотреть фотоальбомы, видео, и даже игры в полно-экранном режиме.


HTML5 fullscreen (полно-экранный режим)

технология HTML5 fullscreen является экспериментальной, и не все браузеры поддерживают её.

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

Хром 15 +
Firefox 9 +
Сафари 5 +
Opera 12.10 +
Internet Explorer 11 +
Демо

Применить HTML5 fullscreen

Из-за того, что этот режим поддерживается на разных браузерах по-разному, мы должны предусмотреть все случаи:

var elem = document.getElementById("myObject");
if (elem.requestFullscreen) {
elem.requestFullscreen();
} else if (elem.mozRequestFullScreen) {
elem.mozRequestFullScreen();
} else if (elem.webkitRequestFullscreen) {
elem.webkitRequestFullscreen();
}
Мы запросили полно-экранный режиме для любого элемента DOM (или мы можем применять для всей страницы — document.documentElement). Код направляет запрос пользователю о наличии разрешения на включение полно-экранного режима, если этот запрос будет принят, все панели инструментов и другие панели браузера исчезнут, и на экране появится элемент или вся веб-страница нужная для просмотра в полно-экранном режиме.

CSS псевдо-класс

В CSS был добавлен псевдо-класс — :full-screen. Он используется для того что бы применить стиль к элементам, которые находятся в полно-экранном режиме.

:-webkit-full-screen #myObject {
font-size: 20px;
width: 100%;
}
:-moz-full-screen #myObject {
font-size: 20px;
width: 100%;
}
Дополнительные события

Существует несколько оповещений: при успешном активизировании полно-экранного режима, документ получает событие mozfullscreenchange. Когда полно-экранный режим отменён, документ получает снова событие mozfullscreenchange. Обратите внимание, что это событие не показывает, документ в полно-экранный режиме, или нет. Подсказка: если документ имеет значение, отличное от null mozFullScreenElement, мы в полно-экранном режиме.

Что, если запрос fullscreen не выполняется? Если это не удается, значит элемент, который вызвал fullscreen получит событие fullscreenerror. Плюс, ваш браузер будет сообщать ошибку в Веб-Консоли.

Что бы выйти из полно-экранного режима, вызываем метод cancelFullScreen.

Включить/выключить полно-экранный режим

Вот пример, который можно использовать для перехода к другой странице документа в полно-экранном режиме. Существуют два обработчика событий (для работы с mozfullscreenerror и события keydown). Используйте F или Enter, чтобы включить полно-экранный режим.

// mozfullscreenerror event handler
function errorHandler() {
alert('mozfullscreenerror');
}
document.documentElement.addEventListener('mozfullscreenerror', errorHandler, false);

// toggle full screen
function toggleFullScreen() {
if (!document.fullscreenElement && // alternative standard method
!document.mozFullScreenElement && !document.webkitFullscreenElement) { // current working methods
if (document.documentElement.requestFullscreen) {
document.documentElement.requestFullscreen();
} else if (document.documentElement.mozRequestFullScreen) {
document.documentElement.mozRequestFullScreen();
} else if (document.documentElement.webkitRequestFullscreen) {
document.documentElement.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT);
}
} else {
if (document.cancelFullScreen) {
document.cancelFullScreen();
} else if (document.mozCancelFullScreen) {
document.mozCancelFullScreen();
} else if (document.webkitCancelFullScreen) {
document.webkitCancelFullScreen();
}
}
}

// keydown event handler
document.addEventListener('keydown', function(e) {
if (e.keyCode == 13 || e.keyCode == 70) { // F or Enter key
toggleFullScreen();
}
}, false);
Методы и События API

partial interface Element {
void requestFullscreen();
};

partial interface Document {
readonly attribute boolean fullscreenEnabled;
readonly attribute Element? fullscreenElement;

void exitFullscreen();
};
Пояснение

element.requestFullscreen() — отображает элемент в полно-экранном режиме.
document.fullscreenEnabled — возвращает true, если документ имеет возможность отображения элементов fullscreen, false если нет.
document.fullscreenElement — возвращает элемент, который отображается в полно-экранном режиме, или null если такого элемента нет.
document.exitFullscreen() — останавливает все элементы в документе и не отображать полный экран.
В заключение

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

Статья подготовлена для вас коллективом сайта www.pixelcom.crimea.ua
Оригинал статьи: www.script-tutorials.com/new-technology-fullscreen-mode
Перевел: Виктор Клим

http://ruseller.com/lessons.php?id=1698&rub=1
Спойлер
Используем HTML5 Full-Screen API

HTML5 состоит из множества отдельных компонентов, сопровождённых специальными API. Среди них и Fullscreen API, который позволяет сделать то, что раньше мог только Flash: отобразить приложение в полно-экранном режиме.

Данная возможность может вам понадобиться, если вы создаёте видео плеер, галерею изображений или игру.

Самое классное во всём этом то, что использовать Fullscreen API очень просто.

Методы

Fullscreen API состоит из множества методов. Благодаря следующей команде, можно вывести в полно-экранный режим отдельный элемент:

1
element.requestFullScreen()
Следующая команда сделает то же самое с элементом canvas с id = myCanvas:

1
Document.getElementById(“myCanvas”).requestFullScreen()
Теперь давайте рассмотрим, как можно выйти из полно-экранного режима:

1
document.cancelFullScreen()
Следующая запись вернёт true, если страница находится в полно-экранном режиме:

1
Document.fullScreen
Для того чтобы получить элемент, который задействован в полно-экранном режиме, нужно выполнить следующую команду:

1
document.fullScreenElement
Прошу обратить внимание, что перечисленные команды являются стандартными для Fullscreen API. Однако для того, чтобы данные механизмы работали в других браузерах, необходимо воспользоваться браузерными префиксами (IE и Opera вообще никак не поддерживают Fullscreen API).

Запуск полно-экранного режима

Для того, чтобы определить, каким браузером пользуется пользователь, напишем специальные проверки и поместим их в функцию:

1
function fullScreen(element) {
2
if(element.requestFullScreen) {
3
element.requestFullScreen();
4
} else if(element.webkitRequestFullScreen ) {
5
element.webkitRequestFullScreen();
6
} else if(element.mozRequestFullScreen) {
7
element.mozRequestFullScreen();
8
}
9
}
Если хотя бы один из методов вернёт true, то мы точно узнаем браузер пользователя и если в нём есть поддержка Fullscreen API, воспользуемся этим.

После этого, всё что нам остаётся сделать, так это переключить пользователя в полно-экранный режим:

1
// всю страницу
2
var html = document.documentElement;
3
fullScreen(html);
4

5
// специфический элемент страницы
6
var canvas = document.getElementById('mycanvas');
7
fullScreen(canvas);
Сначала пользователю высветится окошко, чтобы он разрешил переключиться в другой режим, а затем, в зависимости от его решения, будет произведена та или иная операция

Выход из полно-экранного режима

Для выхода из полно-экранного режима нужно так же воспользоваться специальными методами с префиксами.

Особенность данных методов в том, что их нужно применить не к каким-то отдельным элементам, а к документу в целом:

01
function fullScreenCancel() {
02
if(document.requestFullScreen) {
03
document.requestFullScreen();
04
} else if(document .webkitRequestFullScreen ) {
05
document.webkitRequestFullScreen();
06
} else if(document .mozRequestFullScreen) {
07
document.mozRequestFullScreen();
08
}
09
}
10

11
fullScreenCancel();
Псевдо-классы CSS

Благодаря специальным псевдо классам CSS, вы можете изменить размеры некоторых элементов страницы при переходе в полно-экранный режим:

01
/* изменяем размер шрифта */
02
:-webkit-full-screen {
03
font-size: 16px;
04
}
05
:-moz-full-screen {
06
font-size: 16px;
07
}
08

09
/*меняем отдельный элемент */
10
:-webkit-full-screen img {
11
width: 100%;
12
height: 100%;
13
}
14
:-moz-full-screen img {
15
width: 100%;
16
height: 100%;
17
}
На заметку: нельзя перечислять селекторы через запятую. Браузеры пока этого не понимают:

1
/* это не сработает */
2
:-webkit-full-screen img,:-moz-full-screen img {
3
width: 100%;
4
height: 100%;
5
}
Для того чтобы всё работало, каждый селектор с браузерным префиксом нужно поместить в отдельный блок.

Итог

JavaScript API, на котором и работает интерактивность HTML5, довольно-таки прост в изучении и применении. Пока что есть конечно проблемы кроссбраузерности, но это дело времени. Изучать это дело можно уже сейчас.
Изображение

dyvniy M
Автор темы, Администратор
Администратор
Аватара
dyvniy M
Автор темы, Администратор
Администратор
Возраст: 41
Репутация: 1
Лояльность: 1
Сообщения: 3579
Зарегистрирован: Ср, 10 октября 2012
С нами: 11 лет 5 месяцев
Профессия: Программист
Откуда: Россия, Москва
ICQ Сайт Skype ВКонтакте

#11 dyvniy » Пн, 11 мая 2015, 18:35:29

Выбрать браузер и повернуть
http://javascript.ru/blog/plush-grigorij/funkciya-opredeleniya-brauzera-ego-versii
Спойлер
Функция определения браузера и его версии

Как-то потребовалось создать функцию, принимающую объект json со следующим форматом записи: "браузер":то, что следует вернуть, иначе говоря такую функцию, которая бы возвращала указанный объект в зависимости от браузера пользователя.

ТЗ, которое я для себя определил:

браузеры можно группировать, указывая их через пробел
браузерам можно задавать версию (ie6,opera10)
браузерам можно задавать полную версию (opera10.15)
поддержка самых популярных браузеров
Мой способ гарантированно не самый точный, так как он используют свойство userAgent объекта navigator, которое при желании подделывается, но с другой стороны любой способ проверки клиента как серверный, так и клиентский можно обойти. Так же встает вопрос о надобности такой "подделки", тем более мало какие роботы исполняют javascript код.

После изучения следующей статьи http://ru.wikipedia.org/wiki/Useragent, я написал функцию ifBrowser. Она поддерживает проверку следующий браузеров: Internet Explorer, Firefox, Opera, Chrome, Safari, Konqueror, Iceweasel, SeaMonkey.

Код функции:

01
/*
02
ifBrowser 0.0.3
03
a function which returns specified data depending on a user's browser
04
written by Plyushch Gregory, 2012
05
is free to use for everyone in condition of saving the author's name
06
*/
07
function ifBrowser (obj) {
08
var none = obj["none"] || "none";
09
function cBrowser() {
10
var ua = navigator.userAgent;
11
var bName = function () {
12
if (ua.search(/MSIE/) > -1) return "ie";
13
if (ua.search(/Firefox/) > -1) return "firefox";
14
if (ua.search(/Opera/) > -1) return "opera";
15
if (ua.search(/Chrome/) > -1) return "chrome";
16
if (ua.search(/Safari/) > -1) return "safari";
17
if (ua.search(/Konqueror/) > -1) return "konqueror";
18
if (ua.search(/Iceweasel/) > -1) return "iceweasel";
19
if (ua.search(/SeaMonkey/) > -1) return "seamonkey";}();
20
var version = function (bName) {
21
switch (bName) {
22
case "ie" : return (ua.split("MSIE ")[1]).split(";")[0];break;
23
case "firefox" : return ua.split("Firefox/")[1];break;
24
case "opera" : return ua.split("Version/")[1];break;
25
case "chrome" : return (ua.split("Chrome/")[1]).split(" ")[0];break;
26
case "safari" : return (ua.split("Version/")[1]).split(" ")[0];break;
27
case "konqueror" : return (ua.split("KHTML/")[1]).split(" ")[0];break;
28
case "iceweasel" : return (ua.split("Iceweasel/")[1]).split(" ")[0];break;
29
case "seamonkey" : return ua.split("SeaMonkey/")[1];break;
30
}}(bName);
31
return [bName,bName + version.split(".")[0],bName + version];
32
}
33
var current_browser = cBrowser();
34
for (var key in obj) {
35
var trg = key.toLowerCase();
36
if (trg.indexOf(current_browser[2]) > -1) return obj[key];
37
else if (trg.indexOf(current_browser[1]) > -1) return obj[key];
38
else {
39
var nsymbol = trg.charAt(trg.indexOf(current_browser[0])+current_browser[0].length);
40
if (trg.indexOf(current_browser[0]) > -1 && (nsymbol == " " || nsymbol == "")) return obj[key];
41
};
42
}
43
return none;
44
}
http://gregivy.tf9.ru/ivyco/ifbrowser.js

В случае если ни одного выражение не совпадает с браузером пользователя, возвращается "none", которое можно также указать как свойство передаваемого объекта.

Пример использования: (данный код повернет на 25 градусов все содержимое страницы в любом современном браузере)

1
var transform = ifBrowser({
2
"ie":"msTransform",
3
"chrome safari":"WebkitTransform",
4
"firefox":"MozTransform",
5
"opera":"OTransform",
6

7
}) // определяем название нужного нам css свойства
8
document.body.style[transform] = "rotate(25deg)"
Пример наклона картинки на 60 градусов

Название браузера нужно вводить в следующем формате : ie, firefox, opera, chrome, safari, konqueror, iceweasel, seamonkey. Регистр неважен. Версия пишется слитно с название, пример: ie7. Если для нескольких браузеров должно возвратиться одно и тоже значение, вы просто указываете их через пробел, как
на примере ("chrome safari":"WebkitTransform"). В качестве возвращаемого значения может выступать любой объект JavaScript (строка, число, функция, логическое значение и т.д.)
Изображение

dyvniy M
Автор темы, Администратор
Администратор
Аватара
dyvniy M
Автор темы, Администратор
Администратор
Возраст: 41
Репутация: 1
Лояльность: 1
Сообщения: 3579
Зарегистрирован: Ср, 10 октября 2012
С нами: 11 лет 5 месяцев
Профессия: Программист
Откуда: Россия, Москва
ICQ Сайт Skype ВКонтакте

#12 dyvniy » Чт, 24 сентября 2015, 16:38:07

Прототипичность
http://habrahabr.ru/post/178133/
Спойлер
Особенности работы или «За что я люблю JavaScript»: Замыкания, Прототипирование и Контекст tutorial
JavaScript*
Зародившись как скриптовый язык в помощь веб-разработчикам, с дальнейшим развитием JavaScript стал мощным инструментом разработки клиентской части, обеспечивающий удобство и интерактивность страницы прямо в браузере у пользователя.

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

Думаю, что большинство программистов, писавших код на JavaScript больше пары дней, сталкивались с этими особенностями. Цель данного топика не открыть что-то новое, а попытаться описать эти особенности «на пальцах» и «недостатки» сделать «преимуществами».

В данном топике будут рассматриваться:

Замыкания
Прототипирование
Контекст выполнения


Предисловие

Мне, как автору, конечно же хочется описать все-все-все возможности, которыми богат JavaScript. Однако если я просто попытаюсь сделать это, статья растянется на огромное количество страниц, и многие начинающие разработчики просто не смогут запомнить всю информацию. Поэтому приводимые примеры могут кому-то показаться слишком простыми, а темы раскрытыми не до конца. Но, надеюсь, статья сумеет заинтересовать тех, кто ещё не сильно знаком с данными особенностями, а тем, кто уже знаком, — помочь понять, что на самом-то деле всё элементарно.

Замыкания, или «Эти необычные области видимости»

«Архиполезная штука!» — этими двумя словами можно выразить
моё отношение к замыканиям и их реализации в JavaScript.


Суть замыканий проста: внутри функции можно использовать все пременные, которые доступны в том месте, где функция была объявлена.

Хотя идея замыканий проста, на практике зачастую возникает много непонятных моментов по поведению в том или ином случае. Так что для начала вспомним основы объявления переменной, а именно – "переменные в JavaScript объявляются с помощью ключевого слова var":

var title = "Hello World";
alert(title);


При запуске кода выведет текст "Hello World", как и ожидалось. Суть происходящего проста – создаётся глобальная переменная title со значением "Hello World", которое показывается пользователю с помощью alert-а. В данном примере, даже если мы опустим ключевое слово var, код всё равно сработает правильно из-за глобального контекста. Но об этом позже.

Теперь попробуем объявить ту же переменную, но уже внутри функции:

function example (){
var title = "Hello World";
}
alert(title);


В результате запуска кода сгенерируется ошибка "'title' is undefined" — "переменная 'title' не была объявлена". Это происходит из-за механизма локальной области видимости переменных: все переменные, объявленные внутри фукнции являются локальными и видны только внутри этой функции. Или проще: если мы объявим какую-то переменную внутри функции, то вне этой функции доступа к этой переменной у нас не будет.

Для того, чтобы вывести надпись "Hello World", необходимо вызвать alert внутри вызываемой функции:

function example(){
var title = "Hello World";
alert(title);
}
example();


Либо вернуть значение из функции:

function example(){
var title = "Hello World";
return title;
}
alert(example());


Думаю, что все эти примеры очевидны — подобное поведение реализовано практически во всех языках программирования. Так в чём же особенность замыканий в JavaScript, что так сильно отличает реализацию от других языков?

Ключевое отличие в том, что в JavaScript-е функции можно объявлять внутри других функций, а сами функции в JavaScript являются объектами! Благодаря этому с ними можно производить те же действия, что и с обычными объектами — проверять на существование, присваивать переменным, добавлять свойства, вызывать методы и возвращать объект функции как разультат выполнения другой функции!

Так как функция — это объект, то это значит, что механизм областей видимости переменных распространяется и на функции: функция, объявленная внутри другой функции, видна только там, где она была объявлена

function A(){
function B(){
alert("Hello World");
}
}
B();


Как и в примере с переменной, при запуске кода сгенерируется ошибка, что переменная B не была объявлена. Если же поместить вызов функции B сразу после объявления внутри функции A, и вызвать саму функцию A — получим заветное сообщение "Hello World"

function A(){
function B(){
alert("Hello World");
}
B();
}
A();


Теперь приступим к описанию того, обо что спотыкаются большинство начинающих изучать JavaScript – определению того, откуда переменные берут свои значения. Как упоминалось выше, переменные нужно объявлять с помощью ключевого слова var:

var title = 'external';

function example(){
var title = 'internal';
alert(title);
}

example();
alert(title);


В данном примере переменная title была объявлена дважды – первый раз глобально, а второй раз – внутри функции. Благодара тому, что внутри функции example переменная title была объявлена с помощью ключевого слова var, она становится локальной и никак не связана с переменной title, объявленной до функции. В результате выполнения кода вначале выведется "internal" (внутренняя переменная), а затем "external" (глобальная переменная).

Если убрать ключевое слово var из строки var title = 'internal', то запустив код, в результате дважды получим сообщение "internal". Это происходит из-за того, что при вызове функции мы объявляли не локальную переменную title, а перезаписывали значение глобальной переменной!

Таким образом можно увидеть, что использование ключевого слова var делает переменную локальной, гарантируя отсутствие конфликтов с внешними переменными (к примеру, в PHP все переменные внутри функции по умолчанию являются локальными; и для того, чтобы обратиться к глобальной переменной необходимо объявить её глобальной с помощью ключевого слова global).

Скрытый текст
Следует помнить, что все параметры функции автоматически являются локальными переменными:

var title = "external title";
function example(title){
title = "changing external title";
alert(title);
}

example('abc');
alert(title);

При запуске кода сгенерируются сообщения "changing external title", а затем "external title", показывающее, что внешняя переменная title не была изменена внутри функции, хотя мы её и не объявляли с помощью var.

Сам процесс инициализации локальных переменных происходит до выполнения кода — для этого интерпретатор пробегается по коду функции и инициализирует (не присваивая значений!) все найденные локальные переменные:

var title = "external title";
function example(){
title = "changing external title";
alert(title);
var title = "internal title";
}

example();
alert(title);


Как и в предыдущем примере, при запуске кода сгенерируются сообщения "changing external title", а затем "external title", показывающее, что внешняя переменная title не была изменена внутри функции.

Если закомментировать строку title = «changing external title»;, то при первым сгенерированным сообщением станет "undefined" — локальная переменная title уже была инициализирована (существует), но значение (на момент вызова alert-а) не было присвоено.

Код:
function example(){
alert(title);
var title = "internal title";
}

эквивалентен следующему:
function example(){
var title;
alert(title);
title = "internal title";
}


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



Итак, как же определить какая переменная используется в функции?


Если при объявлении фукнции переменная не была объявлена локально с помощью ключевого слова var, переменная будет искаться в родительской функции. Если она не будет там найдена — поиск будет происходить дальше по цепочке функций-родителей до тех пор, пока интерпретатор не найдёт объявление переменной, либо не дойдёт до глобальной области видимости.

Если объявление переменной не будет найдено ни вверх по цепочке объявлений фукнции, ни в глобальной области видимости, существует два варианта развития:
При попытке использовать (получить значение) переменной сгенерируется ошибка, что переменная не была объявлена
При попытке присвоить переменной значение, переменная будет создана в глобальной области видимости, и ей присвоится значение.


function A(){
title = 'internal';
return function B(){
alert(title);
}
}
var B = A();
B();
alert(title);


Выполнив код, получим оба раза "internal". Присваивание значения переменной title внутри функции A создаёт глобальную переменную, которую можно использовать и вне функции. Следует иметь в виду, что присвоение значения переменной (а значит и создание глобальной переменной) происходит на этапе вызова функции А, так что попытка вызвать alert(title) до вызыва функции A сгенерирует ошибку.

Скрытый текст

А теперь вернёмся обратно к теме замыканий в JavaScript.

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

Как известно, все локальные переменные создаются заново при каждом новом вызове функции. Например, у нас есть функция A, внутри которой объявляется переменная title:

function A(){
var title = 'internal';
alert(title);
}
A();


После того, как функция A будет выполнена, переменная title перестанет существовать и к ней никак нельзя получить доступ. Попытка как-либо обратиться к переменной вызовет ошибку, что переменная не была объявлена.

Теперь внутри функции A добавим объявлении функции, выводящую значение переменной title, а тажке функцию, которая это значение изменяет на переданное, и вернём эти функции:

function getTitle (){
var title = "default title";
var showTitle = function(){
alert(title);
};
var setTitle = function(newTitle){
title = newTitle;
};
return {
"showTitle": showTitle,
"setTitle": setTitle
};
}
var t = getTitle();
t.showTitle();
t.setTitle("Hello World");
t.showTitle();


До того, как запустить этот пример, попробуем рассмотреть логически поведение переменной title: при запуске функции getTitle переменная создаётся, а после окончания вызова – уничтожается. Однако при вызове функции getTitle возвращается объект с двумя динамически-объявленными функциями showTitle и setTitle, которые используют эту переменную. Что же произойдёт, если вызвать эти функции?

И теперь, запустив пример, можно увидеть, что вначале выведется "default title", а затем "Hello World". Таким образом переменная title продолжает существовать, хотя функция getTitle уже давно завершилась. При этом к данной переменной нет другого доступа, кроме как из вышеупомянутых функций showTitle/setTitle. Это и есть простой пример замыкания – переменная title «замкнулась» и стала видимой только для тех функций, которые имели к ней доступ во время своего объявления.

Если запустить функцию getTitle ещё раз, то можно увидеть, что переменная title, как и функции showTitle/setTitle, каждый раз создаются заново, и никак не связаны с предыдущими запусками:

var t1 = getTitle();
t1.setTitle("Hello World 1");

var t2 = getTitle();
t2.setTitle("Hello World 2");

t1.showTitle();
t2.showTitle();


Запустив код (не забыв добавить выше код функции getTitle), будет сгенерировано два сообщения: "Hello World 1" и "Hello World 2" (подобное поведение используется для эмуляции приватных переменных).

Оставив теорию и простейшие примеры, и попытаемся понять какую выгоду можно извлечь из замыканий на практике:


Первое — это возможность не засорять глобальную область видимости.

Проблема конфликтов в глобальной области видимости очевидна. Простой пример: если на странице подключаются несколько javascript файлов, объявляющие функцию showTitle, то при вызове showTitle будет выполняться функция, объявленная последней. То же самое относится и к объявленным переменным.

Чтобы избежать подобной ситуации, необходимо либо каждую функцию/переменную называть уникальным именем, либо использовать замыкания. Извращаться и называть каждую функцию и переменную уникальным именем неудобно, и всё равно не гарантирует полной уникальности. С другой стороны механизм замыканий предоставляет полную свободу в именовании переменных и функций. Для этого достаточно весь код обернуть в анонимную функцию, выполняющуюся сразу после объявления:

(function(){
/* объявление функций, переменных и выполнение кода */
})();


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

Что же делать, если всё же необходимо чтобы одна или две функции были доступны глобально? И тут всё довольно просто. Самый-самый глобальный объект, обеспечивающий глобальную область видимости — это объект window. Благодаря тому, что функция — это объект, её можно присвоить свойству window, чтобы та стала глобальной. Пример объявления глобальной функции из закрытой области видимости:

(function(){
var title = "Hello World";
function showTitle(){
alert(title);
}
window.showSimpleTitle = showTitle;
})();
showSimpleTitle();


В результате выполнения кода сгенерируется сообщение "Hello World" – локальная функция showTitle стала доступна глобально под именем showSimpleTitle, при этом использует «замкнутую» переменную title, недоступную вне нашей анонимной функции.

Т.к. мы всё обернули в анонимную функцию, которая сразу же выполняется, этой функции можно передать параметры, которые внутри этой функции будут доступны под локальными называниями. Пример с jQuery:

(function(){
$('.hidden').hide();
})();


Вызовет ошибку, если глобальная переменная $ не является jQuery. А такое случается, если помимо jQuery используется другая библиотека, которая использует функцию $, к примеру, Prototype.JS. Решение «в лоб»:

(function(){
var $ = jQuery;
$('.hidden').hide();
})();


Будет работать, и будет работать правильно. Но не очень красиво. Есть более красивое решение — объявить локальную переменную $ в виде аргумента функции, передав туда объект jQuery:

(function($){
/* Код, использующий $ */
})(jQuery);


Если вспомнить, что все аргументы функции по умолчанию являются локальными переменными, то становится ясно, что теперь внутри нашей анонимной функции $ никак не связан с глобальным объектом $, являясь ссылкой на объект jQuery. Для того, чтобы приём с анонимной функций стал понятнее, можно анонимную функцию сделать неанонимной – объявили функцию и сразу же её запустили:

function __run($){
/* code */
}
__run(jQuery);


Ну, а если всё же понадобится вызвать глобальную функцию $, можно воспользоваться window.$.

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

При использовании событийной модели, часто возникают ситуации, когда нужно повесить одно и то же событие, но на разные элементы. Например, у нас есть 10 div-элементов, по клику на которые нужно вызывать alert(N), где N — какой-либо уникальный номер элемента.

Простейшее решение c использованием замыкания:

for(var counter=1; counter <=10; counter++){
$('<div>').css({
"border": "solid 1px blue",
"height": "50px",
"margin": "10px",
"text-align": "center",
"width": "100px"
}).html('<h1>'+ counter +'</h1>')
.appendTo('body')
.click(function(){
alert(counter);
});
}


Однако выполнение данного кода приводит к «неожиданному» результату — все клики выводят одно и то же число — 11. Догадываетесь почему?

Ответ прост: значение переменной counter берётся в момент клика по элементу. А так как к тому времени значение переменной стало 11 (условие выхода из цикла), то и выводится соответственно число 11 для всех элементов.

Правильное решение — динамически генерировать функцию обработки клика отдельно для каждого элемента:

for(var counter=1; counter <=10; counter ++){
$('<div>').css({
"border": "solid 1px blue",
"height": "50px",
"margin": "10px",
"text-align": "center",
"width": "100px"
}).html('<h1>'+ counter +'</h1>')
.appendTo('body')
.click((function(iCounter){
return function(){
alert(iCounter);
}
})(counter));
}


В данном подходе мы используем анонимную функцию, которая принимает значение counter в виде параметра и возвращает динамическую функцию. Внутри анонимной функции локальная переменная iCounter содержит текущее значение counter на момент вызова функции. А так как при каждом вызове любой функции все локальные переменные объявляются заново (создаётся новое замыкание), то при вызове нашей анонимной функции каждый раз будет возращаться новая динамическая функция с уже «замкнутым» номером.

Если говорить проще, то запустив функцию второй (третий, четвёртый...) раз, все локальные переменные будут созданы в памяти заново и не будут иметь никакого отношения к переменным, созданным во время предыдущего вызова функции.

Сложно? Думаю, что с первого раза — да. Зато не нужно иметь кучу глобальных переменных, и делать проверки в функции «откуда же меня вызвали...». А с использованием jQuery.each, который по-умолчанию вызвает переданную функцию, код становится ещё проще и читабельнее:

$('div.handle-click').each(function(counter){
$(this).click(function(){
alert(counter);
});
});


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

Скрытый текст

Прототипирование или «Я хочу сделать объект класса»

Про прототипирование в JavaScript написано много хороших статей. Поэтому постараюсь не повторять то, что уже написано, а просто опишу основу механизма прототипов.

В JavaScript-е есть понятие объекта, но нет понятия класса. Из-за слабой типизации, Весь JavaScript основан на объектах, поэтому почти все данные в JavaScript-е являются объектами, или могут быть использованы как объекты. А в качестве альтернативы классам есть возможность прототипирования — назначать объекту прототип со свойствами и методами «по умолчанию».

Работать с объектами в JavaScript очень просто — нужно всего лишь объявить объект и назначить ему свойства и методы:

var dog = {
"name": "Rocky",
"age": "5",
"talk": function(){
alert('Name: ' + this.name + ', Age: ' + this.age);
}
};


Если у нас много объектов, то удобнее будет сделать отдельную функцию, возвращающую объект:

function getDog(name, age){
return {
"name": name,
"age": age,
"talk": function(){
alert('Name: ' + this.name + ', Age: ' + this.age);
}
};
}
var rocky = getDog('Rocky', 5);
var jerry = getDog('Jerry', 3);


То же самое можно сделать с использованием прототипов:

function Dog(name, age){
this['name'] = name;
this.age = age;
}
Dog.prototype = {
"talk": function(){
alert('Name: ' + this.name + ', Age: ' + this.age);
}
};

var rocky = new Dog('Rocky', 5);
var jerry = new Dog('Jerry', 3);


Как упоминалось выше, прототип — это простой объект, который содержит свойства и методы «по умолчанию». Т.е. если у какого-либо объекта попытаться получить свойство или вызвать функцию, которой у объекта нет, то JavaScript интерпретатор, прежде чем сгенерировать ошибку, попытается найти это свойство/функцию в объекте-прототипе и, если оно будет найдено, будет использоваться свойство/функция из прототипа.

Скрытый текст

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

И небольшой наглядный пример расширения возможностей существующих объектов с помощью прототипов

Необходимо получить название дня недели от какой-либо даты, но встроенный объект Date содержит только метод getDay, возвращающий числовое представление дня недели от 0 до 6: от воскресенья до субботы.

Можно сделать так:

function getDayName(date){
var days = ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'];
return days[date.getDay()];
}
var today = new Date();
alert(getDayName(today));


Или использовать прототипирование и расширить встроенный объект даты:

Date.prototype.getDayName = function(){
var days = ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'];
return days[this.getDay()];
}
var today = new Date();
alert(today.getDayName());


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

Контекст выполнения или «Этот загадочный this»

Переходя на JavaScript с других языков программирования, где используется ООП, довольно сложно понять, что же в JavaScript означает объект this. Если попытаться объяснить просто, то this — это ссылка на объект, для которого вызывается функция. Например:

var exampleObject = {
"title": "Example Title",
"showTitle": function(){
alert(this.title);
}
};
exampleObject.showTitle();


Из примера видно, что при вызове exampleObject.showTitle() функция вызывается как метод объекта, и внутри функции this ссылается на объект exampleObject, вызвавший функцию. Сами по себе функции никак не привязаны к объекту и существуют отдельно. Привязка контекста происходит непосредственно во время вызова функции:

function showTitle(){
alert(this.title);
}

var objectA = {
"title": "Title A",
"showTitle": showTitle
};

var objectB = {
"title": "Title B",
"showTitle": showTitle
};

objectA.showTitle();
objectB.showTitle();


В данном примере наглядно показывается, что при вызове objectA.showTitle(), this ссылается на objectA, а при вызове objectB.showTitle() — на objectB. Сама функция showTitle существует отдельно и просто присваивается объектам как свойство во время создания.

Если при вызове функции она (функция) не ссылается ни на один объект, то this внутри функции будет ссылаться на глобальный объект window. Т.е. если просто вызвать showTitle(), то будет сгенерирована ошибка, что переменная title не объявлена; однако если объявить глобальную переменную title, то функция выведет значение этой переменной:

var title = "Global Title";
function showTitle(){
alert(this.title);
}
showTitle();


Скрытый текст

Чтобы продемонстрировать, что контекст функции определяется именно во время вызова, приведу пример, где функция изначально существует только как метод объекта:

var title = "Global Title";
var exampleObject = {
"title": "Example Title",
"showTitle": function(){
alert(this.title);
}
};
var showTitle = exampleObject.showTitle; // вначале забираем ссылку на функцию
showTitle(); // а тут вызываем функцию без ссылки на объект


В результате выполнения выведется сообщение "Global Title", означающее, что во время вызова функции this указывает на глобальный объект window, а не на объект exampleObject. Это происходит из-за того, что в строке var showTitle = exampleObject.showTitle мы получаем ссылку только функцию, и при вызове showTitle() нет ссылки на исходный объект exampleObject, отчего this начинает ссылаться на объект window.

Упрощая: если функция вызвана как свойство объекта, то this будет ссылаться на этот объект. Если вызывающего объекта нет, this будет ссылаться на глобальный объект window.

Пример частой ошибки:

var exampleObject = {
"title": "Example Title",
"showTitle": function(){
alert(this.title);
}
};
jQuery('#exampleDiv').click(exampleObject.showTitle);


При клике на DIV с id "exampleDiv" вместо ожидаемого "Example Title", выведется пустая строка или значение атрибута «title» DIV-а. Это происходит из-за того, что на событие клика мы отдаём функцию, но не отдаём объект; и, в итоге, функция запускается без привязки к исходному объекту exampleObject (для удобства, jQuery запускает функции-обработчики в контексте самого элемента, что и приводит к подобному результату). Чтобы запустить функцию, привязанную к объекту, нужно вызывать функцию с ссылкой на объект:

jQuery('#exampleDiv').click(function(){
exampleObject.showTitle();
});


Чтобы избежать подобного «неуклюжего» объявления, средствами самого JavaScript можно привязать функцию к контексту с помощью bind:

jQuery('#exampleDiv').click(exampleObject.showTitle.bind(exampleObject));


Однако всеми любимый ИЕ до 9 версии не поддерживает данную возможность. Поэтому большинство JS библиотек самостоятельно реализуют данную возможность тем или иным способом. Например, в jQuery это proxy:

jQuery('#exampleDiv').click(jQuery.proxy(exampleObject.showTitle, exampleObject));
// или так:
jQuery('#exampleDiv').click(jQuery.proxy(exampleObject, "showTitle"));


Суть подхода довольна проста — при вызове jQuery.proxy возвращается анонимная функция, которая с помощью замыканий вызывает исходную функцию в контексте переданного объекта.

По факту, JavaScript может запустить любую функцию в любом контексте. И даже не придётся присваивать функцию объекту. Для этого в JavaScript-е предусмотрено два способа — apply и call:

function showTitle(){
alert(this.title);
}

var objectA = {
"title": "Title A",
};

var objectB = {
"title": "Title B",
};

showTitle.apply(objectA);
showTitle.call(objectA);


Без использования параметров функции, обе работают одинаково — функции apply и call отличаются лишь способом передачи параметров при вызове:

function example(A, B, C){
/* code */
}
example.apply(context, [A, B, C]);
example.call(context, A, B, C);


Более развернутую информацию по затронутым темам можно прочитать тут:
Замыкания
Замыкания в JavaScript
Замыкания и объекты JavaScript. Переизобретаем интерпретатор
Тонкости ECMA-262-3. Часть 4. Цепь областей видимости.
«Классы» в JavaScript
Прототипы JavaScript — программистам на C/C++/C#/Java
Работа с объектами в JavaScript: теория и практика
Контекст this в деталях
Привязка функции к объекту и карринг: «bind/bindLate»
Ключевое слово this в javascript — учимся определять контекст на практике
JavaScript, замыкания, прототипы, контекст, прокси
+70

63383

781


Хабаров Юрий @Gromo карма37,0 рейтинг0,0
Похожие публикации

Метапрограммирование (с примерами на JavaScript) (23)
Javascript без this (26)
Ключевое слово this в javascript — учимся определять контекст на практике (121)
Извлечение ссылки на объект из замыкания (19)
Javascript: ООП, прототипы, замыкания, «класс» Timer.js (51)
Комментарии (85) отслеживать новые: в почте в трекере

+5 stardust_kid 8 мая 2013 в 00:03#
Большое спасибо за статью. Небольшой совет: console.log() намного удобнее чем alert для отладки.
+1 Gromo 8 мая 2013 в 00:49#↵↑
Надо же, кто-то ещё не спит :)

Рад, что кому-то статья пригодится. А алерт — это для наглядности. Большинство примеров рассчитаны на то, что их даже не придётся вбивать в браузер — их поведение и так понятно. Да и для начинающих, имхо, легче понять с алертом, чем с логом в консоли.
+1 stardust_kid 8 мая 2013 в 01:07#↵↑
Меня алерт бесил с самого начала, когда показали console.log был очень рад.
+2 k12th 8 мая 2013 в 01:23#↵↑
Особенно удобно дебажить console.log'ом в IE6-7.
+5 stardust_kid 8 мая 2013 в 01:51#↵↑
Месье знает толк в извращениях.
+1 zorgzerg 8 мая 2013 в 21:20#↵↑
Так ведь сарказм, нет? ))
–1 alist 8 мая 2013 в 23:52#↵↑
А алерт — это для наглядности.

Извините, но у меня в JavaScript алерта нет — интерпретатор ругается:

$ node
> alert('Hi')
ReferenceError: alert is not defined
at repl:1:2
at REPLServer.self.eval (repl.js:110:21)
at Interface.<anonymous> (repl.js:239:12)
at Interface.EventEmitter.emit (events.js:95:17)
at Interface._onLine (readline.js:202:10)
at Interface._line (readline.js:531:8)
at Interface._ttyWrite (readline.js:754:14)
at ReadStream.onkeypress (readline.js:99:10)
at ReadStream.EventEmitter.emit (events.js:98:17)
at emitKey (readline.js:1089:12)
>
0 k12th 8 мая 2013 в 00:48#
Из-за слабой типизации, почти все данные в JavaScript-е являются объектами.
Вот это сурово!
Например, в Haskell нет объектов (в ООП-понимании этого слова), у него какая типизация?
В Java почти все, за исключением int, byte и boolean является объектом (могу быть не точен, поправьте) — потомком, если не ошибаюсь, object — какая в ней типизация?
0 k12th 8 мая 2013 в 00:49#↵↑
А вообще статья неплохая, полезная.
–4 Gromo 8 мая 2013 в 01:03 (комментарий был изменён)#↵↑
В данном случае я имею в виду, что в отличие от других языков программировния, в JavaScript объектами являются не только сами объекты, но и строки, массивы и функции (насколько я знаком с другими языками — в них не так). Само понятие типизации — определение типа данных, коих может быть довольно много.

JavaScript же со своей стороны, может осуществлять приведение типов для сравнения или использования, что делает его слаботипизированным. В выражении var t = 5; переменная t в зависимости от способа использования может быть трактована как булев тип, как строка, как число, а при необходимости, выступить и как объект. Вот это я имел в виду, говоря о слабой типизации. Если не прав, поправьте.
+7 rock 8 мая 2013 в 01:18#↵↑
В js 5 типов примитивов, объектами они не являются. Null и undefined к объектам не приводятся. При обращении к свойствам строк, чисел и булевых, они оборачивается во временный объект, который, после операций над ним, удаляется. Не вводите людей в заблуждение, а то потом путают, что передается по ссылке, а что по значению.
0 k12th 8 мая 2013 в 01:24#↵↑
а какой пятый?
+3 rock 8 мая 2013 в 01:29#↵↑
Number, string, boolean, null, undefined.
0 k12th 8 мая 2013 в 01:32#↵↑
Ах да, string. Передается по ссылке, но является immutable, и посему причисляется к примитивам. Спасибо!
0 Verkholantsev 8 мая 2013 в 10:45#↵↑
Можете подробнее на примере пояснить про «оборачивание в объект»? Или дайте, пожалуйста, ссылку.
+13 rock 8 мая 2013 в 11:06#↵↑
// строка-примитив
var str1='string';
console.log(typeof str1); // string
// строка-объект
var str2=new String('string');
console.log(typeof str2); // object
// создаем временный объект, аналогичный str2, его свойству q присваиваем 1
str1.q=1;
// тут продолжаем работать с примитивом - у него нет этого свойства
console.log(str1.q); // undefined
// str2 - объект, соответственно никакая обертка не создается и работаем с его свойствами
str2.q=1;
console.log(str2.q); // 1

String.prototype.met=function(){console.log(typeof(this))}
console.log(typeof 'string'); // string
// внутри методов контекстом будет этот временный объект
'string'.met(); // object
0 Verkholantsev 8 мая 2013 в 11:33#↵↑
Спасибо
0 k12th 8 мая 2013 в 01:22#↵↑
Не могу так сходу вспомнить ни одного ООП-языка, в котором функции, массивы и строки не являлись бы объектами (ну, кроме PHP (и отчасти Ruby — методы не являются там объектами, только блоки)). То, что некоторые сущности автоматически боксятся в объекты говорит не о слабой типизации, а о… не знаю, автоматическом боксинге/анбоксинге?)

Моя мысль была в том, что степень того, насколько язык пронизан идеями ООП (являются ли все встроенные типы объектами, например) и степень «силы» его типизации не связаны никак. Да, JS слабо-типизированный язык; и да, в нем функции являются объектами.
В контекста вашей статьи вторая часть высказывания — важная и полезная мысль, но слабая типизация тут не при чем.
+4 HhyperR 8 мая 2013 в 02:07 (комментарий был изменён)#↵↑
В Ruby методы являются объектами. Просто obj.f — вызов метода, а obj.method(:f) — объект.
0 k12th 8 мая 2013 в 02:08#↵↑
А, вот как. Спасибо.
0 alexac 8 мая 2013 в 03:03#↵↑
В C++ функции — это не объекты. По факту, там даже методы (в том числе виртуальные) — обычные функции, но со скрытым параметром this.
0 k12th 8 мая 2013 в 03:15#↵↑
Не силен в этом семействе, к своему стыду… но мне казалось, что в C или С++ можно передать ссылку на функцию, нет?
+3 alexac 8 мая 2013 в 03:38 (комментарий был изменён)#↵↑
В C и C++ можно было взять указатель на функцию, это не делает функцию объектом, с одинаковым успехом можно написать:

int f() {
return 123;
}

/* будет работать */

int (*ptr)() = f;
(*ptr)();

/* допустимо, но никто не гарантирует, что это будет работать на конкретной машине */

int (*ptr)() = (int (*)()) 0xfe017c24h;
(*ptr)();


В C++11 появились лямбды.

bool c = true;
std::function<bool(int, int)> f = [&] (int a, int b) -> bool {
return c ? a < b : a > b;
}

/* ... */

c = true;
f(5, 3); // false
c = false;
f(5, 3); // true


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

struct __my_functor{
bool& c;
__my_functor(bool &c): c(c) {}
bool operator() (int a, int b) { return c ? a < b : a > b; }
};

/* ... */

bool c = true;
__my_functor f(c);

/* ... */
c = true;
f(5, 3); // false
c = false;
f(5, 3); // true


То есть в C++, благодаря перегрузке операторов, объект может прикинуться функцией, но функция не является объектом.
0 k12th 8 мая 2013 в 03:52#↵↑
Спасибо, узнал новое.

Я к тому, что в java нельзя сделать даже близкого, максимум, что можно сделать — создавать анонимный инстанс анонимного класса.
0 alexac 8 мая 2013 в 04:05#↵↑
На самом деле, после C++ во многих языках не хватает переопределения операторов — может пригодиться как в простых случаях вроде операций с матрицами, так и вот в подобной магии с функторами (одна из вещей, что раздражают меня в java — нельзя создать простой callback, приходится городить анонимные классы с именованными методами).
0 alexac 8 мая 2013 в 04:11#↵↑
Кстати, с массивами в C++ не все так просто. С одной стороны, это просто последовательно расположенные в памяти данные, как это было в C, с другой, если точно известен размер массива в данном контексте, то доступны итераторы по этому массиву (по крайней мере в C++11).

void f(int [] array, size_t size);
/* ... */
int array[5];

/* тут доступны begin() и end() */
for (int a : array) {
std::cout << array << std::endl;
}

f(array, 5);

/* ... */
void f(int [] array, size_t size) {
/* а тут уже нет */
for (int i = 0; i < size; ++i) {
std::cout << array[i] << std::endl;
}
}
0 alexac 8 мая 2013 в 04:15#↵↑
Вру. в C++11 появился шаблонный класс array, который не хранит размер массива, но использует значение аргумента шаблона.
+2 megalol 8 мая 2013 в 12:46#↵↑
Код с явно записанным функтором функтором эквивалентен
auto f = [&] (int a, int b) -> bool { return c ? a < b : a > b;
, а не
std::function<bool(int, int)> f = [&] (int a, int b) -> bool
Если появляется std::function, возникает дополнительный полиморфизм, который стоит производительности (лишний malloc+косвенность для захвата замыкания вместо простой структуры на стеке с auto/вручную записанным функтором)
0 Gorthauer87 8 мая 2013 в 13:33#↵↑
Функтор это функциональный объект или же прямой аналог функции в js. С ними тоже можно что угодно делать. даже складывать, если оператор опишешь.
0 alisey 8 мая 2013 в 20:24#↵↑
У функтора несколько разных пониманий. Функторы в C++, Prolog и Haskell — совершенно разные вещи.

Отличие функциональных объектов в JS от C++ в том, что в C++ объект может вести себя как функция, если определен operator(), а в JS любая определенная пользователем функция является объектом.
0 Gorthauer87 8 мая 2013 в 22:47#↵↑
Требования производительности и совместимости с Си, функциональные объекты не так то просто заинлайнить.
0 Gorthauer87 8 мая 2013 в 13:36#↵↑
А чем обьект, прикинувшийся функцией, отличается от функции в понимании js? Правильнее называть это дело не функторами, а функциональными объектами.
0 k12th 8 мая 2013 в 03:26#↵↑
Да, про C++ я не подумал. Исключительный случай — низкоуровневый ООП-язык. Конечно, объектами в нем являются только инстансы классов.
+1 Ashot 8 мая 2013 в 01:35 (комментарий был изменён)#↵↑
Строки, массивы и т.д. являются объектами далеко не только в JS, как верно заметили выше.
может осуществлять приведение типов для сравнения или использования

как мне кажется немного не точная формулировка: JS делает слаботипизированным то, что он может производить приведение типов неявно. например мы можем сделать так:
var a = 5;
if (a) {
// do something
}

тут при проверке условия происходит неявное приведение переменной a к булеву (если a == 0 или undefined или null то вернётся false). В строготипизированных языках такой фокус не прокатит: оператору if должно бередаваться только булево значание

Вот ещё пример слабой типизации:
var a = 5;
a = 'String'

мы не задаём явного типа переменной a, и как следствие можем в любой момент присвоить ей значение люого типа.

а ваше утверждение, что если всё являтся объектами, то язык слаботипизирован, является в корне неверным
0 k12th 8 мая 2013 в 02:12#↵↑
Мне не нравится, что мы тут все трактуем сильную и слабую типизацию (ключевое слово «трактуем»), потому что есть вполне определенные термины.

Но последняя фраза вашего комента — это тютелька в тютельку то, что я хотел выразить изначально:)
0 k12th 8 мая 2013 в 02:19#↵↑
Неявное приведение типов, кстати говоря, есть и в Haskell — можно сложить вещественное и целое, например (с точки зрения Haskell это разные типы). Но это строго типизированный язык, просто операция (функция, вообще говоря) сложения определена и для пары R+N и для N+R и т.д. Тем не менее, не получится просто так передать в функцию, которая принимает строку, целое число (нужно определить функцию с таким же именем, но с другими аргумантами).

В данном контексте слабая типизация JS — это возможность в рантайме поменять тип значения переменной.
0 Ashot 8 мая 2013 в 02:33 (комментарий был изменён)#↵↑
я не являюсь знатоком большого числа языков программирования, к сожалению, и про такой нюанс в Haskel`е не знал(хотя чего реха таить, я с ним и не знаком. ну разве что название знаю) Если честно, вообще как-то не задумывался что в строготипизированном языке могут быть неявные приведения, так что спасибо, что развеяли моё заблуждение
0 k12th 8 мая 2013 в 02:35#↵↑
Я тоже не особо знаток, просто полистал «Learn you a Haskell for a great good». Интересно же, на чем люди пишут и почему:)
0 mayorovp 8 мая 2013 в 12:30 (комментарий был изменён)#↵↑
Стоп, в Haskel же нет перегрузки функций! Потому и значения разных типов складывать нельзя, никак.

Возможно, вас обмануло наличие полиморфных констант в Haskell — число «5» может быть, в зависимости от контекста, целым, вещественным, дробным или даже комплексным. Но стоит зафиксировать его тип — и сложение не сработает.

2 + 2.0 -- Сложение двух вещественных чисел, результат = 4.0
(2 :: Integer) + 2.0 -- Ошибка компиляции, функция (+) требует одинакового типа операндов

let a = 2 -- Именованная константа a имеет тип Integer
a + 2.0 -- И снова ошибка компиляции

let b :: Num x => x; b = 2 -- Именованная константа b является полиморфной
b + 2.0 -- Работает, результат = 4.0

let c = 2 in c + 2.0 -- Механизм вывода типа справился, с имеет тип Double, результат 4.0
0 megalol 8 мая 2013 в 12:56#↵↑
Стоп, в Haskel же нет перегрузки функций! Потому и значения разных типов складывать нельзя, никак.

Можно, никто не запрещает спрятать прелюдию и определить свой плюс на multiparam тайпклассе.
0 mayorovp 8 мая 2013 в 12:58 (комментарий был изменён)#↵↑
Но это будет уже другой плюс, тут же обсуждалась встроенная функция.

PS вы меня покритиковали только затем, чтобы написать ниже ровным счетом то же самое?
а дальше вывод типов поработает и приведет все к одному типу, на котором определен + (который всегда принимает 2 одинаковых типа, он не может быть определен для разных типов никак)
0 megalol 8 мая 2013 в 17:54#↵↑
Плюс не является встроенным, это часть стандартной библиотеки, точнее — тайпкласса Num в модуле Prelude. Язык не запрещает не подключать Prelude к модулю.
Встроенными примитивами являются функции, которые используются в инстансах Num для Int, Integer и прочих (ctrl-f instance Num Int в www.haskell.org/ghc/docs/6.12.2/html/libraries/base-4.2.0.1/src/GHC-Num.html)

Я не критиковал, а уточнил. У себя не стал это писать, чтобы не дублировать коммент.
0 mayorovp 8 мая 2013 в 17:58#↵↑
Стандартная библиотека — часть языка.

PS http://habrahabr.ru/post/178133/#comment_6217079 — добавил для симметрии
0 k12th 8 мая 2013 в 14:34#↵↑
Честно считал, что перегрузка функций там есть. С чем-то перепутал.
Спасибо.
+1 megalol 8 мая 2013 в 12:53#↵↑
Не определено там это.
В хаскеле литерал «5» является синтаксическим сахаром для «fromInteger 5», а «5.0» — «fromRational 5.0». Отсюда они имеют тип:
fromInteger 5 :: (Num a) => a
fromRational 5.0 :: (Fractional a) => a

То есть «5 + 5.0» будет на самом деле «fromInteger 5 + fromRational 5.0», а дальше вывод типов поработает и приведет все к одному типу, на котором определен + (который всегда принимает 2 одинаковых типа, он не может быть определен для разных типов никак)
0 k12th 8 мая 2013 в 14:35#↵↑
Ясно, спасибо.
0 mayorovp 8 мая 2013 в 17:57 (комментарий был изменён)#↵↑
а дальше вывод типов поработает и приведет все к одному типу, на котором определен + (который всегда принимает 2 одинаковых типа, он не может быть определен для разных типов никак)
Почему никак? Никто не запрещает спрятать прелюдию и определить свой плюс на multiparam тайпклассе.
+3 kibizoidus 8 мая 2013 в 03:55#↵↑
Пожалуй, оставлю это здесь.
0 k12th 8 мая 2013 в 03:59#↵↑
Да, сей прекрасный, хоть и донельзя избитый ролик отлично иллюстрирует.
0 Gromo 8 мая 2013 в 06:56#↵↑
Классный ролик, спасибо за ссылку.
+2 ghosthope 8 мая 2013 в 11:02#
Я долго осознавал, что такое замыкание в JS. Мне долго пытались это вдолбить разные статьи, книги, советы. Пока один хороший человек не ткнул меня в такое понятие как Scope. И только осознав, что такое scope, до меня дошло, что же такое замыкание. Почему здесь про это нет ни слова?
–2 Gromo 8 мая 2013 в 11:31#↵↑
Цель данной статьи не показать внутренние механизмы работы, а попытаться «на пальцах» объяснить как это работает. Тем более, что про Scope отлично описывается в статьях, чьи ссылки находится внизу топика; если быть точнее — в статье по первой же ссылке.
0 subzey 8 мая 2013 в 11:37#↵↑
Аналогично. Когда я узнал, что scope — это всего-навсего объект, а переменные «внутри» него — это его свойства, оказалось, что в прототипах и в скопах используется по сути один и тот же механизм, делегация.

object → [[proto]] → [[proto]] → [[proto]] → null
[[scope]] → [[scope]] → [[scope]] → window → null

А «магическая» конструкция with() всего-то навсего добавляет в эту цепь произвольный объект. И тогда js стал для меня из непредсказуемой штуки красивым и изящным языком.
0 mayorovp 8 мая 2013 в 12:37#↵↑
Ну, у прототипов и вложенных областей видимости есть принципиальное различие — операция присваивания значения свойству никогда не изменит значение свойства, найденного в прототипе. В то же время операция присваивания значения переменной всегда меняет ту переменную, где она была найдена.

И вторая неточность: вместо window лучше бы написать [[global]], поскольку глобальный объект не обязан быть окном. Особенно заметен данный факт, если делать расширения для браузеров.
0 jalners 8 мая 2013 в 13:16#↵↑
Подразумевалось, что при разрешении имен идентификаторов в обоих случаях используется делегация (или проще — обход цепочек [[scope]] или [[proto]]). А про различие правильно подмечено.
0 subzey 8 мая 2013 в 13:38#↵↑
Уточнения хорошие. Да, согласен, разница между scope chain и proto chain есть. Но не такая уж она прям и принципиальная.

А насчет [[global]] — незачем новичков пугать лишний раз. Я надеюсь, что разработчики расширений для браузеров или скриптов на node уже знают js достаточно хорошо, и им мой коммент вообще без надобности.

Главное, имхо, уточнениями не запутать все окончательно, а то ещё выйдет, что и «операция присваивания значения свойству никогда не изменит значение свойства, найденного в прототипе» — не совем-то и правда.
Скрытый текст
0 jalners 8 мая 2013 в 12:06#↵↑
Я так же считаю, что только благодаря пониманию Scope можно понять замыкания. По сути, замыкания и есть сама функция и это свойство Scope. Очень прекрасно все это описано на сайте Дмитрия Сошникова. Добавьте эту ссылку в топик.
А статья, в целом, хорошая.
0 Arks 8 мая 2013 в 11:27#
почти все данные в JavaScript-е являются объектами, или могут быть использованы как объекты
лучше было бы так: «почти все данные в JavaScript-е являются голыми бабами, или могут быть использованы как голые бабы.» Эх, не было фантазии у разработчиков ECMA 262((
0 Gromo 8 мая 2013 в 11:38#↵↑
Это скорее претензия ко мне, как к автору, в том, что не могу правильно сформулировать мысль — пытался сформулировать по разному много раз; то, что получилось, и есть результат.

А про голых баб занятно. Только, думаю, что эту статью будут читать не только парни — думаю, они бы проголосовали не за «баб», а за «мужиков». И чтобы не выбирать между двух зол, пусть останутся объекты.
0 Arks 8 мая 2013 в 11:42#↵↑
ну давай тогда будут объекты-мужики(объекты, массивы) и объекты-бабы(функции). А голые они потому что у них все свойства паблик!
+1 Mithgol 8 мая 2013 в 12:44 (комментарий был изменён)#↵↑
Тому, кто считает, что почти все данные в джаваскриптах могут быть использованы как голые бабы, наверняка необходима помощь квалифицированного сексопатолога — да поскорее, пока не случилось непоправимое.
0 zxcabs 8 мая 2013 в 13:16#
В данном примере переменная title была объявлена дважды – первый раз глобально, а второй раз – внутри функции. Благодара тому, что внутри функции example переменная title была объявлена с помощью ключевого слова var, она становится локальной и никак не связана с переменной title, объявленной до функции. В результате выполнения кода вначале выведется «internal» (внутренняя переменная), а затем «external» (глобальная переменная).


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

Пример:
var o = { x: 1 };

function addY(obj) {
obj.y = 2;
}

function addZ(obj) {
//повторно объявим obj
var obj;
obj.z = 3;
}

addY(o);
console.log(o); // => { x: 1, y: 2 }

addZ(o);
console.log(o); // => { x: 1, y: 2, z: 3 }

0 Gromo 8 мая 2013 в 13:45#↵↑
Полезное уточнение. Добавил как скрытый текст.
–3 termi 8 мая 2013 в 14:23#
Хорошая статья для начинающих, но печалит, что ничего не сказано про Strict Mode и let.
Например:
> Второе — динамическое объявление функций, использующих замыкания.
можно переписать так (работает в FF и Chrome):

void function() {"use strict";

for(let counter=1; counter <=10; counter++){
let _counter = counter;
$('<div>').css({
//...
}).html('<h1>'+ counter +'</h1>')
.appendTo('body')
.click(function(){
alert(_counter);
});
}

}.call(null)


строку let _counter = counter; можно было бы опустить, если бы браузеры релизовали let по спецификации
+1 zxcabs 8 мая 2013 в 14:27#↵↑
в моем хроме let не работает, да и это только будущее языка.
0 termi 8 мая 2013 в 16:36#↵↑
Во первых, let можно использовать уже сейчас
Во вторых, javascript это уже давно не только язык интернет страниц. Если вы пишете код под Node.js или FireFox OS, то можете использовать конструкцию let без препроцессоров и это существенно упростит/ускорит ваш код.
+1 rock 8 мая 2013 в 16:44#↵↑
И вы уверены, что это должны знать те, кто еще не знает что такое замыкание или прототип?) А по поводу this в strict mode — соглашусь.
0 termi 8 мая 2013 в 17:07#↵↑
У меня товарищ-робототехник начал изучение javascript именно с Node.js, для написания сервера управления Arduino.

И моё личное мнение: изучать сейчас javascript без (хотя бы поверхностного) ознакомления с Strict Mode и let, такая же ошибка, как начать изучать javascript с jQuery.
0 rock 8 мая 2013 в 17:27#↵↑
Вы и ваш товарищ молодцы, и что дальше?:) И я javascriptECMAScript учил на ноде и по спецификации. И в текущей принятой версии спецификации ни слова о let и const. Да, это нужно знать, это нужно учить на будущее — но уже после того, как выучил js на базовом уровне — уже явно после того, как разобрался с тем, что написано в статье. А вот strict mode в текущей версии спецификации присутствует, но к теме статьи относится только одно — при вызове функции не в контексте объекта, контекст не переопределяется на глобальный объект.
0 Gromo 8 мая 2013 в 14:46#↵↑
Вы абсолютно правильно поняли — статья ориентирована на начинающих. Поэтому я старался не выходить за пределы тем, упомянутых в начале топика. Да и это описание занимет более 10 страниц ворда, что довольно много для одной статьи.
0 zvorygin 8 мая 2013 в 15:21#
Мне кажется, что правильное решение будет таким:
for(var counter=1; counter <=10; counter ++){
$('<div>').css({
"border": "solid 1px blue",
"height": "50px",
"margin": "10px",
"text-align": "center",
"width": "100px"
}).html('<h1>'+ counter +'</h1>')
.appendTo('body')
.click(window.alert.bind(window, counter));
}
0 Gromo 8 мая 2013 в 15:54#↵↑
для того, чтобы просто вывести номер — пойдёт, т.к. суть та же — замыкание номера. Однако цель примера — не вывести алерт, а показать, что внутри функции можно получить текущий номер с помощью замыкания.

Ещё следует быть осторожным с bind-ом, т.к. он не не совсем кроссбраузерный.
0 termi 8 мая 2013 в 16:38#↵↑
Проблема кроссбраузерности bind'а (да и других функций из ES5) решается очень просто
0 arXangel 8 мая 2013 в 20:38#↵↑
Самая печаль, это то, что обычное замыкание работает намного быстрее чем .bind()
0 Silver_Clash 8 мая 2013 в 23:23#
Минусую топик только за
При попытке присвоить переменной значение, переменная будет создана в глобальной области видимости, и ей присвоится значение.


В конце статьи есть ссылки на статьи Дмитрия Сошникова. Жаль что вы не читали (читали невнимательно) вот эту. В ней наглядно объясняется разница между глобальной переменной и свойством глобального объекта. Эта разница на первый взгляд совершенно несущественна, но на самом деле весьма значительна.
0 Gromo 9 мая 2013 в 00:15#↵↑
Спасибо за объяснение причины минусования топика.
0 Silver_Clash 9 мая 2013 в 21:49#↵↑
Не за что. Вы бы статейку прочитали хотя бы в части «объявления переменных без var» и поправили свою, что бы людей в заблуждение не вводить.
0 Gromo 9 мая 2013 в 23:16#↵↑
Прочитал до того, как ответить Вам на комментарий. Ваше замечание полезно, но выходит за рамки уровня целевой аудитории, для которого написана эта статья. К сожалению, многих вещей пришлось коснуться лишь поверхностно и не в подробностях, чтобы не слишком усложнять статью. Приводя ваше же высказывание:

Я думаю этот вопрос все же лучше рассмотреть отдельно, потому что если рассматривать их в одном топике, топик будет слишком сложным.
0 Silver_Clash10 мая 2013 в 12:31#↵↑
Тогда хотя бы исправьте фразу, которая может ввести читателей в заблуждение.
0 Gromo10 мая 2013 в 15:33#↵↑
Добавил ниже спойлер с сылками, где объясняется разница в объявлении глобальной переменной от присвоения значения несуществующей. Саму фразу исправлять не стал, т.к. имхо, разница не столь уж значительная.
0 Quilin 9 мая 2013 в 04:16#
Мне кажется, статьи, в которых рассказывается про прототипирование, контекст и замыкания в JS, появляются на хабре примерно раз в два месяца. И там обязательно одни и те же комментарии. Например, подобные моему. Сансара.
0 webhamster11 мая 2013 в 00:00#
Я не понял. Вы пишите:

Если убрать ключевое слово var из строки var title = 'internal', то запустив код, в результате дважды получим сообщение «internal». Это происходит из-за того, что при вызове функции мы объявляли не локальную переменную title, а перезаписывали значение глобальной переменной!


Потом далее приводите пример:

var title = «external title»;
function example(){
title = «changing external title»;
alert(title);

example();

Если закомментировать строку title = «changing external title»;, то при первым сгенерированным сообщением станет «undefined» — локальная переменная title уже была инициализирована (существует), но значение (на момент вызова alert-а) не было присвоено.


Вопрос. Почему в этом примере переменная title толи не вида внутри функции, то ли еще не проинициализирована, хотя это делается в первой же строке программы?
0 Silver_Clash11 мая 2013 в 01:08#↵↑
В этом фрагменте автор имел ввиду что функция example принимает аргумент title (то есть функция должна быть объявлена как function example(title)). Это становится очевидно, если посмотреть предыдущий фрагмент кода. Если аргументы вернуть то все становится на свои места — функция была вызвана без аргументов, поэтому и

первым сгенерированным сообщением станет «undefined»
+1 Gromo11 мая 2013 в 12:32 (комментарий был изменён)#↵↑
Во втором примере объясняется момент с инициализацией переменных внутри функции. Надеюсь, Вы уже уловили разницу между локальными и глобальными переменными.

Так вот, локальные переменные создаются не тогда, когда Вы их видите в коде, а в тот момент, когда создаётся функция. Т.е. даже если переменная будет объявлена в конце, она будет создана до того, как вызовется функция. А вот значение присваивается там, где Вы и видите в коде. Что и показывает второй пример.

Существует разница между «переменная не существует» и «переменная с пустым значением (undefined)»
+1 Gromo11 мая 2013 в 13:50#↵↑
Возможно более понятным будет показать на примере:

function example(){
alert(title);
var title = "internal title";
}


Из-за того, что все локальные переменные объявляются до вызова функции, то предыдущий код эквивалентен:

function example(){
var title;
alert(title);
title = "internal title";
}


отсюда и сообщение «undefined» — переменная есть, а вот значения — нет.
Изображение


Название раздела: Web на стороне клиента, Java Script
Описание: Наиболее перспективное направление развития компьютерных наук. Надо быть в теме!

Быстрый ответ


Введите код в точности так, как вы его видите. Регистр символов не имеет значения.
Код подтверждения
:) ;) :hihi: :P :hah: :haha: :angel: :( :st: :_( :cool: 8-| :beee: :ham: :rrr: :grr: :* :secret: :stupid: :music: Ещё смайлики…
   

Вернуться в «Web на стороне клиента, Java Script»

Кто сейчас на форуме (по активности за 15 минут)

Сейчас этот раздел просматривают: 4 гостя