Как стать автором
Обновить

Комментарии 43

>Из него я, например, узнал, что массивам можно задавать length
я об этом узнал из книжки «JavaScript на 100%» для чайников.
А Чак Норрис знал об этом еще за год до своего рождения. Только я не понимаю, как это относится к теме.
Тем что это «тонкости» описаны в любой вменяемой книги о JavaScript.
Ну, в спецификации описано вообще всё. Только глупо ожидать, что каждый прочитав ее будет помнить наизусть. Учебник хорош для знакомства с языком, а этот справочник хорош для заполнения пробелов. Конечно информация будет дублироваться, но здесь она представлена в сконденсированном виде.
Единственная проблема — ни в одной книге никто не выносил «тонкостей» в отдельное место, они всегда размазаны по книге. В итоге их довольно сложно искать.
Что изменение arguments вызывает изменение соотвествующих локальных переменных:

Может все же не локальных переменных, а параметров ф-ции?

(function(){
var a = 'a';
console.log(a); // => 'a'
arguments[0]='b';
console.log(a); // => 'a'
})();


Как-то оно не особо работает :)
Да, я имел в виду параметры (которые доступны через **соотвествующие** локальные переменные).
хм, интересно

firefox 3.5.16


chrome 11.0.686.3
Ого, интересно. В Firefox 4 ведет себя как в Хроме.
возможно это был глюк фаербага, я его замучал. в новой вкладке так
наверняка пока тестил, успел создать переменную name
> Что вложенные функции выполняются в контексте глобального объекта
Почитайте уже, наконец, как работают замыкания и анонимные функции.
Причем здесь анонимные функции и замыкания? Именованные функции работают точно так же, а замыкания здесь и близко нет, так как this не является локальной переменной.
>Что вложенные функции выполняются в контексте глобального объекта

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

Напишите так и будет как Вы хотите:
var name;
new function Cat() {
this.name = 'barsik';
var obj = this;
(function() {
console.log(obj.name); // => barsik
})();
}
объект window разве не одно и тоже что глобальный контекст?
Если функция может доступиться к глобальному объекту, то это не означает, что она выполняется в глобальном контексте. Вложенные функции выполняются в контексте того, где они были определены. И если во вложенной функции было обращение к локальному объекту обрамляющей функции, то ссылка на этот объект сохранится(замкнется) во вложенной. А this не имеет никакого отношения к вложености. this — это ключевое слово, которое указывает на того, кто вызвал функцию.

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

var name = 123;
new function Cat() {
this.name = 'barsik';
var f = function() {
console.log( this.name);
};
this.f = f;

f(); //123
new f(); //undefined
this.f(); //barsik
}


Вот еще один пример.
var name = "global context";
function test() {
var name = "local context";
(function () {
console.log(name);
})()
}
test() // "local context"
То, о чём вы говорите принято называть «scope» а не «context» и, то, на что ссылается «this» тоже можно называть «контекстом» выполнения.
Более того, вы — неправы, говоря, что this «во вложенной функции — это объект window.» Потому что это не всегда так. Это именно «глобальный объект», который в браузерах равен window.
(function () {
	console.log('this', this);
})();

shock@localhost:~> node scope.js
this [object global]
>Более того, вы — неправы, говоря, что this «во вложенной функции — это объект window.» Потому что это не всегда так.

Я так и сказал, что это не всегда так: «Вот есть одна вложенная функция, но поскольку она вызвается по разному, то она выдает 3 разных результата.» Даже пример привел:
f(); //123
new f(); //undefined
this.f(); //barsik

Это как раз и связанно с тем, что this лишь в одном случае ссылается на window.

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

В ES5 Strict глобальный объект нельзя получить таким образом, this будет undefined
window — это частный случай. JS есть не только в браузерах.
Наверное «контекст» здесь неподходящее слово, потому что намекает на «execution context», о котором здесь речь не идет. Поменяю на вариант в терминах this.
Функции выполняются в том контексте, в котором их вызвали. Любая ф-ия, вызванная напрямую, будет выполнена в контексте глобального объекта.
Одни под контекстом подразумевают значение this внутри функции, другие execution context, отсюда возникает недопонимание и народ начинает друг друга поправлять.

Мне здесь уже пара человек отсылала читать руководство по JavaScript для чайников. При том, что я написал на нем полнофункциональный Jabber-клиент с кастомной системой презенсов, который держит ростер в 5000 контактов и сейчас крутится на сайте с миллионами пользователей.
Ух ты, а не кинете ссылку?
Кинул в личку, сайт просто коммерческий.
Это просто с трудом вяжется с фразой —
>> Из него я, например, узнал, что массивам можно задавать length:
Не понимаю, почему это не вяжется. Фича с довольно ограниченной практической ценностью, которая в других языках не работает. У каждого свой набор вещей, которые он считает очевидными. Вот у вас в статье преобразование десятичного числа в двоичное делается так:

var byt = num.toString(2);
if (byt.length != 8) {
byt = addByte(byt)
}

function addByte(byt) {
while (8 != byt.length) {
byt = '0' + byt;
}
return byt;
}


Мне очевидно, что делать нужно так:
byt = (512 | num).toString(2).substr(1)

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

byt = (512 | num).toString(2).substr(1) — намного лучше моего костыля. Побитовые операции редко используются в JS и написано о них мало.
Вот лучше про это статью написали :) Ну Имхо конечно.
Их применять практические негде, да и в JavaScript нет особых побитовых операций — они везде одинаковые, соответственно и применение. Что вспомнил:
1. Проверка на четность (num & 1)
2. Быстрое отбрасывание дробной части — замена Math.floor (~~num или num >> 0), как следствие быстрый Math.round
3. Быстрое целочисленное деление/умножение на 2 4 8 16 итд (X << N, X >> N)
4. Короткое сравнение через xor e.keyCode^27 || e.preventDefault(); вместо e.keyCode === 27 && e.preventDefault(); и конструкций с if
5. Битовые маски которые практически не применяются в JavaScript
6. xor-шифрование
Хотел бы заметить, что пункты 2 и 3 не «быстрые», а «короткие» и в JavaScript такая «оптимизация» почти не имеет смысла:
var MAX = 10000000, i;

console.time('~~');
	for (i = 0; i < MAX; i += 1.1) ~~i;
console.timeEnd('~~');

console.time('floor');
	for (i = 0; i < MAX; i += 1.1) Math.floor(i);
console.timeEnd('floor');


shock@localhost:~> node speed.js
~~: 222ms
floor: 198ms
shock@localhost:~> node speed.js
~~: 216ms
floor: 197ms
shock@localhost:~> node speed.js
~~: 218ms
floor: 194ms


Та даже если бы и была незначительная разница, то она не стоит той потери читабельности, имхо. Лучше писать так, чтобы даже тот, кто не знает таких интересных особенностей смог прочесть код)
Не знаю, как получились такие результаты (node -v ?). Во всех браузерах ~~ быстрее либо значительно быстрее Math.floor. Думаю, что v8 закэшировал/оптимизнул.
Пруф:
Math.round vs hack jsperf.com/math-round-vs-hack/5
Math.floor vs Math.round vs parseInt vs Bitwise jsperf.com/math-floor-vs-math-round-vs-parseint/2
var MAX = 10000000, i, arr = [];

console.time('~~');
	for (i = 0; i < MAX; i += 1.1) arr.push(~~i);
console.timeEnd('~~');

console.time('floor');
	for (i = 0; i < MAX; i += 1.1) arr.push(Math.floor(i));
console.timeEnd('floor');

console.log(arr[~~(Math.random() * arr.length)])


shock@localhost:~> node speed.js
~~: 570ms
floor: 530ms
9444389
shock@localhost:~> node -v
v0.3.0


Ну всё-равно. Крайне редко математика становится причиной тормозов. И, как видим, результаты очень зависят от платформы. Потому, имхо, не стоит заниматься преждевременной оптимизацией в этом месте)
Если это короче и быстрее почему бы сразу не использовать? ;)
Ну типа, потому что хак, который не все знают) Вон люди не знали, что можно массив очищать, меняя длину, а уж такие особенности)
Ещё есть интересные моменты c typeof
// 1. typeof RegExp
// - WebKit'ы
typeof /[a-z]{2,4}/  // function RegExp, конечно имеет некоторое поведение функции, 
// например его можно вызвать как функцию, но WebKit'ы что-то перепутали
// - Остальные браузеры
typeof /[a-z]{2,4}/  // object

// 2. typeof DOM method
// - IE8-
typeof document.getElementById  // object - лол!.. однако этот объект имеет все методы function
// и адекватно отзывается на (document.getElementById + '') - 
// function getElementById() {
//    [native code]
// }
// - Остальные браузеры
typeof document.getElementById  // function

// 3. Бородатый боян
// - IE8-
'v'=='\v' // true ИЕ 8 не имеет символа вертикальной табуляции \v
// - Остальные браузеры
'v'=='\v' // false

Скажу, что большинство, что описано в JavaScript Garden это описание моментов стандарта ECMAScript 3 — прочитайте сами и все будете знать и понимать «почему так а не так», а все эти справочники это алхимия.
Нашел ссылку на этот «справочник» в последнем подкасте «Сделай мне красиво!» были интересны лишь баги браузеров (typeof RegExp порадовал).
Что this во вложенных функциях ссылается на глобальный объект
Не путаемся и других не путаем! Ссылка this зависит от способа вызова функции, а не от способа и места задания функции.

Общее правило запоминания для this (читать как мантру)
1. При прямом вызове функции (т.е. без catch, with, bind, call, apply) то, что слева от точки перед именем функции, то и будет this.
2. Если точки нет this === глобальный объект (для браузеров это window).
3. Если функция стоит в позиции выражения, то this будет всегда глобальный объект

Пример для закрепления:
var c = 0,b,a,e = {c: 8};
a = {
  c: 150,
  b: function() {
    return this.c;
  }
};

// 1. 
a.b(); // 150 this === a смотрим, то что перед точкой

// 2. 
b = a.b;
b(); // 0 this === window ибо точки нет 

// 3. 
(a.b)(); // 150 скибки не ставят функцию в состояние выражения поэтому смотрим то, что перед точкой
(false || a.b)(); // 0 позиция выражение
(0, a.b)(); // 0 позиция выражение
(e.b = a.b)(); // 0 позиция выражение
e.b(); // 8 this === e Если вызвать после присвиавния, то будет работать правило 1
(true ? a.b : a.c)(); // 150 не выражение
a[true ? 'b' : 'c'](); // 150 не выражение

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


Если внимательно прочесть, я и не утверждал обратного. Вообще, пример этот я привел не потому что не знаю как оно работает, или почему оно так работает, а просто потому что никогда раньше не обращал на это внимание. Пользовался и ладно.
Странно, что это вас удивило. Не хочу вас учить, просто решил на всякий случай уточнить момент с this потому, что фраза «Что this во вложенных функциях ссылается на глобальный объект» натолкнула меня на мысль о непонимании сути this (многие разработчики JavaScript не понимают this и действуют по аналогии с ПХПшным $this. b = a.b; b(); и this в эвентах — распространенные болезни).
Есть ещё интересный момент:
var a = new function () {
	console.log('a', this); // [object Object]
	this.foo = 'bar';	
};

console.log('value:', a.foo); // 'bar'

(function () {
	console.log('b', this); // [object global] || DOMWindow
})();
Ничего удивительного :) Просто записано в непривычном виде. Идентичный код:
var A = function () {
	console.log('a', this); // [object Object] при вызове конструктора через new this - пустой объект
	this.foo = 'bar';	
};
var a = new A;
console.log('value:', a.foo); // 'bar'

var B = function () {
	console.log('b', this); // [object global] || DOMWindow - правило 2
};
B();
<source>
Ну для меня не секрет, что Вам это не удивительно, но читатель то может путаться)
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации