Скрытые возможности JavaScript? [закрыто]

312

Как вы думаете, какие «скрытые возможности» JavaScript должен знать каждый программист?

Увидев превосходное качество ответов на следующие вопросы, я подумал, что пришло время спросить его о JavaScript.

Хотя JavaScript является, пожалуй, самым важным языком на стороне клиента (просто спросите Google), удивительно, как мало большинство веб-разработчиков понимают, насколько он действительно мощный.

Binoj Antony
источник
1
Разве вы не имели в виду «Видя точки зрения и взгляды, которые привлек этот другой вопрос, я решил задать почти тот же вопрос, чтобы повысить свой собственный»? ;-)
Бобби Джек,
1
Конечно, пессимист. :) Я хотел бы сделать это вопросом сообщества. Кроме того, после того, как вы набрали определенное количество баллов, все это уменьшает отдачу.
Аллен Лалонд
1
Достаточно справедливо - это не выглядит так, как будто вам «нужен» представитель! Я думаю, у меня просто большая проблема с C # one - мне не совсем нравится тип вопроса, для которого предназначен этот сайт.
Бобби Джек,
3
Да, может и нет, но я нашел отличные знания в ответах. Я думаю, что вам будет трудно показать среднестатистического программиста на C # всему этому в одном месте, если бы не SO. Потребовались годы игры, чтобы составить такой же список победителей.
Аллен Лалонд
7
Я профессионально пишу JavaScript уже 10 лет и узнал кое-что из этой темы. Спасибо, Алан!
Эндрю Хеджес

Ответы:

373

Вам не нужно определять какие-либо параметры для функции. Вы можете просто использовать argumentsмассив-подобный объект функции.

function sum() {
    var retval = 0;
    for (var i = 0, len = arguments.length; i < len; ++i) {
        retval += arguments[i];
    }
    return retval;
}

sum(1, 2, 3) // returns 6
Mark Cidade
источник
117
Стоит отметить, что хотя аргументы действуют как массив, это не настоящий массив JavaScript, а просто объект. Таким образом, вы не можете использовать join (), pop (), push (), slice () и так далее. (Вы можете преобразовать его в реальный массив, если хотите: "var argArray = Array.prototype.slice.call (arguments);")
Джейкоб Мэттисон
51
Стоит также отметить, что доступ к объекту Arguments относительно дорог - лучшие примеры - это ночные программы Safari, Firefox и Chrome, где простая ссылка на argumentsобъект делает вызов функции намного медленнее, например. аргументы if (false); будет больно перф.
olliej
48
В том же духе аргументы имеют свойство callee, которым является сама текущая функция. Это позволяет делать рекурсию с анонимными функциями, круто!
Винсент Роберт
4
@ Натан "f (x, y, z)" выглядит лучше, чем "f ([x, y, z])".
Марк Сидаде
16
@ Винсент Роберт: пожалуйста, обратите внимание, что arguments.calleeэто устарело.
Кен
204

Я мог бы процитировать большую часть превосходной книги Дугласа Крокфорда « JavaScript: хорошие части» .

Но я возьму только один для вас, всегда используйте ===и !==вместо ==и!=

alert('' == '0'); //false
alert(0 == ''); // true
alert(0 =='0'); // true

==не является переходным. Если вы используете ===его, вы получите ложь для всех этих утверждений, как и ожидалось.

Martin Clarke
источник
29
Обидно, что так много людей думают, что Крокфорд всезнающий. Конечно, парень в своей критике находится на высоте, но я не могу дать его материалу полное одобрение, как это делают многие разработчики ...
Джейсон Бантинг
21
Я второе предупреждение Джейсона. Книга сама по себе очень интересно, и это действительно дает много хороших советов, но постоянного тока далеко слишком уверен , что его способ делать вещи , это единственно правильный путь, все остальное «дефектный». Если вам нужны примеры, посмотрите его ответы на JSLint Yahoo Group.
Zilk
30
Используйте === вместо == - хороший совет, если вас смущает динамическая типизация, и вы просто хотите, чтобы она была «действительно» равной. Те из нас, кто понимает динамическую типизацию, могут продолжать использовать == для ситуаций, когда мы знаем, что хотим разыграть, как в 0 == '' или 0 == '0'.
Томасруттер
20
Ну == и === не о динамической типизации. == делает тип приведения, который является другим зверем. Если вы знаете, что хотите преобразовать в строку / число / и т. Д., То вы должны сделать это явно.
Рене Саарсоо
15
Я думаю, что самая страшная часть ==это '\n\t\r ' == 0=> true...: D
Shrikant Sharat
189

Функции являются гражданами первого класса в JavaScript:

var passFunAndApply = function (fn,x,y,z) { return fn(x,y,z); };

var sum = function(x,y,z) {
  return x+y+z;
};

alert( passFunAndApply(sum,3,4,5) ); // 12

Методы функционального программирования могут быть использованы для написания элегантного JavaScript .

В частности, функции могут передаваться как параметры, например, Array.filter () принимает обратный вызов:

[1, 2, -1].filter(function(element, index, array) { return element > 0 });
// -> [1,2]

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

function PrintName() {
    var privateFunction = function() { return "Steve"; };
    return privateFunction();
}
Гульзар Назим
источник
3
Есть три способа сделать функции в javascript: function sum (x, y, z) {return (x + y + z); } и var sum = new Function ("x", "y", "z", "return (x + y + z);"); другие способы.
Мариус
6
Концепция функций как данных определенно выигрывает в моей книге.
Джейсон Бантинг
Я только обновил пример, чтобы показать, как использовать «частную» функцию, которая существует только в рамках определенной функции.
Крис Питчманн
new Function()как зло , как eval. Не используй.
Николас
11
не уверен, что это скрытая функция ... больше похоже на основную функцию.
Клавдиу
162

Вы можете использовать оператор in, чтобы проверить, существует ли ключ в объекте:

var x = 1;
var y = 3;
var list = {0:0, 1:0, 2:0};
x in list; //true
y in list; //false
1 in list; //true
y in {3:0, 4:0, 5:0}; //true

Если вы находите литералы объекта слишком уродливыми, вы можете объединить их с функцией без параметров tip:

function list()
 { var x = {};
   for(var i=0; i < arguments.length; ++i) x[arguments[i]] = 0;
   return x
 }

 5 in list(1,2,3,4,5) //true
Mark Cidade
источник
22
Не такой умный, который проверяет наличие ключа, а не значения. х в списке; работает только потому, что x [1]! = null, а не потому, что там есть значение 1.
Армин Ронахер
1
В то время я не использовал эту технику, поэтому я забыл, что раньше я действительно использовал объектные литералы. Спасибо за исправление.
Марк Сидаде
34
Также будьте осторожны: оператор in также тестирует цепочку прототипов! Если кто-то поместил свойство с именем «5» в Object.prototype, второй пример вернет true, даже если вы назвали «5 в списке (1, 2, 3, 4)» ... Вам лучше использовать hasOwnProperty method: list (1, 2, 3, 4) .hasOwnProperty (5) вернет false, даже если Object.prototype имеет свойство '5'.
Мартейн
3
Для самого общего решения, которое может проверить, имеет ли объект свое собственное свойство, даже если оно называется hasOwnProperty, вы должны пройти весь путь до: Object.prototype.hasOwnProperty.call (object, name) ;
Крис Ковал
1
@ Крис, нет, если кто-то не перезаписывает Object.prototype.hasOwnProperty;)
Ник
153

Присвоение значений по умолчанию переменным

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

var a = b || c;

aПеременная получит значение cтолько тогда , когда bэто falsy (если есть null, false, undefined, 0, empty string, или NaN), в противном случае aполучите значение b.

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

function example(arg1) {
  arg1 || (arg1 = 'default value');
}

Пример отката IE в обработчиках событий:

function onClick(e) {
    e || (e = window.event);
}

Следующие языковые функции были с нами в течение долгого времени, все реализации JavaScript поддерживают их, но они не были частью спецификации до выпуска ECMAScript 5th Edition :

debuggerзаявление

Описано в: § 12.15 Оператор отладчика

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

// ...
debugger;
// ...

Если отладчик присутствует или активен, он сразу же прекратит работу прямо в этой строке.

В противном случае, если отладчик отсутствует или активен, этот оператор не имеет видимого эффекта.

Многострочные строковые литералы

Описано в: § 7.8.4 Строковые литералы

var str = "This is a \
really, really \
long line!";

Вы должны быть осторожны, потому что символ рядом с \ должен быть символом конца строки, если после пробела, \например, код будет выглядеть точно так же, но он вызовет a SyntaxError.

CMS
источник
28
Нет, если это ноль, если это считается ложным. a = 0 || 42; даст вам 42. Это сопоставимо с Python или, не C #, ?? оператор. Если вы хотите поведение C #, a = (b === null)? с: б;
Армин Ронахер
Это также работает в Visual Studio, если вы разрабатываете на ASP.NET :)
chakrit
2
Я бы хотел, чтобы было правильно || только для неопределенных. Это меня укусило сегодня за 0, так как я хотел создать эмуляцию перегруженного метода, чтобы последний аргумент был необязательным и вместо него использовалось значение по умолчанию.
egaga
+1 этот трюк используется по умолчанию фрагментом Google Analytics. `var _gaq = _gaq || []; `; это мешает чрезмерно усердным пользователям перезаписывать свою собственную работу.
Яхель
2
Я не знал о методе многострочного строкового литерала. Это фантастика, спасибо.
Чарли Флауэрс
145

У JavaScript нет блочной области видимости (но у него есть замыкание, поэтому давайте назовем его даже?).

var x = 1;
{
   var x = 2;
}
alert(x); // outputs 2
Евгений Йокота
источник
3
Это хорошо. Это действительно важное отличие от большинства языков, подобных Си.
Мартин Кларк
9
Вы всегда можете выполнить "var tmp = function () {/ * block scope * /} ();". Синтаксис безобразен, но работает.
Джори Себрехтс
3
Или вы можете использовать «let», только если это Firefox: stackoverflow.com/questions/61088/…
Юджин Йокота
10
или просто: (function () {var x = 2;}) (); оповещение (typeof x); // undefined
Pim Jager
@Pim: JSLint говорит: «Переместите вызов в парены, содержащие функцию». Наряду с «Ожидается ровно один пробел между« function »и« ('. ».
Hello71
144

Вы можете получить доступ к свойствам объекта с помощью []вместо.

Это позволяет вам искать свойство, соответствующее переменной.

obj = {a:"test"};
var propname = "a";
var b = obj[propname];  // "test"

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

obj["class"] = "test";  // class is a reserved word; obj.class would be illegal.
obj["two words"] = "test2"; // using dot operator not possible with the space.

Некоторые люди не знают этого и заканчивают тем, что используют eval () как это, что является действительно плохой идеей :

var propname = "a";
var a = eval("obj." + propname);

Это сложнее для чтения, труднее найти ошибки (нельзя использовать jslint), медленнее выполнять и может привести к эксплойтам XSS.

Патрик
источник
Эвал - это зло, хотя редко бывает необходимо
Даг Домени
Я никогда не использую eval и помню, когда я обнаружил это. Это сделало меня очень счастливым.
Таким образом, к свойствам объекта можно обращаться как через точку, так и через нижнюю запись
Russ Cam,
9
Интересно отметить, что точечные ссылки на самом деле являются синтаксическим сахаром для скобок. foo.barв соответствии со спецификацией в любом случае ведет себя так же, как foo["bar"]. Также обратите внимание, что все является строковым свойством. даже когда вы array[4]обращаетесь к массиву, 4 преобразуется в строку (опять же, по крайней мере, в соответствии со спецификацией ECMAScript v3)
Claudiu
Я думаю, что каждый программист JS должен знать это.
Джем Калионку
144

Если вы ищете Google справочник JavaScript по данной теме, включите ключевое слово «mdc» в свой запрос, и ваши первые результаты будут получены из Центра разработчиков Mozilla. Я не ношу с собой никаких офлайновых ссылок или книг. Я всегда использую трюк с ключевым словом "mdc", чтобы напрямую получить то, что я ищу. Например:

Google: javascript array sort mdc
(в большинстве случаев вы можете опустить «javascript»)

Обновление: Центр разработчиков Mozilla переименован в Сеть разработчиков Mozilla . Трюк с ключевым словом "mdc" все еще работает, но довольно скоро нам, возможно, придется начать использовать вместо него "mdn" .

Ates Goral
источник
50
Вау, отличный ресурс. Мгновенно лучше, чем дрянные w3schools ...
DisgruntledGoat
11
Вам даже не нужно гуглить, если вы используете Firefox: просто введите «array mdc» в адресную строку и нажмите Enter.
Саша Чедыгов
2
лучшая часть в том, как этот вопрос переполнения стека находится на первой странице результатов :)
Jiaaro
5
Пропаганда это: promojs.com , массовая инициатива в области SEO для продвижения результатов MDC в результатах поиска Google.
Яхель
3
Теперь это центр документации MDN, поэтому ключевое слово 'mdc' все еще действует :)
Aleadam
143

Может быть, немного очевидным для некоторых ...

Установите Firebug и используйте console.log ("привет"). Намного лучше, чем использование random alert (); я помню, что это делал много лет назад.

Qui
источник
12
Только не забудьте удалить операторы консоли перед выпуском кода другим, у кого, возможно, не установлен Firebug.
Крис Но
161
журнал функций (msg) {if (console) console.log (msg) else alert (msg)}
Джош
4
Еще лучше, перед логами ставится ';;;' а затем minify позаботится об этом за вас. (По крайней мере, модуль Perl, который я использую, имеет эту функцию и утверждает, что это обычное дело.)
Kev
10
Джош: Это не сработает, так как консоль не определена. Вы можете проверить typeof console! == "undefined" или window.console.
Эли Грей
23
Всегда включайте: if (typeof ('console') == 'undefined') {console = {log: function () {}}; } тогда вы можете продолжать использовать console.log, и он просто ничего не делает.
gregmac
120

Частные Методы

Объект может иметь приватные методы.

function Person(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;

    // A private method only visible from within this constructor
    function calcFullName() {
       return firstName + " " + lastName;    
    }

    // A public method available to everyone
    this.sayHello = function () {
        alert(calcFullName());
    }
}

//Usage:
var person1 = new Person("Bob", "Loblaw");
person1.sayHello();

// This fails since the method is not visible from this scope
alert(person1.calcFullName());
Аллен Лалонд
источник
16
Это на самом деле не частная функция - это скорее переменная функции в локальной области видимости.
Кит
6
Правда, но по всем операционным определениям я могу думать, что это метод. Это блок кода с именем, который имеет доступ к состоянию экземпляра и может быть виден только этому экземпляру. Какое у вас определение частного метода?
Аллен Лалонд
14
@ Зак точно! После долгих лет работы с ОО-языками на основе классов легко забыть, что они являются лишь одной из реализаций ОО-концепций. Конечно, различные библиотеки, которые пытаются втиснуть ОО на основе квазиклассов в JS, тоже не помогают ...
Shog9
5
Просто интересно, есть ли у person1 юридический блог? ;-)
Тревис
4
+1 за арестованную ссылку на разработку
Доменик
99

Также упоминается в «Javascript: Хорошие части» Крокфорда:

parseInt()опасный. Если вы передадите ей строку, не сообщив ей о надлежащей базе, она может вернуть неожиданные числа. Например, parseInt('010')возвращает 8, а не 10. Передача базы в parseInt заставляет его работать правильно:

parseInt('010') // returns 8! (in FF3)
parseInt('010', 10); // returns 10 because we've informed it which base to work with.
bluefish101
источник
13
Делая обзоры кода, всегда ищите это. Пропуск "10" является распространенной ошибкой, которая остается незамеченной в большинстве тестов.
Дуг Домени
Я сгорел от проблемы с корнем много лет назад и никогда не забывал что-то столь нелогичное как таковое. Замечательная вещь, которую стоит отметить, так как это заставит вас задуматься на некоторое время.
JamesEggers,
4
Почему бы не использовать Math.floorили Number? 10 === Math.floor("010"); 10 === Number("010");плавает:42 === Math.floor("42.69"); 42.69 === Number("42.69");
просто кто-то
1
@Infinity Если вы еще не опубликовали ответ, вы должны. Я понятия не имел, как просто переопределить поведение встроенной функции. Конечно, следует поближе взглянуть на любые пакеты кода, которые они заимствуют на других сайтах. Эту безвредную parseIntфункцию можно легко заставить сделать что-то не столь безобидное.
Боб-разрушитель
6
@Infinity: как насчет переопределения fn для выделения «ошибки кодирования»? __parseInt = parseInt; parseInt = function (str, base) { if (!base) throw new Error(69, "All your base belong to us"); return __parseInt(str, base); }
Дж.Б. Уилкинсон
97

Функции являются объектами и поэтому могут иметь свойства.

fn = function (x) {
   // ...
}

fn.foo = 1;

fn.next = function (y) {
  //
}
VolkerK
источник
13
Это очень полезный совет. Например, вы можете установить значения по умолчанию в качестве свойства функции. Например: myfunc.delay = 100; Затем пользователи могут изменить значение по умолчанию, и все вызовы функций будут использовать новое значение по умолчанию. Например: myfunc.delay = 200; MyFunc ();
BarelyFitz
Полезно ... и опасно!
Palswim
Выглядит небрежно, зачем использовать это вместо переменной?
instantsetsuna
1
@instantsetsuna: Почему есть другая отдельная переменная? Как обычно, это сводится к тому, чтобы «использовать его, когда это уместно / полезно» ;-)
VolkerK
91

Я должен был бы сказать, что выполняющиеся функции.

(function() { alert("hi there");})();

Поскольку Javascript не имеет области видимости блока , вы можете использовать самовыполняющуюся функцию, если хотите определить локальные переменные:

(function() {
  var myvar = 2;
  alert(myvar);
})();

Здесь myvaris не вмешивается и не загрязняет глобальную область и исчезает, когда функция завершается.

thomasrutter
источник
2
Для чего это нужно? Вы получаете те же результаты, если поместить предупреждение за пределы функции.
PotatoEngineer
7
Речь идет не о предупреждении, а об определении и выполнении функции одновременно. Вы могли бы заставить эту самовыполняющуюся функцию возвращать значение и передавать функцию в качестве параметра другой функции.
ScottKoon
5
@Paul это хорошо для инкапсуляции.
Майк Робинсон
22
Это также хорошо для определения объема блока.
Джим Хунзикер
24
Да, я заключаю все свои .jsфайлы в анонимную самовыполняющуюся функцию и прикрепляю к windowобъекту все, что захочу в глобальном масштабе . Предотвращает глобальное загрязнение пространства имен.
cdmckay
83

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

function add_nums(num1, num2, num3 ){
    return num1 + num2 + num3;
}
add_nums.length // 3 is the number of parameters expected.

Знайте, сколько параметров получено функцией

function add_many_nums(){
    return arguments.length;
}    
add_many_nums(2,1,122,12,21,89); //returns 6
оборота pramodc84
источник
23
Никогда не знал о первой части. Ницца!
mcjabberz
1
Точно так же вы можете узнать, сколько аргументов ожидает функция function.length.
Хави
6
@Xavi, который является 1-й частью ответа
pramodc84
79

Вот несколько интересных вещей:

  • По сравнению NaNс чем - либо (даже NaN) всегда ложно, что включает в себя ==, <и >.
  • NaN Обозначает не число, но если вы спросите тип, он на самом деле возвращает число.
  • Array.sort может принимать функцию сравнения и вызывается драйвером, подобным быстрой сортировке (зависит от реализации).
  • Регулярные выражения «константы» могут поддерживать состояние, как и последнее, что они сопоставили.
  • Некоторые версии JavaScript позволяют вам получить доступ $0, $1, $2члены на регулярное выражение.
  • nullотличается от всего остального. Это ни объект, ни логическое значение, ни число, ни строка undefined. Это немного похоже на «альтернативу» undefined. (Примечание: typeof null == "object")
  • Во thisвнешнем контексте выдает иначе неименуемый объект [Global].
  • Объявление переменной с помощью var, а не просто автоматического определения переменной дает среде выполнения реальный шанс оптимизировать доступ к этой переменной
  • withКонструкция будет уничтожать такой optimzations
  • Имена переменных могут содержать символы Unicode.
  • Регулярные выражения JavaScript на самом деле не являются регулярными. Они основаны на регулярных выражениях Perl, и можно создавать выражения с предвкушением, для оценки которых требуется очень и очень много времени.
  • Блоки могут быть помечены и использованы в качестве целей break. Петли могут быть помечены и использованы в качестве цели continue.
  • Массивы не редки. Установка 1000-го элемента в противном случае пустого массива должна заполнить его undefined. (зависит от реализации)
  • if (new Boolean(false)) {...} выполнит {...}блок
  • Механизм регулярных выражений Javascript зависит от реализации: например, можно писать «непереносимые» регулярные выражения.

[немного обновлено в ответ на хорошие комментарии; пожалуйста, смотрите комментарии]

David Leonard
источник
5
Нуль на самом деле (специальный) объект. typeof nullвозвращает «объект».
Ates Goral
4
Вы также можете получить объект [Global] из любого места, например: var glb = function () {return this; } ();
Zilk
2
Глобальный объект в javascript в браузере - это объект окна. Когда в глобальной области видимости делаешь: window.a == a;
Пим Джагер
8
«Массивы не редки» зависит от реализации. Если вы установите значение [1000] и посмотрите на [999], тогда да, это так undefined, но это просто значение по умолчанию, которое вы получаете при поиске несуществующего индекса. Если вы проверили [2000], это тоже будет undefined, но это не значит, что вы уже выделили для него память. В IE8 некоторые массивы плотные, а некоторые - разреженные, в зависимости от того, что чувствовал движок JScript в то время. Подробнее читайте здесь: blogs.msdn.com/jscript/archive/2008/04/08/…
Крис Нильсен,
2
@Ates и @SF: typeof возвращает «объект» для диапазона различных типов. Но как только вы узнаете, как он работает и какие типы идентифицируются как «объект», он по меньшей мере надежен и последовательн в своей реализации.
Томасруттер
77

Я знаю, что опаздываю на вечеринку, но я просто не могу поверить, что +полезность оператора не была упомянута, кроме как «преобразовать что-либо в число». Может быть, как хорошо это скрыто?

// Quick hex to dec conversion:
+"0xFF";              // -> 255

// Get a timestamp for now, the equivalent of `new Date().getTime()`:
+new Date();

// Safer parsing than parseFloat()/parseInt()
parseInt("1,000");    // -> 1, not 1000
+"1,000";             // -> NaN, much better for testing user input
parseInt("010");      // -> 8, because of the octal literal prefix
+"010";               // -> 10, `Number()` doesn't parse octal literals 

// A use case for this would be rare, but still useful in cases
// for shortening something like if (someVar === null) someVar = 0;
+null;                // -> 0;

// Boolean to integer
+true;                // -> 1;
+false;               // -> 0;

// Other useful tidbits:
+"1e10";              // -> 10000000000
+"1e-4";              // -> 0.0001
+"-12";               // -> -12

Конечно, вы можете сделать все это, используя Number()вместо этого, но +оператор намного красивее!

Вы также можете определить числовое возвращаемое значение для объекта, переопределив метод прототипа valueOf(). Любое преобразование чисел, выполненное для этого объекта, не приведет NaNк возвращению значения valueOf()метода:

var rnd = {
    "valueOf": function () { return Math.floor(Math.random()*1000); }
};
+rnd;               // -> 442;
+rnd;               // -> 727;
+rnd;               // -> 718;
Энди Э
источник
Вы можете сделать просто 0xFFи т. Д., Не нужно +"0xFF".
nyuszika7h
9
@ Nyuszika7H: вы упускаете из виду ту точку, которая принуждает другие примитивы и объекты к числам. Конечно, вы можете просто написать 0xFF, почти так же, как вы можете написать 1вместо +true. Я предлагаю, чтобы вы могли использовать +("0x"+somevar)в качестве альтернативы parseInt(somevar, 16), если хотите.
Энди Э
75

« Методы расширения в JavaScript » через свойство prototype.

Array.prototype.contains = function(value) {  
    for (var i = 0; i < this.length; i++) {  
        if (this[i] == value) return true;  
    }  
    return false;  
}

Это добавит containsметод ко всем Arrayобъектам. Вы можете вызвать этот метод, используя этот синтаксис

var stringArray = ["foo", "bar", "foobar"];
stringArray.contains("foobar");
spoon16
источник
18
Обычно это считается плохой идеей, потому что другой код (не ваш) может делать предположения об объекте Array.
Крис Но
39
Также обычно считается плохой идеей делать предположения об объекте Array. :(
век
Хмммм .. Javascript 1.6 массив дополнений? индекс? звонит в колокола?
Бретон
2
@Breton: Это не что-то конкретное для класса Array, это просто пример. Я использую это, чтобы расширить новый Date (). ToString (); метод, позволяющий использовать строку маски. Любой объект может быть расширен, и все его экземпляры получают новый метод.
Эстебан Кюбер
1
@Mathias: это не о DOM.
дольмен
60

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

var obj = { prop1: 42, prop2: 43 };

obj.prop2 = undefined;

for (var key in obj) {
    ...

Свойство prop2 по- прежнему будет частью итерации. Если вы хотите полностью избавиться от prop2 , вы должны вместо этого сделать:

delete obj.prop2;

Свойство prop2 больше не будет появляться , когда вы перебираете свойства.

Атес Горал
источник
3
Обратите внимание, что оператор delete не лишен специфических особенностей браузера. Например, это приведет к большой ошибке, если вы попробуете это в IE, и объект не будет нативным объектом JS (даже при удалении свойства, которое вы добавили самостоятельно). Он также не предназначен для удаления переменной, как в случае удаления myvar; но я думаю, что это работает в некоторых браузерах. Код в ответе выше кажется довольно безопасным.
Томасруттер
кстати, undefined тоже может быть переменной! Попробуйте var undefined = "что-то"
Иоганн Филипп Стратхаузен
57

with,

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

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

var user = 
{
   fname: 'Rocket', 
   mname: 'Aloysus',
   lname: 'Squirrel', 
   city: 'Fresno', 
   state: 'California'
};

// ...

with (user)
{
   mname = 'J';
   city = 'Frostbite Falls';
   state = 'Minnesota';
}

Алан Сторм отмечает, что это может быть несколько опасно: если объект, используемый в качестве контекста, не имеет одного из назначаемых свойств, он будет разрешен во внешней области, возможно создавая или перезаписывая глобальную переменную. Это особенно опасно, если вы привыкли писать код для работы с объектами, где свойства со значениями по умолчанию или пустыми значениями остаются неопределенными:

var user = 
{
   fname: "John",
// mname definition skipped - no middle name
   lname: "Doe"
};

with (user)
{
   mname = "Q"; // creates / modifies global variable "mname"
}

Поэтому, вероятно, будет хорошей идеей избегать использования withоператора для такого назначения.

См. Также: Существуют ли законные варианты использования оператора «with» в JavaScript?

Shog9
источник
29
Обычной мудрости со статутом следует избегать. Если у объекта пользователя не было ни одного из упомянутых вами свойств, переменная вне псевдоскопа блока with будет изменена. Таким образом, кроется ошибка. Больше информации на yuiblog.com/blog/2006/04/11/with-statement-considered-harmful
Алан Сторм
1
Шог, возражения не о переменных с ошибками, а о взгляде на блок кода и возможности с уверенностью сказать, что делает любая конкретная строка в этом блоке. Поскольку объекты Javascript настолько динамичны, вы не можете точно сказать, какими свойствами / членами они обладают в любой момент.
Алан Шторм
2
Аминь - если бы я увидел выражение «с» в любом найденном мной JS, я бы исключил его и задал вопрос разработчику, написавшему его, чтобы удостовериться, что он знает, почему его использовать нехорошо… «скрытая возможность?» Больше похоже на «отвратительную особенность».
Джейсон Бантинг
1
рассмотрим более сложную цепочку abcd "с (abc) {d.foo = bar;} мощна и не подвержена ошибкам. Ключ заключается в том, чтобы сократить корень на один уровень вверх. И неправильно вводить имя переменной? Вы вводите ошибку если вы делаете это, где бы вы ни делали, независимо от «с».
annakata
4
Дуглас Крокфорд недавно сказал, что «с» - одна из худших частей JavaScript в .NET Rocks! Подкаст.
ядро
51

Методы (или функции) можно вызывать для объектов, которые не относятся к тому типу, с которым они были разработаны для работы. Это здорово - вызывать собственные (быстрые) методы для пользовательских объектов.

var listNodes = document.getElementsByTagName('a');
listNodes.sort(function(a, b){ ... });

Этот код вылетает, потому что listNodesэто неArray

Array.prototype.sort.apply(listNodes, [function(a, b){ ... }]);

Этот код работает, потому что listNodesопределяет достаточно массивоподобных свойств (длина, оператор []) для использования sort().

Винсент Роберт
источник
43

Наследование прототипа (популяризируемое Дугласом Крокфордом) полностью революционизирует ваше представление о множестве вещей в Javascript.

Object.beget = (function(Function){
    return function(Object){
        Function.prototype = Object;
        return new Function;
    }
})(function(){});

Это убийца! Жаль, что почти никто не использует это.

Это позволяет вам «создавать» новые экземпляры любого объекта, расширять их, сохраняя при этом (живую) прототипную связь наследования с другими их свойствами. Пример:

var A = {
  foo : 'greetings'
};  
var B = Object.beget(A);

alert(B.foo);     // 'greetings'

// changes and additionns to A are reflected in B
A.foo = 'hello';
alert(B.foo);     // 'hello'

A.bar = 'world';
alert(B.bar);     // 'world'


// ...but not the other way around
B.foo = 'wazzap';
alert(A.foo);     // 'hello'

B.bar = 'universe';
alert(A.bar);     // 'world'
Már Örlygsson
источник
42

Некоторые называют это делом вкуса, но:

aWizz = wizz || "default";
// same as: if (wizz) { aWizz = wizz; } else { aWizz = "default"; }

Оператор трины может быть прикован к цепочке, чтобы действовать как схема (cond ...):

(cond (predicate  (action  ...))
      (predicate2 (action2 ...))
      (#t         default ))

можно записать как ...

predicate  ? action( ... ) :
predicate2 ? action2( ... ) :
             default;

Это очень «функционально», так как ветвит ваш код без побочных эффектов. Так что вместо:

if (predicate) {
  foo = "one";
} else if (predicate2) {
  foo = "two";
} else {
  foo = "default";
}

Ты можешь написать:

foo = predicate  ? "one" :
      predicate2 ? "two" :
                   "default";

Хорошо работает с рекурсией тоже :)

Андрей Федоров
источник
Мне нравится синтаксис предиката, который вы даете. Я никогда не думал о такой цепочке. аккуратный.
Аллен Лалонд
2
Э-э ... JavaScript имеет оператор switch (). :-)
staticsan
Я не большой поклонник операторов switch - это артефакт C, а не функциональное программирование. В моем примере для оператора switch по-прежнему нужны три отдельных оператора, все из которых начинаются с "foo =" - очевидное ненужное повторение.
Андрей Федоров
14
Я, например, приветствую троичного оператора.
Томасруттер
8
При повторном чтении я хотел бы отметить, что это не «делает код похожим на другой язык», а фактически упрощает семантическое значение кода: когда вы пытаетесь сказать «установите foo на одно из трех» вещи ", это утверждение, которое должно начинаться с" foo = ... ", а не" если ".
Андрей Федоров
41

Числа также являются объектами. Таким образом, вы можете делать такие классные вещи, как:

// convert to base 2
(5).toString(2) // returns "101"

// provide built in iteration
Number.prototype.times = function(funct){
  if(typeof funct === 'function') {
    for(var i = 0;i < Math.floor(this);i++) {
      funct(i);
    }
  }
  return this;
}


(5).times(function(i){
  string += i+" ";
});
// string now equals "0 1 2 3 4 "

var x = 1000;

x.times(function(i){
  document.body.innerHTML += '<p>paragraph #'+i+'</p>';
});
// adds 1000 parapraphs to the document
Zach
источник
О, МОЙ БОГ! Я не знал о toString (radix) ...
Ates Goral
1
Эта реализация timesнеэффективна: Math.floorвызывается каждый раз, а не один раз.
Дольмен
33

Как насчет замыканий в JavaScript (аналогично анонимным методам в C # v2.0 +). Вы можете создать функцию, которая создает функцию или «выражение».

Пример замыканий :

//Takes a function that filters numbers and calls the function on 
//it to build up a list of numbers that satisfy the function.
function filter(filterFunction, numbers)
{
  var filteredNumbers = [];

  for (var index = 0; index < numbers.length; index++)
  {
    if (filterFunction(numbers[index]) == true)
    {
      filteredNumbers.push(numbers[index]);
    }
  }
  return filteredNumbers;
}

//Creates a function (closure) that will remember the value "lowerBound" 
//that gets passed in and keep a copy of it.
function buildGreaterThanFunction(lowerBound)
{
  return function (numberToCheck) {
    return (numberToCheck > lowerBound) ? true : false;
  };
}

var numbers = [1, 15, 20, 4, 11, 9, 77, 102, 6];

var greaterThan7 = buildGreaterThanFunction(7);
var greaterThan15 = buildGreaterThanFunction(15);

numbers = filter(greaterThan7, numbers);
alert('Greater Than 7: ' + numbers);

numbers = filter(greaterThan15, numbers);
alert('Greater Than 15: ' + numbers);
Тайлер
источник
1
я не уверен, но могу вернуть (numberToCheck> lowerBound)? правда: ложь; просто стать return (numberToCheck> lowerBound); просто пытаюсь улучшить мое понимание ...
Давидсплит
4
Я бы сказал, что анонимные функции в C # эквивалентны замыканиям, а не наоборот :)
vava,
11
Замыкания и анонимные функции - это отдельные, разные понятия. То, что функции могут быть созданы без имени, имеет анонимные функции. То, что переменная в области «создания» связана с созданной функцией, является закрытием. Короче говоря, замыкание больше похоже на скрытую глобальную переменную.
slebetman
1
Это правда. Только когда анонимные методы используют переменную из создаваемой области, она похожа на замыкание. Я обновил английский в ответе. Это все еще оставляет желать лучшего, но я не могу найти правильный английский.
Тайлер
2
Я не думаю, что это лучший или самый простой для понимания пример того, что такое закрытие. Просто говорю. Смысл замыкания в том, что даже когда группа переменных «выходит из области видимости», они все равно могут оставаться доступными для функции, которая была первоначально определена в этой области. В приведенном выше примере это означает, что переменная lowerBound все еще доступна этой внутренней анонимной функции, даже когда внешняя функция buildGreaterThanFunction завершается.
Томасруттер
32

Вы также можете расширять (наследовать) классы и переопределять свойства / методы, используя цепочку прототипов spoon16, на которую ссылаются.

В следующем примере мы создаем класс Pet и определяем некоторые свойства. Мы также переопределяем метод .toString (), унаследованный от Object.

После этого мы создаем класс Dog, который расширяет Pet и переопределяет метод .toString (), снова изменяя его поведение (полиморфизм). Кроме того, мы добавляем некоторые другие свойства в дочерний класс.

После этого мы проверяем цепочку наследования, чтобы показать, что Dog по-прежнему имеет тип Dog, тип Pet и тип Object.

// Defines a Pet class constructor 
function Pet(name) 
{
    this.getName = function() { return name; };
    this.setName = function(newName) { name = newName; };
}

// Adds the Pet.toString() function for all Pet objects
Pet.prototype.toString = function() 
{
    return 'This pets name is: ' + this.getName();
};
// end of class Pet

// Define Dog class constructor (Dog : Pet) 
function Dog(name, breed) 
{
    // think Dog : base(name) 
    Pet.call(this, name);
    this.getBreed = function() { return breed; };
}

// this makes Dog.prototype inherit from Pet.prototype
Dog.prototype = new Pet();

// Currently Pet.prototype.constructor
// points to Pet. We want our Dog instances'
// constructor to point to Dog.
Dog.prototype.constructor = Dog;

// Now we override Pet.prototype.toString
Dog.prototype.toString = function() 
{
    return 'This dogs name is: ' + this.getName() + 
        ', and its breed is: ' + this.getBreed();
};
// end of class Dog

var parrotty = new Pet('Parrotty the Parrot');
var dog = new Dog('Buddy', 'Great Dane');
// test the new toString()
alert(parrotty);
alert(dog);

// Testing instanceof (similar to the `is` operator)
alert('Is dog instance of Dog? ' + (dog instanceof Dog)); //true
alert('Is dog instance of Pet? ' + (dog instanceof Pet)); //true
alert('Is dog instance of Object? ' + (dog instanceof Object)); //true

Оба ответа на этот вопрос были кодами, модифицированными из великолепной статьи MSDN Рэя Джаджадината.

Тайлер
источник
31

Вы можете ловить исключения в зависимости от их типа. Цитируется из MDC :

try {
   myroutine(); // may throw three exceptions
} catch (e if e instanceof TypeError) {
   // statements to handle TypeError exceptions
} catch (e if e instanceof RangeError) {
   // statements to handle RangeError exceptions
} catch (e if e instanceof EvalError) {
   // statements to handle EvalError exceptions
} catch (e) {
   // statements to handle any unspecified exceptions
   logMyErrors(e); // pass exception object to error handler
}

ПРИМЕЧАНИЕ. Условные предложения catch являются расширением Netscape (и, следовательно, Mozilla / Firefox), которое не является частью спецификации ECMAScript и, следовательно, на него нельзя положиться, за исключением отдельных браузеров.

Ionuț G. Stan
источник
29
Я ничего не мог поделать: поймать (меня, если вы можете)
Ates Goral
6
Прочитайте примечание со страницы MDC, на которую вы ссылались: условия условного перехвата являются расширением Netscape (и, следовательно, Mozilla / Firefox), которое не является частью спецификации ECMAScript и, следовательно, на него нельзя положиться, кроме как в определенных браузерах.
Джейсон С.
31

С верхней части моей головы...

функции

arguments.callee ссылается на функцию, которая содержит переменную «arguments», поэтому ее можно использовать для рекурсии анонимных функций:

var recurse = function() {
  if (condition) arguments.callee(); //calls recurse() again
}

Это полезно, если вы хотите сделать что-то вроде этого:

//do something to all array items within an array recursively
myArray.forEach(function(item) {
  if (item instanceof Array) item.forEach(arguments.callee)
  else {/*...*/}
})

Объекты

Интересная вещь о членах объекта: у них может быть любая строка в качестве их имен:

//these are normal object members
var obj = {
  a : function() {},
  b : function() {}
}
//but we can do this too
var rules = {
  ".layout .widget" : function(element) {},
  "a[href]" : function(element) {}
}
/* 
this snippet searches the page for elements that
match the CSS selectors and applies the respective function to them:
*/
for (var item in rules) {
  var elements = document.querySelectorAll(rules[item]);
  for (var e, i = 0; e = elements[i++];) rules[item](e);
}

Струны

String.split может принимать регулярные выражения в качестве параметров:

"hello world   with  spaces".split(/\s+/g);
//returns an array: ["hello", "world", "with", "spaces"]

String.replace может принимать регулярное выражение в качестве параметра поиска и функцию в качестве параметра замены:

var i = 1;
"foo bar baz ".replace(/\s+/g, function() {return i++});
//returns "foo1bar2baz3"
Лев
источник
Вещи, которые вы упоминаете ... Они реализованы во всех браузерах?
cllpse
4
Нет, я уверен, что Мозаика не хватает большинства из них.
Jsight
2
Функции javascript, да, они реализованы во всех основных браузерах (IE6 / 7, FF2 / 3, Opera 9+, Safari2 / 3 и Chrome). document.querySelectorAll еще не поддерживается во всех браузерах (это W3C-версия JQuery's $ () и Prototype's $$ ())
Leo
6
arguments.calleeустарела и
выдает
не совсем верно. Ключ объекта не может (или, скорее, не должен) использовать строку «hasOwnProperty» в качестве имени, поскольку это переопределит встроенный метод объекта.
Бретон
29

Вы можете использовать объекты вместо переключателей большую часть времени.

function getInnerText(o){
    return o === null? null : {
        string: o,
        array: o.map(getInnerText).join(""),
        object:getInnerText(o["childNodes"])
    }[typeis(o)];
}

Обновление: если вы обеспокоены тем, что дела, оцениваемые заранее, являются неэффективными (почему вы беспокоитесь об эффективности на раннем этапе разработки программы ??), тогда вы можете сделать что-то вроде этого:

function getInnerText(o){
    return o === null? null : {
        string: function() { return o;},
        array: function() { return o.map(getInnerText).join(""); },
        object: function () { return getInnerText(o["childNodes"]; ) }
    }[typeis(o)]();
}

Это более обременительно для типа (или чтения), чем для переключателя или объекта, но оно сохраняет преимущества использования объекта вместо переключателя, как подробно описано в разделе комментариев ниже. Этот стиль также делает более простым превращение этого в надлежащий «класс», как только он вырастет достаточно.

update2: с предлагаемыми расширениями синтаксиса для ES.next, это становится

let getInnerText = o -> ({
    string: o -> o,
    array: o -> o.map(getInnerText).join(""),
    object: o -> getInnerText(o["childNodes"])
}[ typeis o ] || (->null) )(o);
Бретон
источник
3
Вот как Python обходится без оператора switch.
outis
2
Проблема в том, что он всегда оценивает все случаи.
Корнель
@porneL это правда, но это дает некоторые преимущества: это логически чище: случаи - это строки, которые ищутся в хеш-таблице, а не выражения, каждое из которых должно оцениваться на равенство до тех пор, пока одно из них не вернет true. Таким образом, в то время как оценивается больше «значений», оценивается меньше «ключей». Объекты могут генерироваться динамически и модифицироваться для последующей масштабируемости, отражаться для печати пользовательского интерфейса или создания документов и даже заменяться динамической функцией «поиска», что лучше, чем копирование / вставка дел. Не возникает путаницы в отношении разрывов, провалов или значений по умолчанию. Может быть JSON сериализовано ...
Бретон
@porneL о да, и опять же, что касается масштабируемости, объект может даже легко быть выделен во внешнюю конфигурацию или файл данных, что несколько более просто, чем с помощью оператора switch. Но тривиально, если начинать с объекта, задуманного для начала с участием.
Бретон
я знаю, что это поздняя запись, но если у вас нет собственной логики проверки типов, когда массив будет работать с вашим примером? var arr = []; typeof arr; // object
Keeganwatkins
25

Обязательно используйте метод hasOwnProperty при переборе свойств объекта:

for (p in anObject) {
    if (anObject.hasOwnProperty(p)) {
        //Do stuff with p here
    }
}

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

Андреас Греч
источник
23

Закрытые переменные с открытым интерфейсом

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

var test = function () {
    //private members
    var x = 1;
    var y = function () {
        return x * 2;
    };
    //public interface
    return {
        setx : function (newx) {
            x = newx;
        },
        gety : function () {
            return y();
        }
    }
}();

assert(undefined == test.x);
assert(undefined == test.y);
assert(2 == test.gety());
test.setx(5);
assert(10 == test.gety());
Крис Макдональд
источник
1
это называется модульным шаблоном, как его окрестил Эрик Мираглиа на yuiblog.com/blog/2007/06/12/module-pattern. Я считаю, что название вводит в заблуждение, его следует называть шаблоном Singleton или чем-то в этом роде. Я мог бы также добавить, что открытые методы могут также вызывать другие открытые методы, используя объект this. Я постоянно использую этот шаблон в своем коде, чтобы все было организованно и чисто.
mikeycgto