Комментарии 93
Что насчет кроссбраузерности? Во всех IE корректно работает?
Думаю нет ) Это же ИЕ.
Во всех, только нужно помнить о больших числах.
Побитовые операторы появились задолго до IE, а таблицы неявного приведения типов существуют с «бородатых» лет первой спецификации. (9 Type Conversion)
~~null; // => 0
~~undefined; // => 0
~~0; // => 0
~~{}; // => 0
~~[]; // => 0
~~(1/0); // => 0
~~false; // => 0
~~true; // => 1
~~1.2543; // => 1
~~4.9; // => 4
~~(-2.999); // => -2
Важным достоинством «~~» является другой приоритет, который в ряде случаев экономит усилия по расстановке скобок:
Правда, зато «~~» приходится записывать не по порядку операций (наподобие того,как «(x / 1024)|0» означает «сперва поделить, затем отбросить дробную часть»), а спереди.
(А ещё «~~» ничего не означает в asm.js, но за пределами asm.js это не недостаток.)
( "-3.8"|0 ) === -3; // true
"-3.8"|0 === -3; // -3
~~"-3.8" === -3; // true
Правда, зато «~~» приходится записывать не по порядку операций (наподобие того,
(А ещё «~~» ничего не означает в asm.js, но за пределами asm.js это не недостаток.)
Я про это писал на Хабре ажно три года назад. Тогда не оценили :-)
Дык оттого и не оценили, что Вы сами тогда, находясь под влиянием замеров скорости работы джаваскриптов, принизили значение своего сообщения, высказали «не ведитесь на короткую крутую запись» и проч.
С тех пор скорости работы движков, JavaScript исполняющих, существенно изменились, причём в лучшую сторону, тогда как средняя скорость пробежки по клавиатуре босыми пальцами рук ничуть не переменилася.
Вывод: предпочитать следует такие приёмы, которые экономят труд программиста, даже если вычислительно они представляются несколько малоэффективными.
С тех пор скорости работы движков, JavaScript исполняющих, существенно изменились, причём в лучшую сторону, тогда как средняя скорость пробежки по клавиатуре босыми пальцами рук ничуть не переменилася.
Вывод: предпочитать следует такие приёмы, которые экономят труд программиста, даже если вычислительно они представляются несколько малоэффективными.
блин, век живи — век учись…
Я просто оставлю это здесь: Bitwise Operators
Стоит упомянуть про подводные камни с большими числами:
4242334634634634523523 | 0; // => 368574464
3234534534 | 0; // => -1060432762
Только помните:
~~"12345678901" // -539222987
"12345678901"|0 // -539222987
"12345678901"*1 // 12345678901
parseInt("12345678901", 10) // 12345678901
Хорошо, что Вы напомнили про это, но стоило бы ещё и упомянуть, почему это так. А так оно потому что согласно стандарту операнды битового оператора приводятся в 32-х битному знаковому числу, и, как следствие,
Результат является знаковым 32-битовым целым.
Кстати об использовании таких штук, по моему гораздо удобнее это писать перед выражением, то есть
Тогда сразу видно приведение типов.
var a = 0 ^ x
, b = 0 | x
Тогда сразу видно приведение типов.
ИМХО, стоит всё-таки знать меру в использовании особенностей таких конструкций в JS. Кстати говоря, я более, чем уверен, что у многих программистов на JS есть проблема с тем, чтобы разобраться в чужом JS-коде, в том числе из-за использования таких конструкций. Если вам нужно сократить объем кода, то в современных реалиях намного проще будет использовать тот же Google Closure Compiler и учитывать, как он работает, нежели писать такой «код» самому. Из моего опыта, понятность кода (даже на JS) намного важнее лишних пары килобайт, которые будут «выжаты» путем такой «оптимизации».
Код пишется для людей, а не для интерпретатора же. Нужно писать его так, чтобы его легко было прочитать. Разве что только код пишется специально для конкурса обфускаторов.
Даже в таком слабо типизированном языке с динамической типизацией как javascript в ваших программах вы всё равно знаете, откуда к вам приходят данные и какого типа. От этого уже можете плясать.
Даже в таком слабо типизированном языке с динамической типизацией как javascript в ваших программах вы всё равно знаете, откуда к вам приходят данные и какого типа. От этого уже можете плясать.
// Но иногда удобнее писать
if( ~str.indexOf('sub') ){
}
// чем
if( str.indexOf('sub') != -1 ){
}
А если вам нужно рядом инвертировать условие, и реализовать логику «нет в подстроке», то как вы будете писать?
if (!~str.indexOf('sub')) {
}
не?
вообще, если один раз запомнить, то такое легко читается.
Лично меня `!~` пугает. Я всё-таки выражения читаю по большей части слева-направо: «в строке индекс подстроки не равен -1»
Вы правы, я тоже никогда так не писал, а потом стал набирать много кода на jquery и приучился писать так:
согласитесь, тут больше смысла. хотя да, пугает иногда.
if (!~$.inArray(str, arr)) { }
согласитесь, тут больше смысла. хотя да, пугает иногда.
Вы используете библиотеку для поиска вхождения одной строки в другую?
Теперь я понимаю кто такой «программист на jquery»
Теперь я понимаю кто такой «программист на jquery»
Странный вопрос и выводы.
Я не использую библиотеку для поиска вхождений одной строки в другую. С чего вы взяли? В коде написан поиск строки в массиве. Но если вам интересно, то и так я уже давно не пишу. Просто добавляю к проекту SugarJS и использую стандартный indexOf(). Но раньше, когда моё бывшее начальство краснело от идеи изменения прототипов, мне приходилось для поиска внутри массива использовать jQuery, чтобы это везде работало. (до 9 версии IE её не поддерживал). А поиск строки в строке вроде очень давно во всех браузерах реализован, так что использовал его, ну уж точно не $.inArray(), акститесь.
Я не использую библиотеку для поиска вхождений одной строки в другую. С чего вы взяли? В коде написан поиск строки в массиве. Но если вам интересно, то и так я уже давно не пишу. Просто добавляю к проекту SugarJS и использую стандартный indexOf(). Но раньше, когда моё бывшее начальство краснело от идеи изменения прототипов, мне приходилось для поиска внутри массива использовать jQuery, чтобы это везде работало. (до 9 версии IE её не поддерживал). А поиск строки в строке вроде очень давно во всех браузерах реализован, так что использовал его, ну уж точно не $.inArray(), акститесь.
ясно, я неверно вас понял
да и не знал, что с indexOf для Array у ie была беда
да и не знал, что с indexOf для Array у ie была беда
Удобнее, но не читабельнее. Я вообще предпочту написать так:
if( str.contains('sub') ){
}
С 0 выйдет конфуз у вас
Согласен, но на участках с очень большим количеством итераций, лучше перестраховаться.
+ Это не кроссбраузерно, а ломать голову, расширен ли прототип на проекте не охота.
+ Это не кроссбраузерно, а ломать голову, расширен ли прототип на проекте не охота.
+ Это не кроссбраузерно, а ломать голову, расширен ли прототип на проекте не охота.
Как это не кроссбраузерно? А вы ломаете голову, подключён ли у вас jQuery, объявлена ли функция foo или bar при её вызове? Метод contains точно такой же, как любой другой. Или у вас есть этот метод или нету.
Согласен, но на участках с очень большим количеством итераций, лучше перестраховаться.
Этот аргумент вообще не понял. По-моему нету ничего более очевидного, чем слово
contains
— это самая лучшая перестраховка.Насчёт кросс-браузерности полезна библиотека Underscore.string и запись «_.str.include(str, 'sub')».
Chrome 25 — www.rubaxa.org/screenshot/6b15eb6a74e217165b52df651dd2.png
И да, когда я разрабатываю код, который будет работать в неизвестном мне месте, стараюсь писать так, чтобы понял и IE6. + Даже подключенный shim будет на 10 порядково медленней.
И да, когда я разрабатываю код, который будет работать в неизвестном мне месте, стараюсь писать так, чтобы понял и IE6. + Даже подключенный shim будет на 10 порядково медленней.
за
if (~arr.indexOf(val)) { /* ... */ }
на меня косо смотрят, а вот за такое наверное вообще убьют :)Вообще у этого «трюка» есть немного иное назначение, не только для приведения к int, но и замене Math.round — `(x + .5)|0` или Math.floor. С такой же целью это используется в ActionScript.
Пожалуйста, внимательно прочитывайте блогозапись до конца перед сочинением комментариев к ней. Вы пропустили характеристику «удобное средство отбрасывания дробной части».
Простите меня.
Охотно прощаю.
Но прошу заменить, в статье не написано, что `(x + .5)| 0` по скорости быстрей Math.round (если рассматривать синтетический тест).
Да, не написано, Вы правы.
А потом кто-то после вас застрянет на этом месте и будет думать: «а что же имел в виду автор?». Данная конструкция заметно ухудшает читабельность кода.
Указанный метод быстрее, но оба округления настолько быстры, что экономить стоит только в тех случаех когда у вас миллионы округлений в секунду.
P.S.
Указанный метод быстрее, но оба округления настолько быстры, что экономить стоит только в тех случаех когда у вас миллионы округлений в секунду.
P.S.
Спасибо за тесты.
В Файерфоксе они показывают одинаковые значения скорости того и другого округления.
В Файерфоксе они показывают одинаковые значения скорости того и другого округления.
Друзья, я просто пытаюсь показать, что данная практика имеет свои предпосылки, а не только из-за короткой записи. Писав маленькое приложение под iPhone где приходилось работать с пикселями (canvas) такие микро оптимизации помогали. Это всё было 3 года назад, надеюсь сейчас ситуация изменилась.
тест этот совсем не замеряет скорости округления, потому что loop invariant code motion выносит инвариант цикла за цикл.
замеряется по сути дела скорость кручения пустого цикла.
почитайте: mrale.ph/blog/2012/12/15/microbenchmarks-fairy-tale.html
замеряется по сути дела скорость кручения пустого цикла.
почитайте: mrale.ph/blog/2012/12/15/microbenchmarks-fairy-tale.html
Конструкция Math.floor() гораздо очевидней, чем |0. Это уже повод не использовать второй путь, даже без учета сообщений выше про проблемы с большими числами. И вообще, не зря битовые операции по умолчанию запрещены в jslint и jshint
До известной степени это впечатление справедливо.
Я замечу, однако, что программист до известной степени способен воздействовать на собственное впечатлениеот «|0» при помощи мнемотехники, то есть мысленно связать этот оператор с некоторым запоминающимся символом с той целью, чтобы надёжнейше обеспечить себе его очевидность, причём надолго.
На ум приходит, например, идея рассматривать вертикальную черту как острый и безжалостный нож гильотины, отсекающий последующую цифру — что метафорически символизирует отсечение всей дробной части числа. Такой символ очень неплохо передаёт всю суть происходящего — не хуже, чем английское слово «floor» передаёт суть названного им метода.
Я замечу, однако, что программист до известной степени способен воздействовать на собственное впечатление
На ум приходит, например, идея рассматривать вертикальную черту как острый и безжалостный нож гильотины, отсекающий последующую цифру — что метафорически символизирует отсечение всей дробной части числа. Такой символ очень неплохо передаёт всю суть происходящего — не хуже, чем английское слово «floor» передаёт суть названного им метода.
Вдругорядь
куда-куда?)
Эта конструкция делает следующее: объект.toString().перевод_в_integer, если_это_число_иначе_в_ноль. Сразу становится понятно почему массив из двух элементов преобразуется в ноль. Потому что [1,2].toString() это «1,2».
Бесконечность?
Может кто-нибудь объяснить зачем она нужна и как ее использовать? Чес слово не могу придумать.
Может кто-нибудь объяснить зачем она нужна и как ее использовать? Чес слово не могу придумать.
> не округляет, а именно отбрасывает дробную часть
Раньше это называлось округлением вниз по модулю. Да, есть разные способы окрулять.
Раньше это называлось округлением вниз по модулю. Да, есть разные способы окрулять.
(вдруг кто не знал)
Из подобных вещей я чаще всего использую || для обработки undefined и null / false значений:
Аналогично для строк:
Особенно это актуально когда конструируем data для ajax запроса, т.к. там если в поле передадим «undefined», то на сервер это в null не переведёт и будет ругаться (по крайней мере для MVC2 это так).
Из подобных вещей я чаще всего использую || для обработки undefined и null / false значений:
function GetRow (index)
{
return $("selector").eq (index || 0);
}
Аналогично для строк:
$("selector").val (n || "")
Особенно это актуально когда конструируем data для ajax запроса, т.к. там если в поле передадим «undefined», то на сервер это в null не переведёт и будет ругаться (по крайней мере для MVC2 это так).
оператор || возвращает первый положительный вариант или последний из условия, если все остальные дают false
То есть вы можете писать и такие конструкции
То есть вы можете писать и такие конструкции
> null || 0 // => 0
> null || 0 || 1 // =>1
> null || 1 || 0 // => 1
> null || 0 || 0 // => 0
> null || 0 || null // => null
> null || 0 || null || undefined // => undefined
> null || 0 || '' || [] || undefined // => []
> null || 0 || '' || false || undefined // => undefined
> null || 0 || '' || false || undefined || 1 // => 1
> null || 0 || '' || false || 1 || undefined // => 1
Хоть бы разок упомянули об операторе побитовое «или»:
developer.mozilla.org/en-US/docs/JavaScript/Reference/Operators/Bitwise_Operators#.7C_(Bitwise_OR)
А не про «символьную конструкцию «|0» в JavaScript»
developer.mozilla.org/en-US/docs/JavaScript/Reference/Operators/Bitwise_Operators#.7C_(Bitwise_OR)
А не про «символьную конструкцию «|0» в JavaScript»
Я это нарочно.
Я опасался вот чего: если думатьпро «|0» в терминах «побитовое ИЛИ с нулём», то этот оператор воспринимается в качестве NOP-подобной «мусорной команды», потому что из курса двоичной арифметики каждому известно, что побитовое ИЛИ с нулём не меняет значение другого (ненулевого) операнда.
Роль этого оператора в джаваскрипте определяется правилами предварительного преобразования операнда к целому числу, а не смыслом самóй операции. Но мне не хотелось упоминать и об этом: смысл статьи сильнее дойдёт до читателя, если читатель сам проделает часть пути к его разгадке.
Я опасался вот чего: если думать
Роль этого оператора в джаваскрипте определяется правилами предварительного преобразования операнда к целому числу, а не смыслом самóй операции. Но мне не хотелось упоминать и об этом: смысл статьи сильнее дойдёт до читателя, если читатель сам проделает часть пути к его разгадке.
На jsPerf, оказывается, уже есть тест в тему: jsperf.com/bitwise-or-vs-math-floor
Вы должны учитывать то что в этом тесте не учитывается оверхед на вызов функции Math.round
Вот подобный, но учитывающий вызовы: jsperf.com/math-round-vs-x-0-5-0/2
Вот подобный, но учитывающий вызовы: jsperf.com/math-round-vs-x-0-5-0/2
Просто так
Python 2.7.3 |EPD_free 7.3-2 (32-bit)| (default, Apr 12 2012, 14:30:37) [MSC v.1500 32 bit (Intel)] on win32
Type «copyright», «credits» or «license()» for more information.
>>> 3|0
3
>>> 3.8|0
Traceback (most recent call last):
File "<pyshell#1>", line 1, in 3.8|0
TypeError: unsupported operand type(s) for |: 'float' and 'int'
>>> -3|0
-3
>>> '3'|0
Traceback (most recent call last):
File "<pyshell#3>", line 1, in '3'|0
TypeError: unsupported operand type(s) for |: 'str' and 'int'
>>>
Python 2.7.3 |EPD_free 7.3-2 (32-bit)| (default, Apr 12 2012, 14:30:37) [MSC v.1500 32 bit (Intel)] on win32
Type «copyright», «credits» or «license()» for more information.
>>> 3|0
3
>>> 3.8|0
Traceback (most recent call last):
File "<pyshell#1>", line 1, in 3.8|0
TypeError: unsupported operand type(s) for |: 'float' and 'int'
>>> -3|0
-3
>>> '3'|0
Traceback (most recent call last):
File "<pyshell#3>", line 1, in '3'|0
TypeError: unsupported operand type(s) for |: 'str' and 'int'
>>>
ааа, мои глаза…
После этого вы говорите, что код Perl вам трудно понять?
:)
После этого вы говорите, что код Perl вам трудно понять?
:)
Вроде уж совсем основы.
n|0 или n>>0 выполнят преобразование ToInt32, n>>>0 — ToUInt32, для отбрасывания дробной части использовать аккуратно — с большими числами будут проблемы — (Math.pow(2,31)|0)===-2147483648.
Т.к. внутренние операции преобразования типов наружу не вынесены, приходится крутиться
n|0 или n>>0 выполнят преобразование ToInt32, n>>>0 — ToUInt32, для отбрасывания дробной части использовать аккуратно — с большими числами будут проблемы — (Math.pow(2,31)|0)===-2147483648.
Т.к. внутренние операции преобразования типов наружу не вынесены, приходится крутиться
function ToInt32(val){
return val>>0}
function ToUInt32(val){
return val>>>0}
function ToUInt16(val){
return (val>>>0)%65536}
var ToInteger=Number.toInt||Number.toInteger||function(val){
return val=+val,val!==val?0:val!==0&&val!==Infinity&&val!==-Infinity?(val>0||-1)*Math.floor(Math.abs(val)):val}
да чего уж там
//никогда не делайте так со своим кодом
Math.floor = function (x){
return x|0;
}
//никогда не делайте так со своим кодом
Кроме Math.floor есть ещё round и ceil, также существует parseInt, и все они работают в несколько раз быстрее у меня сейчас в хроме.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
Вертикальная черта, затем ноль