Какие «скрытые возможности» JavaScript, по вашему мнению, должен знать каждый программист?
Убедившись в превосходном качестве ответов на следующие вопросы, я подумал, что пришло время спросить о JavaScript.
Несмотря на то, что JavaScript, возможно, является сейчас самым важным языком на стороне клиента (просто спросите Google), удивительно, насколько мало большинство веб-разработчиков понимают, насколько он действительно мощный.
Конечно, пессимист. :) Я бы подумал, что это вопрос сообщества. Кроме того, после того, как вы наберете определенное количество очков, все будет уменьшаться.
Достаточно справедливо - это не похоже, что вам «нужна» репутация! Думаю, у меня просто большая проблема с C# - мне не совсем подходит тип вопроса, для которого предназначен этот сайт.
Да, может быть, и нет, но я нашел, что знания в ответах отличные. Я думаю, вам было бы сложно представить среднему программисту на C# все это в одном месте, если бы не SO. Понадобились годы, чтобы составить такой же список с трудом завоеванным.
Мне нравится эта серия вопросов; Я думаю, что система «digg» для ответов лучше, чем «+1», которую вы видите на форумах. Намного легче понять, что сообщество считает самым важным. Я уверен, что это хорошая приманка для ссылок и для Google!
Я профессионально пишу JavaScript уже 10 лет и кое-чему научился из этой ветки. Спасибо, Алан!



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Функции в 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();
}
Есть три способа создавать функции в javascript: function sum (x, y, z) {return (x + y + z); } и var sum = new Function ("x", "y", "z", "return (x + y + z);"); другие способы.
Концепция функций как данных в моей книге определенно заслуживает особого внимания.
Я только что обновил образец, чтобы показать, как использовать «частную» функцию, которая существует только в рамках определенной функции.
new Function() такой же злой, как eval. Не использовать.
Я не думаю, что функция-как-данные === функция-как-первоклассный гражданин ... Да, вы можете делать некоторые манипуляции со строками + магию eval / new Function (). Но это сильно отличается от того, как это есть в Scala / Lisp.
не уверен, что это скрытая функция ... больше похоже на основную функцию.
@ Marius, Nicolas Очень мало случаев, в которых вы хотели бы использовать новую функцию. Хотя это лучше, чем eval (вы можете повторно использовать функцию вместо анализа кода каждый раз, и код не может получить доступ к локальным переменным), это все же не рекомендуется (например, это усложняет отладку)
Частные методы
У объекта могут быть частные методы.
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());
Педантизм: в JavaScript нет классов. Возможны функции, которые доступны членам объекта, но в остальном недоступны.
На самом деле это не частная функция - это скорее функциональная переменная в локальной области видимости.
Верно, но по всем оперативным определениям я могу думать об этом как о методе. Это блок кода с именем, который имеет доступ к состоянию экземпляра и может быть виден только этому экземпляру. Каково ваше определение частного метода?
Тот, который существует в определении класса, а не в экземпляре. В приведенном выше примере переменную функции calcFullName можно переназначить на что-то другое, и логика каждый раз прорабатывается заново.
Я согласен с Кейтом ... Зная, что технические характеристики квалифицируются как скрытая функция (или больше похожа на «малоизвестную функцию») JavaScript, ее использование для «частных методов» - это скорее взлом, чем что-либо еще.
Я не думаю, что вы можете назвать это методом, поскольку в каждом экземпляре объекта создается новая функция. Метод находится в прототипе и может быть изменен один раз, чтобы изменить все объекты. Добавьте к этому тот факт, что с этой техникой ваши объекты занимают гораздо больше памяти.
@Vince, это метод экземпляра. @Kieth, детали реализации различны для каждого языка. Следует учитывать, обеспечивает ли он необходимый уровень сокрытия информации, чтобы его можно было назвать конфиденциальным.
@ Зак, точно! После многих лет работы с объектно-ориентированными языками на основе классов легко забыть, что они являются лишь одной из реализаций объектно-ориентированных концепций. Конечно, различные библиотеки, которые пытаются втиснуть объектно-ориентированный объект на основе квазиклассов в JS, тоже не помогают ...
Зачем, черт возьми, вам нужны частные методы? В реальных объектно-ориентированных языках их нет (см. Smalltalk).
Просто интересно, есть ли у person1 юридический блог? ;-)
Нет, просто звучит как бла-бла-бла. Я все время использую это имя, когда преподаю.
Если вы настаиваете на том, чтобы называть его частными методами, ожидайте путаницы, когда настоящие и правильные классы будут реализованы в следующем стандарте. Хотя, думаю, мы привыкли объяснять подобные вещи в JAVA-Script. :П
+1 за задержанную ссылку на разработку
@travis - Я не смотрел шоу, но я задал тот же вопрос, когда наткнулся на имя этого персонажа. И я проверил; там является и блог!
любой язык с замыканиями и первоклассными функциями может это сделать. Я думаю, что не хватает смысла называть это «частным методом». более точным было бы «вы можете имитировать частные методы, объявив функцию внутри конструктора». где абстракция не работает: если вы объявите функцию в прототипе Person, она не сможет получить доступ к «закрытому методу» calcFullName. Называя его «частным методом», люди просто сбивают с толку и создают впечатление, будто в JavaScript есть ООП на основе классов.
-1, это не частный метод, люди по-прежнему могут вызывать calcFullName, если хотят.
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 будет изменена. В этом кроется ошибка. Больше информации на yuiblog.com/blog/2006/04/11/with-statement-considered-harmfu l
Если вы неправильно написали имя переменной, вы ввели ошибку. Период. Лично я никогда не использую with для подобных вещей, но это своего рода «скрытая функция», так что ...
Шог, возражения связаны не с ошибками в написании переменных, а с тем, чтобы посмотреть на блок кода и с уверенностью сказать, что делает любая конкретная строка в этом блоке. Поскольку объекты Javascript настолько динамичны, вы не можете точно сказать, какие свойства / члены он имеет в любой момент.
Хорошая точка зрения. Я исправлю свой ответ, чтобы отметить это.
Аминь - если бы я увидел выражение «with» в любом найденном мной JS, я бы исключил его и задал вопрос разработчику, написавшему его, чтобы убедиться, что он знает, почему его использовать нехорошо ... «скрытая функция?» Больше похоже на «отвратительную черту».
рассмотрим более сложную цепочку abcd с (abc) {d.foo = bar;} - это мощный инструмент, который по своей сути не подвержен ошибкам. Ключ в том, чтобы сократить корень на один уровень выше. И неправильное написание имени переменной? Вы вводите ошибку если вы сделаете это где бы, вы сделаете это, независимо от «с».
если нужно поменять половину? функция augment (a, b) {for (i in b) {a [i] = b [i]} return a}
Дуглас Крокфорд недавно сказал, что with - одна из худших частей JavaScript в .NET Rocks! подкаст.
@Chris: Крокфорд утверждал это в течение некоторого времени, и у него есть веские причины так говорить. Однако он предоставляет то, что не предусмотрено никакими другими языковыми конструкциями: способ прямого увеличения области разрешения имен (функции обеспечивают это косвенно). Используйте с осторожностью.
В большинстве случаев вы можете использовать циклы и что-то вроде jQuery.extend, чтобы получить тот же эффект.
Я согласен, что с () - зло. Еще одна вещь, которая может вас убедить, заключается в том, что вы не можете использовать с ним jslint или минификатор (например, компрессор YUI или Google Closure Compiler). Любая область, в которой появляется with (), не будет оптимизирована и не может быть проверена на предмет области видимости переменной (потому что вы не знаете область действия переменной до времени выполнения).
да, пожалуйста, не используйте с. он нарушает лексическую область видимости, сбивает код с толку и т. д.
Это скрыто по какой-то причине. Никто не должен знать об этом, и никто никогда не должен никому рассказывать об этом, если они это сделали.
кроме того, блок кода, написанный с помощью "with", всегда медленнее, чем старый добрый способ
@gion: это в значительной степени деталь реализации - например, это не обязательно верно в (более старых версиях) IE. Для оптимизации with прилагается мало усилий (по очевидным причинам).
@ Shog9: Не деталь реализации: способ определения with делает его неоптимизируемым. Эта презентация содержит информацию о том, почему with работает медленно. В более старых версиях IE все операторы были медленными, поэтому with не был медленнее других.
Вы можете получить доступ к свойствам объекта с помощью [] вместо .
Это позволяет вам искать свойство, соответствующее переменной.
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 - зло, хотя и редко необходимо
Я никогда не использую eval и помню, когда обнаружил это. Это меня очень обрадовало.
Таким образом, к свойствам объекта можно получить доступ как через точечную, так и через подстрочную нотацию.
Интересно отметить, что ссылки на точки на самом деле являются синтаксическим сахаром для скобок. foo.bar, в любом случае, согласно спецификации, ведет себя так же, как foo["bar"]. также обратите внимание, что все является строковым свойством. даже когда вы выполняете доступ к массиву, array[4], 4 преобразуется в строку (опять же, по крайней мере, в соответствии со спецификацией ECMAScript v3)
Думаю, это должен знать каждый JS-программист.
«Методы расширения в JavaScript» через свойство прототипа.
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");
Обычно это считается плохой идеей, потому что другой код (не ваш) может делать предположения об объекте Array.
Также обычно считается плохой идеей делать предположения об объекте Array. :(
Мммм .. javascript 1.6 дополнительные массивы? индекс? звонить в колокола?
@Breton: Это не что-то конкретное для класса Array, это просто пример. Я использую это для расширения функции new Date (). ToString (); метод, позволяющий использовать строку маски. Объект Любой может быть расширен, и все его экземпляры получат новый метод.
Плохая идея: perfectionkills.com/whats-wrong-with-exnding-the-dom
@Mathias: дело не в DOM.
Назначение значений по умолчанию для переменных
Вы можете использовать логический оператор или оператор || в выражении присваивания, чтобы указать значение по умолчанию:
var a = b || c;
Переменная a получит значение c, только если b будет фальшивка (если это 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 5-е издание:
Заявление debugger
Описано на: § 12.15 Оператор отладчика
Этот оператор позволяет вам вставить точки останова программно в ваш код, просто:
// ...
debugger;
// ...
Если отладчик присутствует или активен, он немедленно остановится прямо в этой строке.
В противном случае, если отладчик отсутствует или активен, этот оператор не имеет наблюдаемого эффекта.
Многострочные строковые литералы
Описано на: § 7.8.4 Строковые литералы
var str = "This is a \
really, really \
long line!";
Вы должны быть осторожны, потому что символ рядом с \должен является ограничителем строки, например, если у вас есть пробел после \, код будет Смотреть точно так же, но вызовет SyntaxError.
Нет, если он нулевой, если он считается ложным. а = 0 || 42; даст вам 42. Это сравнимо с Python или, а не с C# ?? оператор. Если вам нужно поведение C#, сделайте a = (b === null)? c: b;
Он также работает в Visual Studio, если вы разрабатываете на ASP.NET :)
Я бы хотел, чтобы это было правильно || только для неопределенных. Меня это сегодня укусило за 0, так как я хотел создать эмуляцию перегруженного метода, чтобы последний аргумент был необязательным, а вместо него использовалось значение по умолчанию.
+1 этот трюк используется сниппетом Google Analytics по умолчанию. `var _gaq = _gaq || []; `; он не позволяет чрезмерно усердным пользователям перезаписывать свою работу.
Я не знал о технике многострочного строкового литерала. Это здорово, спасибо.
Удивительно, как много людей не понимают, что это тоже объектно-ориентированный подход.
Я думаю, что это в значительной степени связано с тем, что объектно-ориентированность Javascript полностью основана на прототипах, а не на объектно-ориентированных объектах на основе классов наиболее популярных языков. Кроме того, в JS мало синтаксических тонкостей объектно-ориентированного программирования, что может сильно оттолкнуть, особенно если вы учитесь.
Странный. Как еще они интерпретировали бы document.writeln или document.getElementsbyTagName?
Потому что я считаю, что большинство разработчиков используют Javascript в браузерах и не заботятся о том, насколько это объектно-ориентированный подход. они используют базовые функции, чтобы что-то делать в браузерах, если они активно используют серверные коды.
JavaScript не имеет области действия блока (но у него есть закрытие, так что назовем его даже?).
var x = 1;
{
var x = 2;
}
alert(x); // outputs 2
Это хорошо. Это действительно важное отличие от большинства C-подобных языков.
Вы всегда можете сделать "var tmp = function () {/ * block scope * /} ();". Синтаксис уродливый, но работает.
Или вы можете использовать let, если это только Firefox: stackoverflow.com/questions/61088/…
или просто: (function () {var x = 2;}) (); предупреждение (тип x); //неопределенный
@Pim: JSLint говорит: «Переместите вызов в скобки, содержащие функцию». Наряду с "Ожидается ровно один пробел между 'function' и '('.".
@ Hello71, что именно хочет JSLint?
@Pim: (function () { var x = 2; }());
Фактически, это так: var v = "переменная в области функций"; попробуйте {бросить "брошенную переменную"; } catch (v) {v = "локальная переменная"; console.info (v); } console.info (v);
Как насчет закрытие в 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);
Я не уверен, но могу ли вернуть (numberToCheck> lowerBound)? истина: ложь; просто стать return (numberToCheck> lowerBound); просто пытаюсь расширить свое понимание ...
Я бы сказал, что анонимные функции в C# эквивалентны закрытию, а не наоборот :)
Замыкания и анонимные функции - это отдельные, разные концепции. Эти функции могут быть созданы без имени, имея анонимные функции. То, что переменная в области «создания» связана с созданной функцией, является закрытием. Короче говоря, замыкание больше похоже на скрытую глобальную переменную.
Это правда. Только когда анонимные методы используют переменную из области создания, это похоже на закрытие. Я обновил английский по ответу. Это все еще оставляет желать лучшего, но я не могу найти правильный английский.
Я не думаю, что это лучший или самый простой для понимания пример того, что такое закрытие. Просто говорю. Смысл закрытия в том, что даже когда кажется, что группа переменных «выходит за пределы области видимости», они все еще могут оставаться доступными для функции, которая изначально была определена в этой области. В приведенном выше примере это означает, что переменная lowerBound все еще доступна этой внутренней анонимной функции, даже когда внешняя функция buildGreaterThanFunction завершается.
@thomasrutter +1 - я использую замыкания для «захвата» области видимости для последующего использования (скажем, в setTimeout или обработчике событий), например function testFunc(){ var str='this will be out of scope when timeout event occurs'; setTimeout(function(){ return function(input){ alert(input); }(str); }, 5000); return; }
Вы также можете расширять (наследовать) классы и переопределять свойства / методы, используя упомянутую цепочку прототипов ложка16.
В следующем примере мы создаем класс 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.
Функции являются объектами и поэтому могут иметь свойства.
fn = function(x) {
// ...
}
fn.foo = 1;
fn.next = function(y) {
//
}
Это очень полезный совет. Например, вы можете установить значения по умолчанию как свойство функции. Например: myfunc.delay = 100; Затем пользователи могут изменить значение по умолчанию, и все вызовы функций будут использовать новое значение по умолчанию. Например: myfunc.delay = 200; myfunc ();
Полезно ... и опасно!
Выглядит небрежно, зачем использовать это вместо переменной?
@instantsetsuna: Зачем нужна еще одна переменная отдельный? Как обычно, это сводится к «использовать его, когда это уместно / полезно» ;-)
Я мог бы процитировать большую часть прекрасной книги Дугласа Крокфорда. JavaScript: хорошие стороны.
Но я возьму для вас только один, всегда используйте === и !== вместо == и !=.
alert('' == '0'); //false
alert(0 == ''); // true
alert(0 =='0'); // true
== не является транзитивным. Если вы используете ===, он даст false для
все эти заявления, как и ожидалось.
Большинство людей считают это одной из худших частей языка, когда впервые видят ее (3 равно !?). Я думаю, что знание об этом важно, потому что это заставляет вас фиксировать в памяти, что JavaScript динамически типизируется.
Да, я согласен - Крокфорд говорит о «худшей части» приложения. Жаль, что они не смогли просто заставить "==" вести себя так, как ожидалось.
Жалко, что так много людей думают, что Крокфорд всезнающий. Конечно, этот парень попадает в цель с большинством своих критических замечаний, но я не останавливаюсь перед тем, чтобы дать его материалу широкую поддержку, как это делают многие разработчики ...
Я мог неправильно понять; но есть ли у них что-то особенное, что, по вашему мнению, он не соответствует действительности?
Я повторяю предупреждение Джейсона. Книга сама по себе очень интересна и дает много хороших советов, но DC слишком убежден, что его образ действий - единственно правильный, а все остальное «несовершенно». Если вам нужны примеры, посмотрите его ответы в JSLint Yahoo Group.
по всем вышеперечисленным причинам, и особенно из-за его комментария к "этому"
Может ли кто-нибудь объяснить мне, почему 0 == '' верно в javascript? Что именно означает нетранзитивное сравнение на равенство?
Нетранзитивно: '' == 0 и 0 == '0', но ''! = '0'. Если бы он был транзитивным, «» равнялось бы «0». 0 == '', потому что преобразование типов происходит автоматически, и некоторые архитекторы JS считали, что '' следует преобразовать в 0.
Используйте === вместо == - хороший совет, если вас смущает динамический набор текста и вы просто хотите, чтобы оно было «действительно» равным. Те из нас, кто понимает динамическую типизацию, могут продолжать использовать == в ситуациях, когда мы знаем, что хотим выполнить приведение, например, 0 == '' или 0 == '0'.
Ну, == и === не о динамической типизации. == вводит принуждение, а это совсем другое дело. Если вы знаете, что хотите преобразовать в строку / число / и т. д., Вам следует сделать это явно.
Лучше всего использовать == при тестировании на undefined, потому что undefined == null, но undefined! == null. Я редко видел что-либо, где я хотел бы различать undefined и null.
@zlik: Полностью согласен. В некотором смысле он гениален, но не бойтесь переписывать свой код. В частности, его код создания PHP JSON ограничен в том, сколько он может собирать.
Я думаю, что самая страшная часть == - это '\n\t\r ' == 0 => true ...: D
Я вижу, что многие люди до сих пор неправильно понимают JavaScript :)
Крокфорд не идеален, но он произвел революцию в JS и сделал его удобным языком для всех нас. Он почти всегда подкрепляет свои мнения вескими основаниями. Если я не согласен с ним в 2% случаев, я не думаю, что это имеет большое значение. Когда вы не согласны, легко настроить набор правил JSLint с комментариями.
В Javascript есть статические переменные внутри функций:
function someFunction(){
var Static = arguments.callee;
Static.someStaticVariable = (Static.someStaticVariable || 0) + 1;
alert(Static.someStaticVariable);
}
someFunction() //Alerts 1
someFunction() //Alerts 2
someFunction() //Alerts 3
Он также имеет статические переменные внутри объектов:
function Obj(){
this.Static = arguments.callee;
}
a = new Obj();
a.Static.name = "a";
b = new Obj();
alert(b.Static.name); //Alerts b
Отлично! Мне пришлось сделать тройной взгляд на это, пока я не понял, что вы определяете свойство самой функции. Мне пришлось запустить alert (someFunction.someStaticVariable); чтобы он утонул.
Я думаю, вы искажаете способность функций иметь свойства в целом. То, что вы говорите, технически верно, но как побочный эффект для функций, являющихся объектами первого порядка в языке.
Согласен, это немного вводит в заблуждение. "arguments.callee" - это просто ссылка на вызванную функцию. Во втором примере a.Static === b.Static === Obj
В теле функции вы можете просто написать someFunction.someStaticVariable вместо arguments.callee.someStaticVariable. Читает чище.
undefined не определен. Итак, вы можете сделать это:
if (obj.field === undefined) /* ... */
«undefined» не является зарезервированным словом, так что это потенциально может потерпеть неудачу, если у вас есть переменная с таким именем.
если у вас есть переменная с таким именем, вы уже потерпели неудачу
jsight, я хочу проголосовать за комментарии: P
Вы даже можете изменить значение глобального undefined.
Вы можете заменить «undefined» выражением «void ()». Преимущество в том, что void нельзя переопределить.
Действительно? Когда я пытаюсь это сделать, я получаю синтаксическую ошибку в FF и Chrome.
@JW void не является функцией. void 0 или void(0) можно безопасно использовать в качестве замены undefined.
Как уже указывал Мариус, в функциях могут быть общедоступные статические переменные.
Я обычно использую их для создания функций, которые выполняются только один раз, или для кеширования некоторых сложных результатов вычислений.
Вот пример моего старого «одноэлементного» подхода:
var singleton = function(){
if (typeof arguments.callee.__instance__ == 'undefined') {
arguments.callee.__instance__ = new function(){
//this creates a random private variable.
//this could be a complicated calculation or DOM traversing that takes long
//or anything that needs to be "cached"
var rnd = Math.random();
//just a "public" function showing the private variable value
this.smth = function(){ alert('it is an object with a rand num=' + rnd); };
};
}
return arguments.callee.__instance__;
};
var a = new singleton;
var b = new singleton;
a.smth();
b.smth();
Как видите, в обоих случаях конструктор запускается только один раз.
Например, я использовал этот подход еще в 2004 году, когда мне пришлось создать модальное диалоговое окно с серым фоном, которое покрыл всю страницу (что-то вроде Лайтбокс). Интернет Explorer 5.5 и 6 имеют самый высокий контекст стекирования для элементы <select> или <iframe> из-за их «оконная» природа; поэтому, если страница содержала элементы выбора, единственный способ скрыть их - создать iframe и расположите его «вверху» страницы. Итак, весь сценарий был довольно сложный и немного медленный (использовался фильтр: выражений для установки непрозрачности закрывающего окна iframe). В В скрипте "shim" был только один метод ".show ()", который создавал прокладку только один раз и кешировали в статической переменной :)
Меня просто восхищает то, как JavaScript работает с Date ()!
function isLeapYear(year) {
return (new Date(year, 1, 29, 0, 0).getMonth() != 2);
}
Это действительно «скрытая особенность».
Обновлено: удалено "?" условие, предложенное в комментариях для политкорректности. Было: ... новая дата (год, 1, 29, 0, 0) .getMonth ()! = 2? правда: ложь ... Подробности смотрите в комментариях.
Это тоже слишком сложно. Я не понимаю, как можно писать cond ? true : false (или наоборот) и не замечать, насколько это идиотично.
Для наглядности строка должна быть return! (New Date (year, 1, 29, 0, 0) .getMonth () == 2); Или, на самом деле, это должно быть return new Date (year, 1, 29, 0, 0) .getMonth ()! = 2;
На самом деле это обозначение не так уж идиотично, как может показаться, когда «неопределенные» результаты могут быть включены в дополнительные данные. Я взял этот пример из реально работающей системы, и изменение его формы на «Политически корректную» полностью тормозит систему :)
@Thevs: Я не понимаю, как изменение формы в этом случае что-нибудь сломает. И старый код, и новый код, похоже, возвращают реальные логические значения (истина / ложь) вместо того, чтобы просто возвращать объекты, которые могут быть или не быть правдивыми.
Ты прав. Виноват. В моем исходном коде было «? 1: 0», а затем возвращаемое значение было добавлено к 28, чтобы определить количество дней в феврале. Изменение его для возврата логического значения vlaue нарушило мой код. В случае «? False: true» оба фрагмента кода эквивалентны.
Также секретные возвращаемые значения методов "set ...": var d = new Date ((new Date ()). SetHours (0, 0, 0, 0));
Это должно оставаться скрытым. По крайней мере, пример использования, потому что он неэффективен. Используйте его только в тестовом наборе для проверки более быстрая реализация, например !(year % 4 != 0 || (year % 100 == 0 && year % 400 != 0)).
Все функции на самом деле являются экземплярами встроенного типа Функция, который имеет конструктор, который принимает строку, содержащую определение функции, поэтому вы можете фактически определять функции во время выполнения, например, объединяя строки:
//e.g., createAddFunction("a","b") returns function(a,b) { return a+b; }
function createAddFunction(paramName1, paramName2)
{ return new Function( paramName1, paramName2
,"return "+ paramName1 +" + "+ paramName2 +";");
}
Кроме того, для пользовательских функций Function.toString () возвращает определение функции в виде буквальной строки.
Однако обычно в этом нет необходимости. В вашем примере вы можете просто сказать: return function (paramName1, paramName2) {return paramName1 + paramName2; }
Это был просто надуманный пример. Это по-прежнему приятная малоизвестная функция. Тем не менее, вы можете создавать макросы, например, раскручивать цикл.
Вам не нужно определять какие-либо параметры для функции. Вы можете просто использовать объект функции 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
Ого! Действительно?! Честно говоря, я понятия не имел. Этому сайту действительно нужна функция "избранных ответов". Я бы обязательно добавил это.
Однако стоит отметить, что, хотя аргументы действуют как массив, это не настоящий массив javascript - это просто объект. Таким образом, вы не можете использовать join (), pop (), push (), slice () и так далее. (Вы можете преобразовать его в настоящий массив, если хотите: "var argArray = Array.prototype.slice.call (arguments);")
Также стоит отметить, что доступ к объекту Arguments является относительно дорогостоящим - лучшие примеры - в ночных сборниках Safari, Firefox и Chrome, где простая ссылка на объект arguments делает вызов функции намного медленнее, например. если (ложные) аргументы; повредит перфорацию.
Точно так же у arguments есть свойство callee, которое является самой текущей функцией. Это позволяет делать рекурсию с анонимными функциями, круто!
По крайней мере, включите в сигнатуру функции аргумент с именем «varargs» или что-то еще, чтобы читатели знали, что ваша функция имеет арность.
Это круто, но есть ли веская причина, по которой вы хотите это сделать? Почему бы просто не передать массив?
@Nathan «f (x, y, z)» выглядит лучше, чем «f ([x, y, z])».
@Nathan Этот arguments позволяет вам изменять любые существующие функции по своему усмотрению, даже не зная аргументов. Попробуйте Google для Memoize Javascript, и вы поймете, как это можно использовать.
@ Винсент Роберт: обратите внимание, что arguments.callee устарел.
Следует отметить, что это не уникально для JavaScript и возможно во многих других языках.
@ken: developer.mozilla.org/en/JavaScript/Reference/… "JavaScript 1.4: устаревшие аргументы, arguments.callee и arguments.length как свойства экземпляров Function; сохранены аргументы как локальная переменная функции и arguments.callee и arguments.length как свойства этой переменной".
Все объекты в Javascript реализованы как хэш-таблицы, поэтому к их свойствам можно получить доступ через индексатор и наоборот. Также вы можете перечислить все свойства с помощью оператора для / в:
var x = {a: 0};
x["a"]; //returns 0
x["b"] = 1;
x.b; //returns 1
for (p in x) document.write(p+";"); //writes "a;b;"
Кроме того, имена свойств являются строками, и если в строке есть символ, который не позволяет использовать ее через точечную нотацию, к ней можно получить доступ через индексную нотацию. Например, свойство объекта x ['funky prop'] не может быть доступно как x.funky prop; x ['funky.prop'] недоступен как x.funky.prop;
Только не забудьте проверить имена свойств с помощью "object.hasOwnProperty (propertyName)" перед их использованием из цикла for-in, иначе вы столкнетесь с некоторыми нежелательными вещами;)
@Beska: Я думаю, что доработка будет такой: for (p in x) if (x.hasOwnProperty (p)) document.write (p + ";"); Это позволяет обойти проблемы, при которых добавление новых свойств к прототипу x приведет к тому, что в также будет перечислять их, что может быть нежелательным поведением.
Вы можете использовать оператор в, чтобы проверить, существует ли ключ в объекте:
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
Если вы находите объектные литералы слишком уродливыми, вы можете объединить их с подсказкой функции без параметров:
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
Не настолько умно, что проверяет наличие ключа, а не значение. x в списке; работает только потому, что x [1]! = null, а не потому, что там есть значение 1.
Я пока не использовал эту технику, поэтому забыл, что раньше я действительно использовал объектные литералы. Спасибо за исправление.
Также будьте осторожны: оператор in также проверяет цепочку прототипов! Если кто-то поместил свойство с именем «5» в Object.prototype, второй пример вернет true, даже если вы вызвали «5 в списке (1, 2, 3, 4)» ... Лучше использовать hasOwnProperty method: list (1, 2, 3, 4) .hasOwnProperty (5) вернет false, даже если Object.prototype имеет свойство '5'.
Для самого общего решения, которое может проверить, есть ли у объекта собственное свойство, даже если оно названо «hasOwnProperty», вы должны полностью перейти к: Object.prototype.hasOwnProperty.call (object, name) ;
Лучшее использование для этого (вместе с hasOwnProperty) - проверка с тремя состояниями ... queue[item] = false; будет делать (item in queue) === true, но все еще сохраняет (queue[item] === false) ... пригодится в некоторых ситуациях (и выглядело странным уборщиком, использующим hasOwnProperty)
@Kris, если только кто-то не перезапишет Object.prototype.hasOwnProperty;)
JavaScript использует простой литерал объекта:
var x = { intValue: 5, strValue: "foo" };
Таким образом создается полноценный объект.
JavaScript использует объектную ориентацию на основе прототипов и предоставляет возможность расширять типы во время выполнения:
String.prototype.doubleLength = function() {
return this.length * 2;
}
alert("foo".doubleLength());
Объект делегирует весь доступ к атрибутам, которые сам не содержит, своему «прототипу», другому объекту. Это можно использовать для реализации наследования, но на самом деле он более мощный (даже если более громоздкий):
/* "Constructor" */
function foo() {
this.intValue = 5;
}
/* Create the prototype that includes everything
* common to all objects created be the foo function.
*/
foo.prototype = {
method: function() {
alert(this.intValue);
}
}
var f = new foo();
f.method();
Странно, что никто не подумал о JSON !?
Может быть, для некоторых это немного очевидно ...
Установите Firebug и используйте console.info («привет»). Намного лучше, чем использовать random alert ();, который, как я помню, делал много лет назад.
Только не забудьте удалить операторы консоли перед тем, как передать свой код другим пользователям, у которых может не быть установлен Firebug.
журнал функций (сообщение) {если (консоль) console.info (сообщение) еще предупреждение (сообщение)}
Еще лучше, ставьте перед операторами журнала ';;;' а затем minify позаботится об этом за вас. (По крайней мере, модуль Perl, который я использую, имеет эту функцию и утверждает, что это обычное дело.)
Джош: Это не сработает, поскольку консоль не определена. Вы можете проверить typeof console! == "undefined" или window.console.
Всегда включайте: if (typeof ('console') == 'undefined') {console = {log: function () {}}; } то вы можете продолжать использовать console.info, и он просто ничего не делает.
window.LOG = (typeof (console)! = 'undefined')? console.info: function () {; } // Позволяет также использовать параметры.
Джош: предлагаю перейти на журнал функций (msg) {if (console.info) console.info (msg) else alert (msg)}
Или еще лучше: function log (msg) {if (console) console.info (msg) else alert (msg + "\ nHey! Установите Firebug, чтобы не получать это раздражающее сообщение!"); }
чтобы облегчить день отладки, я обычно использую цитаты из моих любимых комедийных шоу, например console.info («4 наан, Джереми - это безумие!»)
Кроме того, это доступно в Chrome / Safari / IE без Firebug. Но семантика может немного отличаться (чего в мире браузеров нет?)
Это прекрасно работает: // материал console.info var alertAnyways = true; if (typeof console === "undefined" || typeof console.info === "undefined") {var console = {}; если (alertAnyways) {console.info = функция (сообщение) {предупреждение (сообщение); }; } else {console.info = function (msg) {// ничего не делать}; }} var cl = функция (сообщение) {console.info (сообщение); };
@gregmac: есть опечатка: typeof ('console') is 'string', поэтому никогда не будет 'undefined'.
Один из моих любимых - проверка типа конструктора:
function getObjectType( obj ) {
return obj.constructor.name;
}
window.onload = function() {
alert( getObjectType( "Hello World!" ) );
function Cat() {
// some code here...
}
alert( getObjectType( new Cat() ) );
}
Так что вместо устаревшего старого [Object object], который вы часто получаете с ключевым словом typeof, вы можете получить реальные типы объектов, основанные на конструкторе.
Другой - использование переменных аргументов как способ «перегрузить» функции. Все, что вы делаете, это используете выражение для определения количества аргументов и возврата перегруженного вывода:
function myFunction( message, iteration ) {
if ( arguments.length == 2 ) {
for ( i = 0; i < iteration; i++ ) {
alert( message );
}
} else {
alert( message );
}
}
window.onload = function() {
myFunction( "Hello World!", 3 );
}
Наконец, я бы сказал сокращенное обозначение оператора присваивания. Я узнал об этом из источника фреймворка jQuery ... по-старому:
var a, b, c, d;
b = a;
c = b;
d = c;
Новый (сокращенный) способ:
var a, b, c, d;
d = c = b = a;
Хорошо повеселиться :)
Не рекомендуется полагаться на свойство конструктора, поскольку оно изменяемое и ненадежное. Как только вы начнете экспериментировать со свойством prototype, становится очень легко уничтожить значение .constructor.
Этот очень скрыт и полезен только изредка ;-)
Вы можете использовать цепочку прототипов для создания объекта, который делегирует другому объекту, не изменяя исходный объект.
var o1 = { foo: 1, bar: 'abc' };
function f() {}
f.prototype = o1;
o2 = new f();
assert( o2.foo === 1 );
assert( o2.bar === 'abc' );
o2.foo = 2;
o2.baz = true;
assert( o2.foo === 2 );
// o1 is unchanged by assignment to o2
assert( o1.foo === 1 );
assert( o2.baz );
Это касается только «простых» значений на o1. Если вы изменяете массив или другой объект, прототип больше не «защищает» исходный объект. Будьте осторожны, когда у вас есть {} или [] в определении / прототипе класса.
Метки времени в JavaScript:
// Usual Way
var d = new Date();
timestamp = d.getTime();
// Shorter Way
timestamp = (new Date()).getTime();
// Shortest Way
timestamp = +new Date();
Кратчайший способ: отметка времени = + новая дата;
Самый короткий способ умный, но трудный для понимания, поскольку можно подумать, что вы хотели написать + =, но по ошибке написали = +
@ Рене: Аргх. Я даже запуталась твоим собственным утверждением, и «зафиксировала» ответ ...
Еще короче / менее загадочно: просто используйте «timestamp = new Date ();». Вы можете вычесть отметки времени, потому что они имеют функцию valueOf (), которая возвращает целочисленную отметку времени. Если вы хотите использовать метку времени как целое число, используйте либо «+ метка времени» (коротко, но загадочно), либо «timestamp.valueOf ()».
Конечно, при правильном форматировании / интервале такой двусмысленности можно избежать. Почему так сложно использовать правильный интервал? НЕ пишите «timestamp = + new Date ();», конечно, это сбивает с толку. Вместо этого напишите «timestamp = + new Date ();».
@ken: Кто определяет «правильное»? Кому-то нравится меньше пробелов, кому-то нравится больше. Может быть подходящий способ для организации или проекта, но не обязательно глобально.
@icktoofay: Найдите мне спецификацию стандартов кодирования где угодно, которая диктует, поддерживает или оправдывает код, написанный как «timestamp = + new Date ();». В качестве альтернативы, пропустите эту строку через любое из огромного количества средств форматирования JS (также известных как beautifiers), и я хочу, чтобы ни один из них не оставил его без места. «Правильный» может быть субъективным в теории, но на практике очень очевидно, что является «правильным», а что - нет.
@ken: Да, конечно, timestamp=+new Date(); не очень понятен и, вероятно, не будет поддерживаться никакими рекомендациями по кодированию, но something=123; не редкость и, безусловно, может быть «подходящим» для людей, которые привыкли к этому стилю.
Источник Date.now(): developer.mozilla.org/en/JavaScript/Reference/Global_Objects /…
Function.toString () (неявно):
function x() {
alert("Hello World");
}
eval ("x = " + (x + "").replace(
'Hello World',
'STACK OVERFLOW BWAHAHA"); x("'));
x();
Имейте в виду, что toSource() не является стандартом. В вашем примере у вас есть x + "", который обычно должен быть эквивалентен x.toString(). Полагаю, вы хотели вместо этого использовать x.toSource()?
ааа, вы меня поймали. Я вызываю toString, а не ToSource, но согласно developer.mozilla.org/En/… - Function.ToString () декомпилирует функцию.
Да, toSource полезен для отладки в Mozilla. Но, как вы заметили, неявный toString может делать классные (если не сумасшедшие) вещи.
Частные переменные с общедоступным интерфейсом
Он использует небольшую хитрость с определением функции автоматического вызова. Все внутри возвращаемого объекта доступно в общедоступном интерфейсе, а все остальное является частным.
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());
это называется модульным шаблоном, как было названо Эриком Мираглией из yuiblog.com/blog/2007/06/12/module-pattern. Я действительно думаю, что это название вводит в заблуждение, его следует называть шаблоном синглтона или чем-то в этом роде. Я мог бы также добавить, что общедоступные методы также могут вызывать другие общедоступные методы с помощью объекта this. Я постоянно использую этот шаблон в своем коде, чтобы все было организовано и чисто.
Методы (или функции) могут вызываться для объекта, который не относится к тому типу, для работы с которым они были разработаны. Это замечательно для вызова собственных (быстрых) методов для пользовательских объектов.
var listNodes = document.getElementsByTagName('a');
listNodes.sort(function(a, b){ ... });
Этот код дает сбой, потому что listNodes не является Array
Array.prototype.sort.apply(listNodes, [function(a, b){ ... }]);
Этот код работает, потому что listNodes определяет достаточно свойств, подобных массиву (длина, оператор []), для использования sort().
Числа - тоже объекты. Итак, вы можете делать такие классные вещи, как:
// 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
МОЙ БОГ! Я не знал о toString (radix) ...
Эта реализация times неэффективна: Math.floor вызывается каждый раз, а не только один раз.
Вы можете сделать почти все, что угодно, в круглых скобках, если разделите операторы запятыми:
var z = ( x = "can you do crazy things with parenthesis", ( y = x.split(" "), [ y[1], y[0] ].concat( y.slice(2) ) ).join(" ") )
alert(x + "\n" + y + "\n" + z)
Выход:
can you do crazy things with parenthesis
can,you,do,crazy,things,with,parenthesis
you can do crazy things with parenthesis
Вы можете, но я уверен, что каждый здравомыслящий парень, занимающийся JavaScript, захочет, чтобы вы нарисовали и четвертовали.
Если вы хотите, чтобы в обфускаторе это было действительно неясно, используйте китайские или другие символы Юникода: function 喂 (我) {alert (我)};
Понятие правдивых и ложных ценностей. Вам не нужно делать что-то вроде
если (someVar === undefined || someVar === null) ...
Просто сделайте:
если (! someVar).
Каждому значению соответствует логическое представление.
С этим нужно быть осторожнее. Ноль и пустая строка также преобразуются в false.
как следствие, вы должны использовать if (!! x) для проверки «истинности» вместо if (x)
Вы можете выполнить метод объекта для любого объекта, независимо от того, есть ли у него этот метод или нет. Конечно, это может не всегда работать (если метод предполагает, что у объекта есть что-то, чего нет), но это может быть чрезвычайно полезно. Например:
function(){
arguments.push('foo') // This errors, arguments is not a proper array and has no push method
Array.prototype.push.apply(arguments, ['foo']) // Works!
}
Я не уверен, нравится ли мне это использование или мне следует отшатнуться от страха !? :)
также вызовите - Array.prototype.push.call (arguments, 'foo' / *, arg2, arg3 и т. д. * /);
Joose - хорошая объектная система, если вам нужен объектно-ориентированный объект на основе классов, который похож на CLOS.
// Create a class called Point
Class("Point", {
has: {
x: {
is: "rw",
init: 0
},
y: {
is: "rw",
init: 0
}
},
methods: {
clear: function () {
this.setX(0);
this.setY(0);
}
}
})
// Use the class
var point = new Point();
point.setX(10)
point.setY(20);
point.clear();
Все ваши «скрытые» функции находятся прямо здесь, в вики Mozilla: http://developer.mozilla.org/en/JavaScript.
Есть Справочник по основному JavaScript 1.5, что нового в JavaScript 1.6, что такое новое в JavaScript 1.7, а также что новое в JavaScript 1.8. Просмотрите все те примеры, которые действительно работают и ошибочны в нет.
Посещение:
Вставьте этот код JavaScript в адресную строку своего веб-браузера:
Наслаждайтесь дискотекой на JavaScript :-p
Очень хорошо! Я люблю это :-)
Кто-то назвал бы это делом вкуса, но:
aWizz = wizz || "default";
// same as: if (wizz) { aWizz = wizz; } else { aWizz = "default"; }
Тройной оператор можно объединить в цепочку, чтобы он действовал как Scheme (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";
Также хорошо работает с рекурсией :)
Мне нравится указанный вами синтаксис предиката. Я никогда не думал о таком соединении. аккуратный.
Эээ ... В JavaScript есть инструкция switch (). :-)
Я не большой поклонник операторов switch - это артефакт C, а не функционального программирования. В моем примере для оператора switch все равно потребуются три отдельных оператора, все из которых начинаются с «foo =» - очевидное ненужное повторение.
О боже ... Так что же именно за скрытая особенность? Возможность искажать код, чтобы он выглядел как другой язык? (Думаю, то же самое они сделали и с библиотекой Prototype :)
@harto: Я думаю, что JavaScript достаточно гибок и не имеет определенного стиля. Не говорите мне, что вы никогда используете в своем коде элементы из других языков. Это тоже дело вкуса.
@harto: трюк состоит в том, чтобы использовать тернарный оператор для выполнения if-else в выражениях. Это действительно здорово, что та же языковая функция, которая выполняет эквивалент if, также может выполнять if-else без изменений.
Я, например, приветствую тернарный оператор.
При перечитывании я хотел бы отметить, что это не «превращение кода в другой язык», а на самом деле упрощение семантического значения кода: когда вы пытаетесь сказать «установите foo на одно из трех things », это утверждение должно начинаться с« foo = ... », а не« if ».
Это здорово, но как это спрятать? C существует уже давно, и мы делаем это всегда.
Тернарная цепочка возможна во многих языках, основанных на C, в этом нет ничего специфического для JavaScript.
С верхней части моей головы...
Функции
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"
То, что вы упомянули ... Реализованы ли они во всех браузерах?
Нет. Я почти уверен, что большинство из них у Mosaic отсутствует.
Да, функции javascript реализованы во всех основных браузерах (IE6 / 7, FF2 / 3, Opera 9+, Safari2 / 3 и Chrome). document.querySelectorAll пока поддерживается не во всех браузерах (это версия JQuery $ () от W3C и $$ () от Prototype)
arguments.callee устарел и будет вызывать исключение в ECMAScript 5.
не совсем так. Ключ объекта не может (или, скорее, не должен) использовать строку hasOwnProperty в качестве имени, так как это переопределит встроенный метод объекта.
Вы можете на лету переопределить большие части среды выполнения, например изменение конструктора Array или определение undefined. Не то чтобы вам следовало, но это может - мощная функция.
Несколько менее опасная форма этого - добавление вспомогательных методов к существующим объектам. Вы можете, например, сделать IE6 "родной" поддержкой indexOf на массивах.
Вот несколько интересных вещей:
NaN с чем-либо (даже с NaN) всегда неверно, включая ==, < и >.NaN означает не число, но если вы спросите тип, он фактически вернет число.Array.sort может выполнять функцию компаратора и вызывается драйвером, подобным быстрой сортировке (зависит от реализации).$0, $1, $2 с помощью регулярного выражения.null не похож ни на что другое. Это ни объект, ни логическое значение, ни число, ни строка, ни undefined. Это немного похоже на «альтернативу» undefined. (Примечание: typeof null == "object")this выдает безымянный объект [Global].var вместо того, чтобы просто полагаться на автоматическое объявление переменной, дает среде выполнения реальный шанс оптимизировать доступ к этой переменной.with разрушит такие оптимизацииbreak. Петли могут быть помечены и использоваться в качестве мишени для continue.undefined. (зависит от реализации)if (new Boolean(false)) {...} выполнит блок {...}[немного обновлено в ответ на хорошие комментарии; пожалуйста, смотрите комментарии]
null на самом деле (особый) объект. typeof null возвращает «объект».
Вы также можете получить объект [Global] откуда угодно, например так: var glb = function () {return this; } ();
Глобальный? Вы имеете в виду окно и себя?
Только что укусил NaN === NaN === false.
Глобальный объект в javascript в браузере - это объект окна. Когда в глобальной области действия выполняем: window.a == a;
Array.sort не реализован с использованием драйвера быстрой сортировки, потому что это необходимо, чтобы метод сортировки мог обрабатывать абсурдные вещи, такие как Math.random, используемый в качестве функции сортировки, или функции сравнения, которые они изменяют то, что они сравнивают. JavaScriptCore (например, WebKit) использует деревья AVL: - /
«Массивы не редкие» зависит от реализации. Если вы установите значение [1000] и посмотрите на [999], то да, это undefined, но это просто значение по умолчанию, которое вы получаете при поиске несуществующего индекса. Если вы отметили [2000], это тоже будет undefined, но это не значит, что вы еще выделили для него память. В IE8 некоторые массивы являются плотными, а некоторые - разреженными, в зависимости от того, как движок JScript чувствовал себя в то время. Подробнее здесь: blogs.msdn.com/jscript/archive/2008/04/08/…
@Ates: не берите то, что возвращает typeof, за индикатор что-либо. Эта функция настолько неработоспособна и совершенно неточна, что вызывает отвращение.
@Ates и @SF: typeof возвращает «объект» для ряда различных типов. Но как только вы узнаете, как он работает и какие типы идентифицируют как «объект», он будет по крайней мере надежным и последовательным в своей реализации.
+1, но это можно было бы значительно улучшить с помощью некоторых ссылок на примеры и т. д.
typeof x === "object" довольно ненадежный, но typeof x === "function" по-прежнему полезен :)
Сравнение NaN с чем-либо (даже с NaN) всегда неверно. Представляет ли js NaN как фактическую двоичную последовательность с плавающей запятой? Если да, то соответствует ли сравнение двух идентичных NaN? Если вы запутались, посмотрите представление с плавающей запятой.
@Razor Storm: JS использует представление IEEE 754, которое имеет множество битовых шаблонов, которые являются значениями «не числа». NaN в JS представляет их всех (ECMA 262-3 s4.3.23) неразличимо (s8.5). Все NaN должны сравниваться как неравные (s11.9.3), поэтому даже «идентичные» NaN IEEE 754 будут сравниваться как неравные.
@ Дэвид Леонард, хорошо, круто, спасибо за ваш ответ, это имеет смысл.
+1. новое логическое значение (false) == true меня взбесило
Также упоминается в "Javascript: The Good Parts" Крокфорда:
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.
При проверке кода всегда ищите этот. Отказ от «, 10» - распространенная ошибка, которая остается незамеченной в большинстве тестов.
Я был обожжен проблемой системы счисления много лет назад и никогда не забывал чего-то столь нелогичного как такового. Замечательная вещь, на которую стоит обратить внимание, так как это заставит вас на некоторое время задуматься.
Почему бы не использовать Math.floor или Number? 10 === Math.floor("010"); 10 === Number("010"); поплавки: 42 === Math.floor("42.69"); 42.69 === Number("42.69");
@Infinity Если это еще не опубликованный ответ, вам следует это сделать. Я понятия не имел, что так просто переопределить поведение встроенной функции. Конечно, это должно заставить присмотреться к любым пакетам кода, которые они заимствуют с других сайтов. Эту безобидную функцию parseInt можно легко заставить делать что-то не столь безобидное.
Ха .. ты думаешь, что parseInt может быть опасным? Попробуйте сделать undefined = 'foo' вверху страницы ... Javascript очень гибкий ... слишком гибкий
Но на самом деле ответ был опубликован, поэтому я просто прокомментировал его: stackoverflow.com/questions/61088/hidden-features-of-javascr ipt /…
@Infinity: как насчет переопределения fn, чтобы выделить «ошибку кодирования»? __parseInt = parseInt; parseInt = function (str, base) { if (!base) throw new Error(69, "All your base belong to us"); return __parseInt(str, base); }
@ Просто кто-нибудь: Math.floor ведет себя иначе, чем parseInt, например, при работе с отрицательными числами, что означает проверки на + -носпособность и т.д. // истинный ; parseInt ('- 10.2') === -10; // истинный;
Это исправлено в ES5, например parseInt('010') возвращает 10.
Это не unexpected number, это восьмеричное число: p
В этой теме есть несколько ответов, показывающих, как
расширить объект Array через его прототип. Это ПЛОХО
IDEA, потому что она нарушает заявление for (i in a).
Ничего страшного, если вы не используете for (i in a)
где-нибудь в вашем коде? Ну, только если ваш собственный код
только код, который вы используете, что маловероятно
внутри браузера. Боюсь, что если люди начнут расширяться
их объекты Array, как это, начнется переполнение стека
переполнен кучей загадочных ошибок JavaScript.
См. Полезные подробности здесь.
Вам вообще не следует перебирать массив с помощью for..in! Используйте стандартный цикл for () или новый метод forEach () для массивов и for..in строго для итерации по свойствам объекта.
Попробуйте убедить существующий код в этом совете;)
for (x in y) никогда не работал для меня должным образом для массивов. Я очень быстро научился использовать длинную форму цикла for. Я бы не позволил никакому коду, который использует for (in) в массиве, приближаться к любой моей работе. Вместо этого я мог бы использовать множество действительно приличного, хорошо написанного кода.
Вы просто недостаточно знаете JavaScript. Это не нарушает циклы for-in, вы неправильно их строите. Вы должны проверить все свойства с помощью "yourObject.hasOwnProperty (propertyName)", когда вы выполняете итерацию через for-in.
проверка .hasOwnProperty может быть приемлемой для итерации по объекту, подобному словарю, но не кажется правильным использовать это для массива.
@ statictype.org - да, это не так - в том-то и дело. Вместо этого просто используйте индексную переменную для итерации.
Если вам нужна скорость, итерация на основе индекса по массиву - самая быстрая из всех (по сравнению с for-in и forEach).
Вот пара ярлыков:
var a = []; // equivalent to new Array()
var o = {}; // equivalent to new Object()
с var a = []; , вы не можете создавать массивы указанного размера. Вам нужно будет использовать var a = new Array (arraySize);
Есть ли ощутимое преимущество в производительности при объявлении массива в JavaScript с указанным размером?
Я бы сказал самоисполняющиеся функции.
(function() { alert("hi there");})();
Поскольку Javascript не имеет области действия блока, вы можете использовать самоисполняющуюся функцию, если хотите определить локальные переменные:
(function() {
var myvar = 2;
alert(myvar);
})();
Здесь myvar не мешает и не загрязняет глобальную область видимости и исчезает при завершении функции.
Для чего это полезно? Вы получите те же результаты, если поместите предупреждение вне функции.
Дело не в предупреждении, а в одновременном определении и выполнении функции. Вы могли бы заставить эту самоисполняющуюся функцию возвращать значение и передавать функцию в качестве параметра другой функции.
@Paul это хорошо для инкапсуляции.
Это также хорошо для определения области видимости блока.
Это может быть полезно для создания и объявления объекта и его методов за один шаг.
Да, я заключаю все свои файлы .js в анонимную самоисполняющуюся функцию и прикрепляю все, что хочу глобально доступным внутри нее, к объекту window. Предотвращает загрязнение глобального пространства имен.
Я добавил пример того, почему это полезно для этого ответа.
Это также можно использовать для переименования переменных: (function ($) {...}) (jQuery) будет обращаться к глобальному объекту jQuery как $.
Я не согласен с названием; это вовсе не "самоисполняющееся".
@hasen j: согласен .. он просто создает анонимную функцию и сразу вызывает ее.
@ Пол, согласен с Майком; хорошо подходит для инкапсуляции; прочтите книгу Крокфорда, чтобы получить хорошее объяснение.
я думаю, что лучшее название - "немедленная функция"
На самом деле это «выражение немедленно вызываемой функции». Проверьте это: benalman.com/news/2010/11/…
Оператор == имеет очень особое свойство, которое создает это тревожное равенство (да, я знаю, что в других динамических языках, таких как Perl, такое поведение было бы ожидаемым, но JavaScript обычно не пытается быть умным в сравнении):
>>> 1 == true
true
>>> 0 == false
true
>>> 2 == true
false
Предотвращение раздражающих ошибок при тестировании в Internet Explorer при использовании console.info () для Firebug:
function log(message) {
(console || { log: function(s) { alert(s); }).log(message);
}
это не имеет отношения к языку ... :)
"function (s) {alert (s);}" можно просто заменить на "alert"
let.
Аналогом отсутствие у var области видимости блока является let, введено в JavaScript 1.7.
- The let statement provides a way to associate values with variables within the scope of a block, without affecting the values of like-named variables outside the block.
- The let expression lets you establish variables scoped only to a single expression.
- The let definition defines variables whose scope is constrained to the block in which they're defined. This syntax is very much like the syntax used for var.
- You can also use let to establish variables that exist only within the context of a for loop.
function varTest() {
var x = 31;
if (true) {
var x = 71; // same variable!
alert(x); // 71
}
alert(x); // 71
}
function letTest() {
let x = 31;
if (true) {
let x = 71; // different variable
alert(x); // 71
}
alert(x); // 31
}
С 2008 года JavaScript 1.7 поддерживается в FireFox 2.0+ и Safari 3.x.
Генераторы и итераторы (работает только в Firefox 2+ и Safari).
function fib() {
var i = 0, j = 1;
while (true) {
yield i;
var t = i;
i = j;
j += t;
}
}
var g = fib();
for (var i = 0; i < 10; i++) {
document.write(g.next() + "<br>\n");
}
The function containing the
yieldkeyword is a generator. When you call it, its formal parameters are bound to actual arguments, but its body isn't actually evaluated. Instead, a generator-iterator is returned. Each call to the generator-iterator'snext()method performs another pass through the iterative algorithm. Each step's value is the value specified by theyieldkeyword. Think ofyieldas the generator-iterator version of return, indicating the boundary between each iteration of the algorithm. Each time you callnext(), the generator code resumes from the statement following theyield.In normal usage, iterator objects are "invisible"; you won't need to operate on them explicitly, but will instead use JavaScript's
for...inandfor each...instatements to loop naturally over the keys and/or values of objects.
var objectWithIterator = getObjectSomehow();
for (var i in objectWithIterator)
{
document.write(objectWithIterator[i] + "<br>\n");
}
Чтобы правильно удалить свойство из объекта, вы должны удалить свойство вместо того, чтобы просто установить для него значение неопределенный:
var obj = { prop1: 42, prop2: 43 };
obj.prop2 = undefined;
for (var key in obj) {
...
Свойство prop2 по-прежнему будет частью итерации. Если вы хотите полностью избавиться от prop2, вам следует вместо этого сделать:
delete obj.prop2;
Свойство prop2 больше не будет отображаться при итерации свойств.
Обратите внимание, что оператор удаления не лишен специфических особенностей браузера. Например, это приведет к сбою с большой ошибкой, если вы попробуете его в IE, а объект не является собственным JS-объектом (даже при удалении свойства, которое вы добавили сами). Он также не предназначен для удаления переменной, как в delete myvar; но я думаю, что это работает в некоторых браузерах. Однако код в приведенном выше ответе кажется довольно безопасным.
кстати, undefined тоже может быть переменной! Попробуйте var undefined = "something"
Если вы ищете в Google достойную ссылку на JavaScript по данной теме, включите в свой запрос ключевое слово «mdc», и ваши первые результаты будут из Центра разработчиков Mozilla. Я не ношу с собой никаких офлайн-справочников или книг. Я всегда использую трюк с ключевыми словами "mdc", чтобы напрямую добраться до того, что я ищу. Например:
Google: Сортировка массива javascript mdc
(в большинстве случаев вы можете опустить "javascript")
Обновлять: Mozilla Developer Центр был переименован в Mozilla Developer Сеть. Уловка с ключевым словом "mdc" по-прежнему работает, но вскоре нам может потребоваться вместо этого начни использовать "mdn".
Что мы сделали, чтобы заслужить связь с LMGTFY ...
Вау, отличный ресурс. Мгновенно лучше, чем дерьмовые w3schools ...
Вам даже не нужно гуглить, если вы используете Firefox: просто введите "array mdc" в адресную строку и нажмите Enter.
Лучшая часть - это то, как этот вопрос о переполнении стека находится на первой странице результатов :)
Предложение это: Promotejs.com, массовая инициатива SEO, направленная на продвижение результатов MDC в результатах поиска Google.
Теперь это центр документации MDN, поэтому ключевое слово mdc по-прежнему актуально :)
Если вы слепо eval() строку JSON для ее десериализации, вы можете столкнуться с проблемами:
Если вы не заключите строку JSON в круглые скобки, имена свойств могут быть ошибочно приняты за метки, что приведет к неожиданному поведению или синтаксической ошибке:
eval("{ \"foo\": 42 }"); // syntax error: invalid label
eval("({ \"foo\": 42 })"); // OK
Вы не должны использовать eval для JSON. Используйте JSON.parse () и JSON.stringify ().
function l(f,n){n&&l(f,n-1,f(n));}
l( function( loop ){ alert( loop ); }, 5 );
предупреждения 5, 4, 3, 2, 1
В этом есть смысл, но ... Я бы никогда тебя не нанял.
Чтобы преобразовать число с плавающей запятой в целое число, вы можете использовать один из следующих загадочных приемов (пожалуйста, не делайте этого):
3.14 >> 0 (через 2.9999999999999999 >> .5?)3.14 | 0 (через Как лучше всего преобразовать числа с плавающей запятой в целое число в JavaScript?)3.14 & -13.14 ^ 0~~3.14По сути, применение любой бинарной операции к веществу с плавающей запятой, которая не изменяет конечное значение (т.е. функцию идентификации), приводит к преобразованию числа с плавающей запятой в целое число.
Пожалуйста, просто используйте Math.floor ().
@TheLindyHop, дело в том, что действительные числа и бинарные операторы нелегко смешивать, и в качестве эмульгатора используется неявное приведение к целому числу :) Даже если вы можете утверждать, что something_binary ^ 0 == something_binary, для этого потребуется исключение должно быть сделано в реализации и документации оператора xor.
Интересно. Это сделано специально для разработчиков javascript? может из-за лени? обычно это считается сломанным.
@TheLindyHop: Лень, глупость - факт остается фактом: это детерминированное поведение от имени JavaScript. Я указал на это только потому, что речь идет о «скрытых возможностях JavaScript». :) И будет ли это считать сломано немного спорный в контексте этого вопроса, но я бы сказал, что это было бы ожидаемое поведение.
jQuery и JavaScript:
Имена переменных могут содержать несколько нечетных символов. Я использую символ $ для обозначения переменных, содержащих объекты jQuery:
var $links = $("a");
$links.hide();
Шаблон связывания объектов в jQuery довольно хорош, но применение этого шаблона может немного запутать. К счастью, JavaScript позволяет разбивать строки, например:
$("a")
.hide()
.fadeIn()
.fadeOut()
.hide();
Общий JavaScript:
Я считаю полезным эмулировать область видимости с помощью самоисполняющихся функций:
function test()
{
// scope of test()
(function()
{
// scope inside the scope of test()
}());
// scope of test()
}
Если вы хотите, чтобы это было действительно странно: function 喂 (我) {alert (我)};喂 («мир»);
Операторы функций и выражения функций обрабатываются по-разному.
function blarg(a) {return a;} // statement
bleep = function(b) {return b;} //expression
Все операторы функций анализируются перед запуском кода - функция в нижней части файла JavaScript будет доступна в первом операторе. С другой стороны, он не сможет использовать преимущества определенного динамического контекста, такого как окружающие операторы with - with не был выполнен при анализе функции.
Выражения функций выполняются встроенно, прямо там, где они встречаются. Они недоступны до этого времени, но могут воспользоваться динамическим контекстом.
Прототипное наследование (популяризированный Дугласом Крокфордом) полностью меняет ваше представление о множестве вещей в 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'
Значение window.name сохраняется при изменении страницы, может быть прочитано родительским окном, если оно находится в том же домене (если в iframe, используйте document.getElementById("your frame's ID").contentWindow.name для доступа к нему) и ограничено только доступной памятью.
Обязательно используйте метод hasOwnProperty при итерации по свойствам объекта:
for (p in anObject) {
if (anObject.hasOwnProperty(p)) {
//Do stuff with p here
}
}
Это сделано для того, чтобы вы имели доступ только к прямые свойства объект, а не использовали свойства, которые находятся в цепочке прототипов.
Если вы хотите удалить элемент из массива, можно использовать оператор Удалить как таковой:
var numbers = [1,2,3,4,5];
delete numbers[3];
//numbers is now [1,2,3,undefined,5]
Как видите, элемент был удален, но в массиве осталась дыра, так как элемент был заменен значением неопределенный.
Таким образом, чтобы обойти эту проблему, вместо использования Удалить используйте метод массива сращивание ... как таковой:
var numbers = [1,2,3,4,5];
numbers.splice(3,1);
//numbers is now [1,2,3,5]
Первый аргумент сращивание - порядковый номер в массиве [index], а второй - количество удаляемых элементов.
Микрософт подарок для JavaScript: AJAX
AJAXCall('http://www.abcd.com/')
function AJAXCall(url) {
var client = new XMLHttpRequest();
client.onreadystatechange = handlerFunc;
client.open("GET", url);
client.send();
}
function handlerFunc() {
if (this.readyState == 4 && this.status == 200) {
if (this.responseXML != null)
document.write(this.responseXML)
}
}
Большие циклы быстрее выполняются в состоянии while и в обратном направлении - то есть, если порядок цикла для вас не имеет значения. Примерно в 50% моего кода это обычно не так.
т.е.
var i, len = 100000;
for (var i = 0; i < len; i++) {
// do stuff
}
Медленнее, чем:
i = len;
while (i--) {
// do stuff
}
Есть ли у вас статьи, объясняющие этот результат? Было бы интересно получить дополнительную информацию, почему это происходит.
Так ли это во всех реализациях Javascript?
Я предполагаю, что второй быстрее, потому что ему не нужно оценивать i <len каждый ход, что требует 100000 для сравнения с i. Это, вероятно, быстрее, чем проверка, равен ли i 0 или нет.
-1; у вас есть доказательства этого? какую реализацию вы используете? Я просто попробовал следующий код (замените pipe | символы на символы новой строки) на jsdb, который использует Spidermonkey (движок javascript Firefox), и это, по сути, такое же время выполнения. var i, len = 15000000; | d1 = новая дата (); для (var я = 0; я <len; я ++) {}; новая дата () - d1 | d1 = новая дата (); i = len; в то время как я--) {}; новая дата () - d1
@Jason - эталоном является IE, так как это доминирующий браузер - по мере того, как новые браузеры конкурируют, мы наблюдаем потрясающие улучшения в движках JS, поэтому, конечно, вы увидите, что движки улучшатся по этому шаблону, но вы имеют, чтобы пересечь браузер протестирует вашу оптимизацию, когда подсчитываются миллисекунды Итак, вы проверяли этот код и в других браузерах?
Это верно, когда вы ничего не делаете, но когда вы меняете массив, он работает быстрее.
Также существует почти неизвестный синтаксис JavaScript:
var a;
a=alert(5),7;
alert(a); // alerts undefined
a=7,alert(5);
alert(a); // alerts 7
a=(3,6);
alert(a); // alerts 6
Подробнее об этом здесь.
В большинстве случаев вы можете использовать объекты вместо переключателей.
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);
Вот как Python обходится без оператора switch.
Проблема в том, что он всегда оценивает все случаи.
@porneL, это правда, но дает некоторые преимущества: это логически чище: прецеденты - это строки, которые ищутся в хеш-таблице, а не выражения, каждое из которых должно оцениваться на равенство, пока одно из них не вернет истину. Таким образом, хотя оценивается больше «значений», оценивается меньшее количество «ключей». Объекты могут быть динамически сгенерированы и изменены для последующего масштабирования, отражены для печати пользовательского интерфейса или создания документов и даже заменены функцией динамического «поиска», что лучше, чем копирование / вставка кейсов. Нет никакой путаницы в отношении разрывов, провалов или значений по умолчанию. Может быть сериализован JSON ...
@porneL о да, и опять же, что касается масштабируемости, объект можно даже легко преобразовать во внешнюю конфигурацию или файл данных, что несколько более прямолинейно, чем с помощью оператора switch - но тривиально, если он спроектирован с учетом объекта для начала с.
Я знаю, что это поздний ввод, но если у вас нет специальной логики проверки типов, когда массив когда-либо будет работать с вашим примером? var arr = []; typeof arr; // object
Слишком верно, хотя обратите внимание, что в последнем примере я использую «typeis (o)», хотя на самом деле typeof был просто тем, что я использовал в качестве целесообразного примера, и это довольно второстепенно по отношению к основному вопросу. Я отредактирую другие примеры, чтобы они соответствовали.
Вы можете поймать исключения в зависимости от их типа. Цитируется из 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 и, следовательно, на него нельзя полагаться, за исключением определенных браузеров.
Я ничего не мог с собой поделать: поймай (меня, если сможешь)
Прочтите примечание со страницы MDC, которую вы процитировали: условные предложения catch - это расширение Netscape (и, следовательно, Mozilla / Firefox), которое не является частью спецификации ECMAScript и, следовательно, на него нельзя полагаться, за исключением определенных браузеров.
Вы можете превратить любой * объект с целочисленными свойствами и свойство длины в собственно массив и, таким образом, снабдить его всеми методами массива, такими как push, pop, splice, map, filter, reduce и т. д.
Array.prototype.slice.call({"0":"foo", "1":"bar", 2:"baz", "length":3 })
// возвращает ["foo", "bar", "baz"]
Это работает с объектами jQuery, коллекциями html и объектами Array из других фреймов (как одно из возможных решений для всего типа массива). Я говорю, что если у него есть свойство length, вы можете превратить его в массив, и это не имеет значения. Существует множество объектов, не являющихся массивами, со свойством length, помимо объекта arguments.
Синтаксический сахар: встроенные замыкания цикла for
var i;
for (i = 0; i < 10; i++) (function ()
{
// do something with i
}());
Нарушает почти все кодовые соглашения Дугласа Крокфорда, но я думаю, что на это довольно приятно смотреть, тем не менее :)
Альтернатива:
var i;
for (i = 0; i < 10; i++) (function (j)
{
// do something with j
}(i));
Извините, я имел в виду и аргумент, и параметр :)
Разве это не превращает функцию в каждую итерацию цикла?
Кажется, это работает только в Firefox (SpiderMonkey). Внутри функции:
arguments[-2] дает количество аргументов (то же, что и arguments.length)arguments[-3] дает функцию, которая была вызвана (так же, как arguments.callee)Проверка наличия. Так часто я вижу такие вещи
var a = [0, 1, 2];
// code that might clear the array.
if (a.length > 0) {
// do something
}
вместо этого, например, просто сделайте это:
var a = [0, 1, 2];
// code that might clear the array.
if (a.length) { // if length is not equal to 0, this will be true
// do something
}
Вы можете выполнить всевозможные проверки существования, но это был всего лишь простой пример, чтобы проиллюстрировать точку зрения.
Вот пример того, как использовать значение по умолчанию.
function (someArgument) {
someArgument || (someArgument = "This is the deault value");
}
Это мои два цента. Есть и другие самородки, но пока это все.
Предупреждение: someArgument будет переопределен, если он оценивается как false (который включает значения 0, NaN, false, "" и null, а также пропуск аргумента)
Вам никогда не придется использовать eval() для сборки имен глобальных переменных.
То есть, если у вас есть несколько глобальных объектов (по какой-либо причине) с именем spec_grapes, spec_apples, вам не нужно обращаться к ним с помощью eval("spec_" + var).
Все глобальные объекты являются членами window[], поэтому вы можете использовать window["spec_" + var].
Также вы можете сократить «if (typeof myvar! = 'Undefined')» до «if (window.myvar)»
Помните, что это Только в движке javascript браузера. Вы можете запустить автономный движок Javascript. Кто-нибудь на стороне сервера javascript? - Просто придирки, я знаю ...
@voyager: Согласен - jaxer.org круто!
@BarelyFitz: Неправда. Переменная window.myvar может иметь любое из следующих значений: 0, false, "", null или NaN. (могут быть и другие, но я думаю, что уже рассказал о них.)
функция getGlobal () {return (function inner () {return this;}) (); }; - даже махинации "применить", "вызов" и прототипы не могут заставить это не возвращать глобальную область видимости (которая в сети называется "окном")
В начале вашего скрипта вы можете просто указать var global = this. Это сработает независимо от того, в каком контексте вы находитесь.
@kzh: ваша переменная global могла быть перегружена охватывающей областью.
Марк Сидад указал на полезность цикла for in:
// creating an object (the short way, to use it like a hashmap)
var diner = {
"fruit":"apple"
"veggetable" = "bean"
}
// looping over its properties
for (meal_name in diner ) {
document.write(meal_name+"<br \n>");
}
Результат :
fruit
veggetable
Но это еще не все. Поскольку вы можете использовать такой объект, как ассоциативный массив, вы можете обрабатывать ключи и значения, точно так же, как цикл foreach:
// looping over its properties and values
for (meal_name in diner ) {
document.write(meal_name+" : "+diner[meal_name]+"<br \n>");
}
Результат :
fruit : apple
veggetable : bean
А поскольку массивы тоже являются объектами, вы можете точно так же перебирать другой массив:
var my_array = ['a', 'b', 'c'];
for (index in my_array ) {
document.write(index+" : "+my_array[index]+"<br \n>");
}
Результат :
0 : a
1 : b
3 : c
var arr = ['a', 'b', 'c', 'd'];
var pos = arr.indexOf('c');
pos > -1 && arr.splice( pos, 1 );
- не совсем случайное распределение, см. Комментарии.arr.sort(function() Math.random() - 0.5);
+1 за красивую перетасовку массива
-1 для перемешивания массива. Аргумент функции sort () всегда должен приводить к последовательному упорядочиванию. У вас нет доказательств того, что результаты будут отображаться как случайное распределение; это зависит от реализации sort ().
Если вам действительно нужна случайная сортировка, используйте функцию (a, b), которая сравнивает "случайную" функцию g (x, k), примененную к a и b (сравните g (a, k) и g (b, k)) где k - некоторый параметр, который остается постоянным, по крайней мере, в течение всего времени сортировки, а g () - хэш-функция некоторого вида.
Или, что еще лучше, просто используйте тасование Фишера-Йетса. en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle
Скобки необязательны при создании новых «объектов».
function Animal () {
}
var animal = new Animal();
var animal = new Animal;
То же самое.
это часть ECMAScript или просто движок Mozilla Spidermonkey? (работает с оболочкой Spidermonkey)
не уверен, я только что воспользовался этим предположением в течение некоторого времени. работает в IE, IIS тоже
Мне нравится оставлять здесь круглые скобки, потому что они напоминают мне, что вы на самом деле вызываете функцию (а 'new' заставляет неявно возвращать 'this').
Вы можете назначить локальные переменные, используя [] с левой стороны. Пригодится, если вы хотите вернуть более одного значения из функции без создания ненужного массива.
function fn(){
var cat = "meow";
var dog = "woof";
return [cat,dog];
};
var [cat,dog] = fn(); // Handy!
alert(cat);
alert(dog);
Это часть ядра JS, но я почему-то не осознавал этого до этого года.
не поддерживается IE, за исключением этой небольшой проблемы, это интересная функция.
Это «деструктурирующее задание»; Я считаю, что он поддерживается только в версиях Firefox с JavaScript 1.7 и новее. Это определенно вызывает ошибку в Opera 10 и Chrome 3, а также в IE. См. developer.mozilla.org/en/…
Мне бы хотелось увидеть подобный синтаксис в других языках, например в C#.
А пока это не хуже: function fn(){ return {cat:"meow",dog:"woof"}; // Handy! }; var snd = fn(); alert(snd.cat); alert(snd.dog);.
не работает в Chrome> 15 w> JS 1.6, иначе это было бы самым большим удивлением. Я даже пробовал это однажды, пытаясь подражать списку PHP (...)
Может быть, один из менее известных:
arguments.callee.caller + Функция # toString ()
function called(){
alert("Go called by:\n"+arguments.callee.caller.toString());
}
function iDoTheCall(){
called();
}
iDoTheCall();
Распечатывает исходный код iDoTheCall -
Устарело, но иногда может быть полезно, когда единственным вариантом является оповещение ...
Вы можете привязать объект JavaScript как атрибут элемента HTML.
<div id = "jsTest">Klick Me</div>
<script type = "text/javascript">
var someVariable = 'I was klicked';
var divElement = document.getElementById('jsTest');
// binding function/object or anything as attribute
divElement.controller = function() { someVariable += '*'; alert('You can change instance data:\n' + someVariable ); };
var onclickFunct = new Function( 'this.controller();' ); // Works in Firefox and Internet Explorer.
divElement.onclick = onclickFunct;
</script>
Если вы пытаетесь изолировать код javascript и отключить все возможные способы оценки строк в код javascript, имейте в виду, что блокировка всех очевидных eval / document.write / new Function / setTimeout / setInterval / innerHTML и других манипуляций с DOM недопустима. довольно.
Для любого объекта o o.constructor.constructor("alert('hi')")() вызовет диалоговое окно с предупреждением со словом «привет» в нем.
Вы можете переписать это как
var Z = "constructor";
Z[Z][Z]("alert('hi')")();
Веселая штука.
Это скорее скрытая ошибка, чем скрытая функция, но она чрезвычайно интересна, учитывая, что я уже пытался заблокировать такие вещи, как eval. Действительно очень интересно :-) +1
Вы могли бы с пользой упомянуть здесь, что причина этого заключается в том, что конструктор любого объекта всегда является некоторой функцией, и что конструктором этой функции всегда будет Function - конструктор функции, который может создавать функции из строк. Function(str) фактически возвращает function() { eval(str) }.
Самые быстрые циклы в JavaScript - это while (i--). Во всех браузерах. Поэтому, если порядок обработки элементов вашего цикла не так важен, вы должны использовать форму while (i--):
var names = new Array(1024), i = names.length;
while(i--)
names[i] = "John" + i;
Кроме того, если вам нужно использовать цикл for () в дальнейшем, не забудьте всегда кешировать свойство .length:
var birds = new Array(1024);
for(var i = 0, j = birds.length; i < j; i++)
birds[i].fly();
Для объединения больших строк используйте массивы (это быстрее):
var largeString = new Array(1024), i = largeString.length;
while(i--) {
// It's faster than for() loop with largeString.push(), obviously :)
largeString[i] = i.toString(16);
}
largeString = largeString.join("");
Это намного быстрее, чем largeString += "something" внутри цикла.
Я уже некоторое время использую вариант цикла for вашего while(i--): for (var i=names.length;i--;) {...
Я не согласен. В firefox (function(){var a=new Array(10000),i=10000;while(--i){a[i]=i}})() занимает около 7 миллисекунд, тогда как (function () {var a = new Array (10000); for (var i = 0; i <10000; i ++) {a [i] = i}}) () занимает около 2 миллисекунды.
Оператор объединения очень крутой и создает чистый и лаконичный код, особенно когда вы объединяете его в цепочку: a || b || c || "default"; Проблема в том, что, поскольку он работает, вычисляя значение bool, а не null, если значения, которые оцениваются как false, допустимы, они будут часто перебираться смотрел. Не волнуйтесь, в этих случаях просто вернитесь к старому доброму тернарному оператору.
Я часто вижу код, который отказался и использовал глобальные вместо статических переменных, поэтому вот как (в примере того, что, я полагаю, вы могли бы назвать универсальной одноэлементной фабрикой):
var getInstance = function(objectName) {
if ( !getInstance.instances ) {
getInstance.instances = {};
}
if ( !getInstance.instances[objectName] ) {
getInstance.instances[objectName] = new window[objectName];
}
return getInstance.instances[objectName];
};
Также обратите внимание на new window[objectName];, который был ключом к созданию экземпляров объектов по имени. Я понял это 2 месяца назад.
В том же духе, работая с DOM, я часто закапываю функциональные параметры и / или флаги в узлы DOM, когда я впервые инициализирую любую добавляемую функциональность. Я добавлю пример, если кто-то кричит.
Удивительно, но никто на первой странице не упомянул hasOwnProperty, что очень досадно. При использовании in для итерации рекомендуется использовать в защитном программировании метод hasOwnProperty для итерируемого контейнера, чтобы убедиться, что используемые имена членов соответствуют ожидаемым.
var x = [1,2,3];
for ( i in x ) {
if ( !x.hasOwnProperty(i) ) { continue; }
console.info(i, x[i]);
}
Прочтите здесь, чтобы узнать больше об этом.
Наконец, with - это почти всегда плохая идея.
Вы можете сделать «классы», у которых есть частные (недоступные вне определения «класса») статические и нестатические члены, в дополнение к публичным членам, используя замыкания.
Обратите внимание, что в приведенном ниже коде есть два типа открытых членов. Зависящие от экземпляра (определенные в конструкторе), которые имеют доступ к частным членам пример, и общие члены (определенные в объекте prototype), которые имеют доступ только к частным членам статический.
var MyClass = (function () {
// private static
var nextId = 1;
// constructor
var cls = function () {
// private
var id = nextId++;
var name = 'Unknown';
// public (this instance only)
this.get_id = function () { return id; };
this.get_name = function () { return name; };
this.set_name = function (value) {
if (typeof value != 'string')
throw 'Name must be a string';
if (value.length < 2 || value.length > 20)
throw 'Name must be 2-20 characters long.';
name = value;
};
};
// public static
cls.get_nextId = function () {
return nextId;
};
// public (shared across instances)
cls.prototype = {
announce: function () {
alert('Hi there! My id is ' + this.get_id() + ' and my name is "' + this.get_name() + '"!\r\n' +
'The next fellow\'s id will be ' + MyClass.get_nextId() + '!');
}
};
return cls;
})();
Чтобы проверить этот код:
var mc1 = new MyClass();
mc1.set_name('Bob');
var mc2 = new MyClass();
mc2.set_name('Anne');
mc1.announce();
mc2.announce();
Если у вас есть Firebug, вы обнаружите, что нет другого способа получить доступ к закрытым членам, кроме как установить точку останова внутри замыкания, которое их определяет.
Этот шаблон очень полезен при определении классов, которым требуется строгая проверка значений и полный контроль изменений состояния.
Чтобы расширить этот класс, вы должны поместить MyClass.call(this); в начало конструктора в расширяющемся классе. Вам также потребуется копировать для объекта MyClass.prototype (не используйте его повторно, так как вы также измените элементы MyClass.
Если бы вы заменили метод announce, вы бы вызвали из него MyClass.announce следующим образом: MyClass.prototype.announce.call(this);
Оператор JavaScript typeof, используемый с массивами или нулями, всегда возвращает значение object, которое в некоторых случаях может не соответствовать ожиданиям программиста.
Вот функция, которая также вернет правильные значения для этих элементов. Распознавание массивов было скопировано из книги Дугласа Крокфорда «JavaScript: хорошие части».
function typeOf (value) {
var type = typeof value;
if (type === 'object') {
if (value === null) {
type = 'null';
} else if (typeof value.length === 'number' &&
typeof value.splice === 'function' &&
!value.propertyIsEnumerable('length')) {
type = 'array';
}
}
return type;
}
Да, я думаю, это можно было бы сделать так. Но, как я уже сказал, это скопировано из книги.
вот почему я никогда не был поклонником Крокфорда - его код всегда слишком тупой
Использование Function.apply для указания объекта, над которым будет работать функция:
Предположим, у вас есть класс
function myClass(){
this.fun = function(){
do something;
};
}
если позже вы сделаете:
var a = new myClass();
var b = new myClass();
myClass.fun.apply(b); //this will be like b.fun();
Вы даже можете указать массив параметров вызова в качестве второго аргумента
посмотрите это: https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/Function/apply
Мое первое представление - это не столько скрытая функция, сколько редко используемое приложение функции переопределения свойств. Поскольку вы можете переопределить методы объекта, вы можете кэшировать результат вызова метода, что полезно, если вычисление является дорогостоящим и вам нужна ленивая оценка. Это дает простейшую форму мемоизация.
function Circle(r) {
this.setR(r);
}
Circle.prototype = {
recalcArea: function() {
this.area=function() {
area = this.r * this.r * Math.PI;
this.area = function() {return area;}
return area;
}
},
setR: function (r) {
this.r = r;
this.invalidateR();
},
invalidateR: function() {
this.recalcArea();
}
}
Выполните рефакторинг кода, который кэширует результат в метод, и вы получите:
Object.prototype.cacheResult = function(name, _get) {
this[name] = function() {
var result = _get.apply(this, arguments);
this[name] = function() {
return result;
}
return result;
};
};
function Circle(r) {
this.setR(r);
}
Circle.prototype = {
recalcArea: function() {
this.cacheResult('area', function() { return this.r * this.r * Math.PI; });
},
setR: function (r) {
this.r = r;
this.invalidateR();
},
invalidateR: function() {
this.recalcArea();
}
}
Если вам нужна мемоизированная функция, вы можете ее использовать. Переопределение собственности не происходит.
Object.prototype.memoize = function(name, implementation) {
this[name] = function() {
var argStr = Array.toString.call(arguments);
if (typeof(this[name].memo[argStr]) == 'undefined') {
this[name].memo[argStr] = implementation.apply(this, arguments);
}
return this[name].memo[argStr];
}
};
Обратите внимание, что это зависит от стандартного преобразования массива в строку и часто не работает должным образом. Исправление оставлено в качестве упражнения для читателя.
Моя вторая заявка - это геттеры и сеттеры. Я удивлен, что они еще не были упомянуты. Поскольку официальный стандарт отличается от стандарта де-факто (defineProperty vs. определить [GS] etter), а Internet Explorer почти не поддерживает официальный стандарт, они обычно бесполезны. Может, поэтому их не упомянули. Обратите внимание, что вы можете довольно хорошо комбинировать методы получения и кеширование результатов:
Object.prototype.defineCacher = function(name, _get) {
this.__defineGetter__(name, function() {
var result = _get.call(this);
this.__defineGetter__(name, function() { return result; });
return result;
})
};
function Circle(r) {
this.r = r;
}
Circle.prototype = {
invalidateR: function() {
this.recalcArea();
},
recalcArea: function() {
this.defineCacher('area', function() {return this.r * this.r * Math.PI; });
},
get r() { return this._r; }
set r(r) { this._r = r; this.invalidateR(); }
}
var unit = new Circle(1);
unit.area;
Эффективное объединение геттеров, сеттеров и кеширования результатов немного сложнее, потому что вам нужно предотвратить аннулирование или обойтись без автоматического аннулирования при наборе, что и делается в следующем примере. В основном это проблема, если изменение одного свойства приведет к аннулированию нескольких других (представьте, что в этих примерах есть свойство «диаметр»).
Object.prototype.defineRecalcer = function(name, _get) {
var recalcFunc;
this[recalcFunc='recalc'+name.toCapitalized()] = function() {
this.defineCacher(name, _get);
};
this[recalcFunc]();
this.__defineSetter__(name, function(value) {
_set.call(this, value);
this.__defineGetter__(name, function() {return value; });
});
};
function Circle(r) {
this.defineRecalcer('area',
function() {return this.r * this.r * Math.PI;},
function(area) {this._r = Math.sqrt(area / Math.PI);},
);
this.r = r;
}
Circle.prototype = {
invalidateR: function() {
this.recalcArea();
},
get r() { return this._r; }
set r(r) { this._r = r; this.invalidateR(); }
}
<script type = "text/javascript">
(function() {
function init() {
// ...
}
window.onload = init;
})();
</script>
Переменные и функции, объявленные без оператора var или вне функции, будут определены в глобальной области видимости. Если переменная / функция с таким же именем уже существует, она будет автоматически переопределена, что может привести к очень трудным для поиска ошибкам. Распространенное решение - заключить все тело кода в анонимную функцию и немедленно выполнить ее. Таким образом, все переменные / функции определены в области действия анонимной функции и не попадают в глобальную область видимости.
Чтобы явно определить переменную / функцию в глобальной области видимости, они должны иметь префикс window:
window.GLOBAL_VAR = 12;
window.global_function = function() {};
В более крупных приложениях или фреймворках JavaScript может быть полезно организовать код в пространствах имен. В JavaScript нет встроенного модуля или концепции пространства имен, но его легко эмулировать с помощью объектов JavaScript. Это создаст пространство имен ns и присоединит к нему функцию foo.
if (!window.ns) {
window.ns = {};
}
window.ns.foo = function() {};
Обычно в проекте используется один и тот же префикс глобального пространства имен, а для каждого файла JavaScript используются подпространства имен. Имя подпространства имен часто совпадает с именем файла.
Заголовок файла ns/button.js может выглядеть так:
if (!window.ns) {
window.ns = {};
}
if (!window.ns.button) {
window.ns.button = {};
}
// attach methods to the ns.button namespace
window.ns.button.create = function() {};
Или вы можете использовать это: «window.ns = window.ns || {} ”Ad @ m
Вот простой способ думать об «этом». 'This' внутри функции будет ссылаться на будущие экземпляры объекта функции, обычно созданные с помощью оператора new. Итак, ясно, что «this» внутренней функции никогда не будет относиться к экземпляру внешней функции.
Вышесказанное должно уберечь человека от неприятностей. Но есть более сложные вещи, которые вы можете сделать с этим.
Пример 1:
function DriveIn()
{
this.car = 'Honda';
alert(this.food); //'food' is the attribute of a future object
//and DriveIn does not define it.
}
var A = {food:'chili', q:DriveIn}; //create object A whose q attribute
//is the function DriveIn;
alert(A.car); //displays 'undefined'
A.q(); //displays 'chili' but also defines this.car.
alert(A.car); //displays 'Honda'
Правило этого:
Всякий раз, когда функция вызывается как атрибут объекта, любое вхождение this внутри функции (но вне любых внутренних функций) относится к объекту.
Нам нужно прояснить, что «Правило этого» применяется даже при использовании оператора new. За кулисами new прикрепляет this к объекту через атрибут конструктора объекта.
Пример 2:
function Insect ()
{
this.bug = "bee";
this.bugFood = function()
{
alert("nectar");
}
}
var B = new Insect();
alert(B.constructor); //displays "Insect"; By "The Rule of This" any
//ocurrence of 'this' inside Insect now refers
//to B.
Чтобы сделать это еще яснее, мы можем создать экземпляр Insect без использования оператора new.
Пример 3:
var C = {constructor:Insect}; //Assign the constructor attribute of C,
//the value Insect.
C.constructor(); //Call Insect through the attribute.
//C is now an Insect instance as though it
//were created with operator new. [*]
alert(C.bug); //Displays "bee."
C.bugFood(); //Displays "nectar."
[*] Единственное различие, которое я могу различить, состоит в том, что в примере 3 'конструктор' является перечислимым атрибутом. Когда используется оператор new, «конструктор» становится атрибутом, но его нельзя перечислить. Атрибут перечислим, если операция for-in «for (имя переменной в объекте)» возвращает имя атрибута.
Знайте, сколько параметров ожидает функция
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
Что ж, это не особенная функция, но очень полезная:
Показывает выбираемые и отформатированные предупреждения:
alert(prompt('',something.innerHTML ));
Это не всегда хорошая идея, но вы можете преобразовать большинство вещей с помощью кратких выражений. Важным моментом здесь является то, что не каждое значение в JavaScript является объектом, поэтому эти выражения будут успешными, если доступ к элементам не-объектов, таких как null и undefined, завершится ошибкой. В частности, будьте осторожны с typeof null == "object", но вы не можете использовать null.toString () или ("name" в null).
Преобразуйте что угодно в число:
+anything
Number(anything)
Преобразуйте что угодно в четырехбайтовое целое число без знака:
anything >>> 0
Преобразуйте что угодно в строку:
'' + anything
String(anything)
Преобразуйте что угодно в логическое значение:
!!anything
Boolean(anything)
Кроме того, использование имени типа без "new" по-разному ведет себя для String, Number и Boolean, возвращая примитивное число, строку или логическое значение, но с "new" они возвращают "упакованные" типы объектов, которые почти бесполезны.
Мой любимый трюк - использовать apply для выполнения обратного вызова метода объекта и поддержания правильной переменной this.
function MakeCallback(obj, method) {
return function() {
method.apply(obj, arguments);
};
}
var SomeClass = function() {
this.a = 1;
};
SomeClass.prototype.addXToA = function(x) {
this.a = this.a + x;
};
var myObj = new SomeClass();
brokenCallback = myObj.addXToA;
brokenCallback(1); // Won't work, wrong "this" variable
alert(myObj.a); // 1
var myCallback = MakeCallback(myObj, myObj.addXToA);
myCallback(1); // Works as expected because of apply
alert(myObj.a); // 2
Хм, я не прочитал всю тему, хотя она для меня довольно интересна, но позвольте мне сделать небольшое пожертвование:
// forget the debug alerts
var alertToFirebugConsole = function() {
if ( window.console && window.console.info ) {
window.alert = console.info;
}
}
В Chrome эта функция не работает, потому что log может вызываться только как экземпляр console. Это будет работать: window.alert = function(){console.info.apply(null,Array.prototype.slice.call(arguments));}. Часть Array.prototype.slice.call необходима для совместимости со старыми браузерами, которые не могут обрабатывать объект arguments как параметр для apply.
функция может иметь методы.
Я использую этот шаблон отправки форм AJAX.
var fn = (function() {
var ready = true;
function fnX() {
ready = false;
// AJAX return function
function Success() {
ready = true;
}
Success();
return "this is a test";
}
fnX.IsReady = function() {
return ready;
}
return fnX;
})();
if (fn.IsReady()) {
fn();
}
Этот узор совсем не скрывается :)
Считается, что JavaScript очень хорошо раскрывает весь свой объект, независимо от того, является ли его объект окна.
Поэтому, если я хочу переопределить предупреждение браузера с помощью всплывающего окна JQuery / YUI div, которое также принимает строку в качестве параметра, это можно сделать, просто используя следующий фрагмент.
function divPopup(str)
{
//code to show the divPopup
}
window.alert = divPopup;
С этим изменением все вызовы alert () будут показывать хорошее новое всплывающее окно на основе div, а не конкретное предупреждение браузера.
Дзен замыканий
Другие люди упомянули закрытия. Но удивительно, как много людей знают о замыканиях, пишут код с использованием замыканий, но все еще имеют неправильное представление о том, что такое замыкания на самом деле. Некоторые люди путают первоклассные функции с замыканиями. Третьи видят в нем своего рода статическую переменную.
Для меня замыкание - это своего рода глобальная переменная 'частный'. Это своего рода переменная, которую некоторые функции считают глобальной, но не видят другие. Я знаю, что с описанием лежащего в основе механизма ничего не происходит, но именно так он себя чувствует и ведет себя именно так. Проиллюстрировать:
// Say you want three functions to share a single variable:
// Use a self-calling function to create scope:
(function(){
var counter = 0; // this is the variable we want to share;
// Declare global functions using function expressions:
increment = function(){
return ++counter;
}
decrement = function(){
return --counter;
}
value = function(){
return counter;
}
})()
теперь три функции increment, decrement и value совместно используют переменную counter, при этом counter не является реальной глобальной переменной. Это истинная природа закрытия:
increment();
increment();
decrement();
alert(value()); // will output 1
Вышеупомянутое не совсем полезное использование замыканий. На самом деле, я бы сказал, что использование этого способа - антипаттерн. Но это полезно для понимания природы замыканий. Например, большинство людей попадают в ловушку, когда они пытаются сделать что-то вроде следующего:
for (var i=1;i<=10;i++) {
document.getElementById('span'+i).onclick = function () {
alert('this is span number '+i);
}
}
// ALL spans will generate alert: this span is span number 10
Это потому, что они не понимают природы замыканий. Они думают, что передают значение i в функции, хотя на самом деле функции совместно используют одну переменную i. Как я уже сказал, особый вид глобальной переменной.
Чтобы обойти это, вам нужно отделить * закрытие:
function makeClickHandler (j) {
return function () {alert('this is span number '+j)};
}
for (var i=1;i<=10;i++) {
document.getElementById('span'+i).onclick = makeClickHandler(i);
}
// this works because i is passed by reference
// (or value in this case, since it is a number)
// instead of being captured by a closure
* примечание: я не знаю здесь правильной терминологии.
Это очень полезный пост, спасибо. Просто мелкая придирка. В цикле for должно быть i = 1, а не i = i. Кроме того, я не мог заставить работать обходной путь. Возможно, вы имели в виду это вместо этого: function makeClickHandler (j) {return function () {alert ('this is span number' + j); }; }
@ Стив: Спасибо за обнаружение ошибок.
Я знаю, что опаздываю на вечеринку, но я просто не могу поверить, что о полезности оператора + не упоминалось, кроме «преобразования чего-либо в число». Может быть, вот насколько хорошо эта функция скрыта?
// 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: вы как бы упускаете суть, которая заставляет другие примитивы и объекты числа к. Конечно, вы можете просто написать 0xFF, почти так же, как вы можете написать 1 вместо +true. Я предлагаю вам использовать +("0x"+somevar) в качестве альтернативы parseInt(somevar, 16), если хотите.
Вот код для переопределения функции window.alert с помощью Виджет диалога пользовательского интерфейса jQuery. Я сделал это как плагин jQuery. И вы можете прочитать об этом в моем блоге; altAlert, плагин jQuery для персонализированных предупреждающих сообщений..
jQuery.altAlert = function (options)
{
var defaults = {
title: "Alert",
buttons: {
"Ok": function()
{
jQuery(this).dialog("close");
}
}
};
jQuery.extend(defaults, options);
delete defaults.autoOpen;
window.alert = function ()
{
jQuery("<div />", {
html: arguments[0].replace(/\n/, "<br />")
}).dialog(defaults);
};
};
Практически никогда не бывает хорошей идеей ... но, безусловно, полезно знать. Я использую его только для исправления ужасного поведения parseInt по умолчанию (восьмеричное основание по умолчанию). Итак, чтобы десятичная дробь была по умолчанию: ___parseInt = parseInt; parseInt = function (str, base) { return ___parseInt(str, base || 10) };
Кэширование возвращаемого значения простой автономной функции:
function isRunningLocally(){
var runningLocally = ....; // Might be an expensive check, check whatever needs to be checked.
return (isRunningLocally = function(){
return runningLocally;
})();
},
Дорогостоящая часть выполняется только при первом вызове, и после этого функция возвращает это значение. Конечно, это полезно только для функций, которые всегда возвращают одно и то же.
В функции вы можете вернуть саму функцию:
function showSomething(a){
alert(a);
return arguments.callee;
}
// Alerts: 'a', 'b', 'c'
showSomething('a')('b')('c');
// Or what about this:
(function (a){
alert(a);
return arguments.callee;
})('a')('b')('c');
Я не знаю, когда это может быть полезно, в любом случае, это довольно странно и весело:
var count = function(counter){
alert(counter);
if (counter < 10){
return arguments.callee(counter+1);
}
return arguments.callee;
};
count(5)(9); // Will alert 5, 6, 7, 8, 9, 10 and 9, 10
На самом деле, похоже, что Фреймворк FAB для Node.js реализовал эту функцию; см., например, Эта тема.
Закрытие:
function f() {
var a;
function closureGet(){ return a; }
function closureSet(val){ a=val;}
return [closureGet,closureSet];
}
[closureGet,closureSet]=f();
closureSet(5);
alert(closureGet()); // gives 5
closureSet(15);
alert(closureGet()); // gives 15
Дело здесь не в так называемом деструктурирующем назначении ([c,d] = [1,3] эквивалентно c=1; d=3;), а в том факте, что вхождения a в closureGet и closureSet по-прежнему относятся к одной и той же переменной. Даже после того, как closureSet присвоил a новое значение!
Я считаю, что вы делаете с [closureGet,closureSet]=f();является назначение деструктуризации. Это не работает в nodejs (Google V8), но работает в smjs (Spidermonkey Mozilla).
Вы правы, это деструктурирующее присваивание, но читайте внимательнее: я хотел сказать, что [a, b] = f () на самом деле интересно, но «закрытие» находится где-то в другом месте. Поскольку [a, b] = f (), когда вы видите его в первый раз, привлекает больше внимания, чем должно быть в этом примере.
Это скрытая функция jQuery, а не Javascript, но поскольку вопрос о "скрытых функциях jQuery" никогда не возникает ...
Вы можете определить свои собственные селекторы :something в jQuery:
$.extend($.expr[':'], {
foo: function(node, index, args, stack) {
// decide if selectors matches node, return true or false
}
});
Для выбора с использованием :foo, такого как $('div.block:foo("bar,baz") span'), функция foo будет вызываться для всех узлов, которые соответствуют уже обработанной части селектора. Смысл аргументов:
node содержит текущий узелindex - это индекс узла в наборе узлов.args - это массив, который полезен, если у селектора есть аргумент или несколько имен:
args[0] - это весь текст селектора (например, :foo("bar, baz"))args[1] - это имя селектора (например, foo).args[2] - это символ кавычки, используемый для обертывания аргумента.
(например, " для :foo("bar, baz")) или пустая строка, если нет кавычек
(:foo(bar, baz)) или undefined, если аргумента нетargs[3] - это аргумент, включая любые кавычки (например, "bar, baz")
или undefined, если нет аргументовstack - это набор узлов (массив, содержащий все узлы, которые совпадают в этой точке)Функция должна вернуть true, если селектор совпадает, иначе false.
Например, следующий код позволит выбирать узлы на основе полнотекстового поиска по регулярному выражению:
$.extend($.expr[':'], {
matches: function(node, index, args, stack) {
if (!args.re) { // args is a good place for caching
var re = args[3];
if (args[2]) { // get rid of quotes
re = re.slice(1,-1);
}
var separator = re[0];
var pos = re.lastIndexOf(separator);
var modifiers = re.substr(pos+1);
var code = re.substr(1, pos-1);
args.re = new RegExp(code, modifiers);
}
return $(node).text().match(args.re);
}
});
// find the answers on this page which contain /**/-style comments
$('.answer .post-text code:matches(!/\*[\s\S]*\*/!)');
Вы можете достичь аналогичного эффекта с версией обратного вызова .фильтр(), но пользовательские селекторы гораздо более гибкие и обычно более читабельные.
Очень хорошее описание, я делал это раньше, но детали всегда ускользают от меня. Тем не менее, я мог бы предложить указать, какие версии jQuery действительны / протестированы.
@kenb: проверено с 1.3.2 и 1.4.2. Содержимое args отличается от 1.2.6.
Скрытые (или малоизвестные) возможности jQuery: stackoverflow.com/questions/121965/…
Когда вы пишете обратные вызовы, у вас много кода, который будет выглядеть так:
callback: function(){
stuff(arg1,arg2);
}
Вы можете использовать приведенную ниже функцию, чтобы сделать ее несколько чище.
callback: _(stuff, arg1, arg2)
Он использует менее известную функцию объекта Function JavaScript, apply.
Он также показывает другой символ, который вы можете использовать как имя функции: _.
function _(){
var func;
var args = new Array();
for(var i = 0; i < arguments.length; i++){
if ( i == 0){
func = arguments[i];
} else {
args.push(arguments[i]);
}
}
return function(){
return func.apply(func, args);
}
}
Существуют похожие реализации, в первую очередь метод Function.prototype.bind() в ECMAScript 5th Edition и библиотека PrototypeJS. С этим вы можете сделать stuff.bind(null, arg1, arg2);. См. Ответ Бобинса здесь.
Это действительно каррирование функции, поэтому я бы назвал функцию curry вместо _. Также в идеале карри должен быть методом Function.prototype.
Не поддерживает использование из объектного контекста. Измените первый параметр, чтобы применить к this. А как насчет просто: function _() { var args = Array.prototype.slice.call(arguments); return function() { args.shift().apply(this,args); }; }
@nicerobot Tnx, это получше. @ Бесконечность Это не каррирование. По крайней мере, в haskell это означает изменение типа функции <code> (a, b) -> c на a -> b -> c </code> <br/> См. Дополнительную информацию: haskell.org/haskellwiki/Currying С каррированием вы можете сделать например: <code> func (1,2,3) == func (1) (2,3) == func (1) (2) (3). </code> Существует множество реализаций каррирования в javascript. <br/> Это действительно похоже на привязку.
Это менее читаемая форма частичного приложения.
Разве вы не имели в виду: «Увидев репутацию и взгляды на этот другой вопрос, я подумал, что задам почти тот же вопрос, чтобы повысить свой собственный»? ;-)