Различия между function declaration и function expression в javascript. Функциональные выражения в JavaScript Первоклассное выражение javascript

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

Создание функции посредством выражения определения

В JavaScript создать функцию можно не только с помощью традиционного способа (объявления - Traditional Declarations), но и посредством выражения определения (Definition Expressions). Этот способ определения функции в некоторых источниках носит названия функционального литерала.

Основная идея Definition Expressions заключается в том, что описание функции используют в качестве значения некоторой переменной или выражения.

Например, переменная sum будет содержать описание функции, которая будет выводить в консоль сумму 2 чисел, указанных в качестве параметра.

Var sum = function(num1,num2) { return console.log(num1+num2); };

Вызов функции, созданной на основании выражения определения, осуществляется по имени переменной.

//например, вызовем функцию, которая содержится в переменной sum и передадим ей 2 аргумента. sum(7,4);

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

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

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

Var factorial = function fact(num) { if (num

Этот код определяет функцию и сохраняет ссылку на неё в переменную factorial . Для вызова функции внутри этой функции используем имя fact . Обратите внимание, что обратиться к функции вне её тела по имени fact нельзя, для этого необходимо использовать переменную factorial .

Но и в этом случае можно обойтись без имени, т.к. JavaScript позволяет вызвать функцию внутри своего тела с помощью свойства callee объекта arguments .

Var factorial = function(num) { if (num

JavaScript - Самовызывающаяся функция

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

Например, вызовем функцию sum немедленно со значениями параметров 7 и 4.

Var sum = function(num1,num2) { return console.log(num1+num2); }(7,4);

Процесс немедленного вызова функции иногда называют инициализацией или инстанциацией функции.

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

(function(num1,num2) { return console.log(num1+num2); }(7,4));

Отличия Function Declaration и Function Expression

Основные различия между функциями Function Declaration и Function Expression представим в следующей таблице:

Function Declaration Function Expression
Браузер (интерпретатор JavaScript) считает функцию - Function Declaration, если она расположена в основном потоке кода (не является частью какого-либо выражения). Браузер находит функцию как Function Expression, если она размещена в составе некоторого выражения.
При объявлении функции этим способом, переменная, по которой происходит обращение к ней, создаётся автоматически. function square(a) { return a*a; } // обращение к функции console.log(square(5)); Для обращения к функции необходимо создать переменную и сохранить в неё ссылку на эту функцию. var square = function(a) { return a*a; } // обращение к функции console.log(square(5));
Инициализируется до выполнения кода (в соотвествующей области видимости). Данное действие ещё называют поднятием или hoisting (хойстингом). Такие функции можно вызывать до объявления. // вызываем функцию до её объявления console.log(square(7)); function square(a) { return a*a; } Функции Function Expression нельзя использовать до объявления. Поднимается только сама переменная. // ошибка при вызове функции console.log(square(7)); var square = function(a) { return a*a; }

В этом случае происходит следующее:

Var square; console.log(square(7)); // но на этом этапе переменная square имеет значение undefined square = function(a) { return a*a; }

При использовании use strict функция, объявленная как Function Declaration, будет видна только внутри блока, в котором она объявлена. "use strict"; if (true) { function sum(a,b,c) { return a+b+c; } console.log(sum(10,20,10)); } // ошибка доступа к функции sum console.log(sum(4,5,4)); В отличие от Function Declaration, доступ к функции можно получить вне блока, в котором она создана: "use strict"; if (true) { var sum = function (a,b,c) { return a+b+c; } console.log(sum(10,20,10)); } // имеем доступ к функции sum console.log(sum(4,5,4));
Функцию, объявленную как Function Declaration вызвать немедленно нельзя.
Для того чтобы это осуществить необходимо функцию сделать частью выражения, например, обернуть её в круглые скобки. После этого функция будет считаться частью выражения (Function Expression) и её можно вызвать немедленно.
Например: var sum = (function sum(a,b,c) { return a+b+c; })(5,6,7); console.log(typeof sum); // number console.log(sum); // number
Функцию, объявленную как Function Expression можно вызвать немедленно. var sum = function (a,b,c) { return a+b+c; }(5,6,7); console.log(typeof sum); // number console.log(sum); // number При этом переменная sum (в данном случае) содержит уже не ссылку на функцию, а её результат (в этом примере число).

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

Functions are one of the fundamental building blocks in JavaScript. A function is a JavaScript procedure-a set of statements that performs a task or calculates a value. To use a function, you must define it somewhere in the scope from which you wish to call it.

A method is a function that is a property of an object. Read more about objects and methods in Working with objects .

Calling functions

Defining a function does not execute it. Defining the function simply names the function and specifies what to do when the function is called. Calling the function actually performs the specified actions with the indicated parameters. For example, if you define the function square , you could call it as follows:

Square(5);

The preceding statement calls the function with an argument of 5. The function executes its statements and returns the value 25.

Functions must be in scope when they are called, but the function declaration can be hoisted (appear below the call in the code), as in this example:

Console.log(square(5)); /* ... */ function square(n) { return n * n; }

The scope of a function is the function in which it is declared, or the entire program if it is declared at the top level.

Note: This works only when defining the function using the above syntax (i.e. function funcName(){}). The code below will not work. That means, function hoisting only works with function declaration and not with function expression.

Console.log(square); // square is hoisted with an initial value undefined. console.log(square(5)); // Uncaught TypeError: square is not a function var square = function(n) { return n * n; }

The arguments of a function are not limited to strings and numbers. You can pass whole objects to a function. The show_props() function (defined in ) is an example of a function that takes an object as an argument.

A function can call itself. For example, here is a function that computes factorials recursively:

Function factorial(n) { if ((n === 0) || (n === 1)) return 1; else return (n * factorial(n - 1)); }

You could then compute the factorials of one through five as follows:

Var a, b, c, d, e; a = factorial(1); // a gets the value 1 b = factorial(2); // b gets the value 2 c = factorial(3); // c gets the value 6 d = factorial(4); // d gets the value 24 e = factorial(5); // e gets the value 120

There are other ways to call functions. There are often cases where a function needs to be called dynamically, or the number of arguments to a function vary, or in which the context of the function call needs to be set to a specific object determined at runtime. It turns out that functions are, themselves, objects, and these objects in turn have methods (see the Function object). One of these, the apply() method, can be used to achieve this goal.

Function scope

Variables defined inside a function cannot be accessed from anywhere outside the function, because the variable is defined only in the scope of the function. However, a function can access all variables and functions defined inside the scope in which it is defined. In other words, a function defined in the global scope can access all variables defined in the global scope. A function defined inside another function can also access all variables defined in its parent function and any other variable to which the parent function has access.

// The following variables are defined in the global scope var num1 = 20, num2 = 3, name = "Chamahk"; // This function is defined in the global scope function multiply() { return num1 * num2; } multiply(); // Returns 60 // A nested function example function getScore() { var num1 = 2, num2 = 3; function add() { return name + " scored " + (num1 + num2); } return add(); } getScore(); // Returns "Chamahk scored 5"

Scope and the function stack

Recursion

A function can refer to and call itself. There are three ways for a function to refer to itself:

  1. the function"s name
  2. an in-scope variable that refers to the function

For example, consider the following function definition:

Var foo = function bar() { // statements go here };

Within the function body, the following are all equivalent:

  1. bar()
  2. arguments.callee()
  3. foo()

A function that calls itself is called a recursive function . In some ways, recursion is analogous to a loop. Both execute the same code multiple times, and both require a condition (to avoid an infinite loop, or rather, infinite recursion in this case). For example, the following loop:

Var x = 0; while (x < 10) { // "x < 10" is the loop condition // do stuff x++; }

can be converted into a recursive function and a call to that function:

Function loop(x) { if (x >= 10) // "x >= 10" is the exit condition (equivalent to "!(x < 10)") return; // do stuff loop(x + 1); // the recursive call } loop(0);

However, some algorithms cannot be simple iterative loops. For example, getting all the nodes of a tree structure (e.g. the DOM) is more easily done using recursion:

Function walkTree(node) { if (node == null) // return; // do something with node for (var i = 0; i < node.childNodes.length; i++) { walkTree(node.childNodes[i]); } }

Compared to the function loop , each recursive call itself makes many recursive calls here.

It is possible to convert any recursive algorithm to a non-recursive one, but often the logic is much more complex and doing so requires the use of a stack. In fact, recursion itself uses a stack: the function stack.

The stack-like behavior can be seen in the following example:

Function foo(i) { if (i < 0) return; console.log("begin: " + i); foo(i - 1); console.log("end: " + i); } foo(3); // Output: // begin: 3 // begin: 2 // begin: 1 // begin: 0 // end: 0 // end: 1 // end: 2 // end: 3

Nested functions and closures

You can nest a function within a function. The nested (inner) function is private to its containing (outer) function. It also forms a closure . A closure is an expression (typically a function) that can have free variables together with an environment that binds those variables (that "closes" the expression).

Since a nested function is a closure, this means that a nested function can "inherit" the arguments and variables of its containing function. In other words, the inner function contains the scope of the outer function.

  • The inner function can be accessed only from statements in the outer function.
  • The inner function forms a closure: the inner function can use the arguments and variables of the outer function, while the outer function cannot use the arguments and variables of the inner function.

The following example shows nested functions:

Function addSquares(a, b) { function square(x) { return x * x; } return square(a) + square(b); } a = addSquares(2, 3); // returns 13 b = addSquares(3, 4); // returns 25 c = addSquares(4, 5); // returns 41

Since the inner function forms a closure, you can call the outer function and specify arguments for both the outer and inner function:

Function outside(x) { function inside(y) { return x + y; } return inside; } fn_inside = outside(3); // Think of it like: give me a function that adds 3 to whatever you give // it result = fn_inside(5); // returns 8 result1 = outside(3)(5); // returns 8

Preservation of variables

Notice how x is preserved when inside is returned. A closure must preserve the arguments and variables in all scopes it references. Since each call provides potentially different arguments, a new closure is created for each call to outside . The memory can be freed only when the returned inside is no longer accessible.

This is not different from storing references in other objects, but is often less obvious because one does not set the references directly and cannot inspect them.

Multiply-nested functions

Functions can be multiply-nested, i.e. a function (A) containing a function (B) containing a function (C). Both functions B and C form closures here, so B can access A and C can access B. In addition, since C can access B which can access A, C can also access A. Thus, the closures can contain multiple scopes; they recursively contain the scope of the functions containing it. This is called scope chaining . (Why it is called "chaining" will be explained later.)

Consider the following example:

Function A(x) { function B(y) { function C(z) { console.log(x + y + z); } C(3); } B(2); } A(1); // logs 6 (1 + 2 + 3)

In this example, C accesses B "s y and A "s x . This can be done because:

  1. B forms a closure including A , i.e. B can access A "s arguments and variables.
  2. C forms a closure including B .
  3. Because B "s closure includes A , C "s closure includes A , C can access both B and A "s arguments and variables. In other words, C chains the scopes of B and A in that order.

The reverse, however, is not true. A cannot access C , because A cannot access any argument or variable of B , which C is a variable of. Thus, C remains private to only B .

Name conflicts

When two arguments or variables in the scopes of a closure have the same name, there is a name conflict . More inner scopes take precedence, so the inner-most scope takes the highest precedence, while the outer-most scope takes the lowest. This is the scope chain. The first on the chain is the inner-most scope, and the last is the outer-most scope. Consider the following:

Function outside() { var x = 5; function inside(x) { return x * 2; } return inside; } outside()(10); // returns 20 instead of 10

The name conflict happens at the statement return x and is between inside "s parameter x and outside "s variable x . The scope chain here is { inside , outside , global object}. Therefore inside "s x takes precedences over outside "s x , and 20 (inside "s x) is returned instead of 10 (outside "s x).

Closures

Closures are one of the most powerful features of JavaScript. JavaScript allows for the nesting of functions and grants the inner function full access to all the variables and functions defined inside the outer function (and all other variables and functions that the outer function has access to). However, the outer function does not have access to the variables and functions defined inside the inner function. This provides a sort of encapsulation for the variables of the inner function. Also, since the inner function has access to the scope of the outer function, the variables and functions defined in the outer function will live longer than the duration of the outer function execution, if the inner function manages to survive beyond the life of the outer function. A closure is created when the inner function is somehow made available to any scope outside the outer function.

Var pet = function(name) { // The outer function defines a variable called "name" var getName = function() { return name; // The inner function has access to the "name" variable of the outer //function } return getName; // Return the inner function, thereby exposing it to outer scopes } myPet = pet("Vivie"); myPet(); // Returns "Vivie"

It can be much more complex than the code above. An object containing methods for manipulating the inner variables of the outer function can be returned.

Var createPet = function(name) { var sex; return { setName: function(newName) { name = newName; }, getName: function() { return name; }, getSex: function() { return sex; }, setSex: function(newSex) { if(typeof newSex === "string" && (newSex.toLowerCase() === "male" || newSex.toLowerCase() === "female")) { sex = newSex; } } } } var pet = createPet("Vivie"); pet.getName(); // Vivie pet.setName("Oliver"); pet.setSex("male"); pet.getSex(); // male pet.getName(); // Oliver

In the code above, the name variable of the outer function is accessible to the inner functions, and there is no other way to access the inner variables except through the inner functions. The inner variables of the inner functions act as safe stores for the outer arguments and variables. They hold "persistent" and "encapsulated" data for the inner functions to work with. The functions do not even have to be assigned to a variable, or have a name.

Var getCode = (function() { var apiCode = "0]Eal(eh&2"; // A code we do not want outsiders to be able to modify... return function() { return apiCode; }; })(); getCode(); // Returns the apiCode

There are, however, a number of pitfalls to watch out for when using closures. If an enclosed function defines a variable with the same name as the name of a variable in the outer scope, there is no way to refer to the variable in the outer scope again.

Var createPet = function(name) { // The outer function defines a variable called "name". return { setName: function(name) { // The enclosed function also defines a variable called "name". name = name; // How do we access the "name" defined by the outer function? } } }

Using the arguments object

The arguments of a function are maintained in an array-like object. Within a function, you can address the arguments passed to it as follows:

Arguments[i]

where i is the ordinal number of the argument, starting at zero. So, the first argument passed to a function would be arguments . The total number of arguments is indicated by arguments.length .

Using the arguments object, you can call a function with more arguments than it is formally declared to accept. This is often useful if you don"t know in advance how many arguments will be passed to the function. You can use arguments.length to determine the number of arguments actually passed to the function, and then access each argument using the arguments object.

For example, consider a function that concatenates several strings. The only formal argument for the function is a string that specifies the characters that separate the items to concatenate. The function is defined as follows:

Function myConcat(separator) { var result = ""; // initialize list var i; // iterate through arguments for (i = 1; i < arguments.length; i++) { result += arguments[i] + separator; } return result; }

You can pass any number of arguments to this function, and it concatenates each argument into a string "list":

// returns "red, orange, blue, " myConcat(", ", "red", "orange", "blue"); // returns "elephant; giraffe; lion; cheetah; " myConcat("; ", "elephant", "giraffe", "lion", "cheetah"); // returns "sage. basil. oregano. pepper. parsley. " myConcat(". ", "sage", "basil", "oregano", "pepper", "parsley");

Note: The arguments variable is "array-like", but not an array. It is array-like in that it has a numbered index and a length property. However, it does not possess all of the array-manipulation methods.

Two factors influenced the introduction of arrow functions: shorter functions and non-binding of this .

Shorter functions

In some functional patterns, shorter functions are welcome. Compare:

Var a = [ "Hydrogen", "Helium", "Lithium", "Beryllium" ]; var a2 = a.map(function(s) { return s.length; }); console.log(a2); // logs var a3 = a.map(s => s.length); console.log(a3); // logs

No separate this

Until arrow functions, every new function defined its own value (a new object in the case of a constructor, undefined in function calls, the base object if the function is called as an "object method", etc.). This proved to be less than ideal with an object-oriented style of programming.

Function Person() { // The Person() constructor defines `this` as itself. this.age = 0; setInterval(function growUp() { // In nonstrict mode, the growUp() function defines `this` // as the global object, which is different from the `this` // defined by the Person() constructor. this.age++; }, 1000); } var p = new Person();

In ECMAScript 3/5, this issue was fixed by assigning the value in this to a variable that could be closed over.

Function Person() { var self = this; // Some choose `that` instead of `self`. // Choose one and be consistent. self.age = 0; setInterval(function growUp() { // The callback refers to the `self` variable of which // the value is the expected object. self.age++; }, 1000); }

Выражения в JavaScript представляют собой комбинации операндов и операторов .

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

Рис. 1. Структура выражения в JavaScript

Операнды — это данные, обрабатываемые сценарием JavaScript. В качестве операндов могут быть как простые типы данных, так и сложные, а также другие выражения.

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

В зависимости от количества операндов различают следующие типы операторов:
унарный — в операции участвует один операнд;
бинарный — в операции участвуют два операнда;
тернарный — комбинирует три операнда.

Простейшая форма выражения — литерал — нечто, вычисляемое само в себя, например, число 100 , строка "Hellow world" . Переменная тоже может быть выражением, так как она вычисляется в присвоенное ей значение.

Выражения и операторы в JavaScript

1. Арифметические операторы

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

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

Таблица 1. Арифметические операторы
Оператор/Операция Описание Приоритет
+ Сложение Складывает числовые операнды. Если один из операндов — строка, то результатом выражения будет строка. 12
- Вычитание Выполняет вычитание второго операнда из первого. 12
- Унарный минус Преобразует положительное число в отрицательное, и наоборот. 14
* Умножение Умножает два операнда. 13
/ Деление Делит первый операнд на второй. Результатом деления может являться как целое, так и число с плавающей точкой. 13
% Деление по модулю (остаток от деления) Вычисляет остаток, получаемый при целочисленном делении первого операнда на второй. Применяется как к целым числам, так и числам с плавающей точкой. 13
var x = 5, y = 8, z; z = x + y; // вернет 13 z = x - y; // вернет -3 z = - y; // вернет -8 z = x * y; // вернет 40 z = x / y; // вернет 0.625 z = y % x; // вернет 3

2. Операторы присваивания

Операторы присваивания используются для присваивания значений переменным. Комбинированные операторы позволяют сохранить первоначальное и последующее значение в одной переменной.

var a = 5; // присваиваем переменной a числовое значение 5 var b = "hellow"; // сохраняем в переменной b строку hellow var m = n = z = 10; // присваиваем переменным m, n, z числовое значение 10 x += 10; // равнозначно x = x + 10; x -= 10; // равнозначно x = x - 10; x *= 10; // равнозначно x = x * 10; x /= 10; // равнозначно x = x / 10; x %= 10; // равнозначно x = x % 10;

3. Операторы инкремента и декремента

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

var x = y = m = n = 5, z, s, k, l; z = ++x * 2; /* в результате вычислений вернет значение z = 12, x = 6, т.е. значение x сначала увеличивается на 1, а после выполняется операция умножения */ s = y++ * 2; /* в результате вычислений вернет значение s = 10, y = 6, т.е. сначала выполняется операция умножения, а после в переменной y сохраняется увеличенное на 1 значение */ k = --m * 2; // вернет значение k = 8, m = 4 l = n-- * 2; // вернет значение l = 10, n = 4

4. Операторы сравнения

Операторы сравнения используются для сопоставления операндов, результатом выражения может быть одно из двух значений — true или false . Операндами могут быть не только числа, но и строки, логические значения и объекты. Однако сравнение может выполняться только для чисел и строк, поэтому операнды, не являющиеся числами или строками, преобразуются.

Если оба операнда не могут быть успешно преобразованы в числа или строки, операторы всегда возвращают false .

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

Если один операнд является строкой/преобразуется в строку, а другой является числом/преобразуется в число, то оператор попытается преобразовать строку в число и выполнить сравнение чисел. Если строка не является числом, она преобразуется в значение NaN и результатом сравнения будет false .

Чаще всего операции сравнения используются при организации ветвлений в программах.

Таблица 4. Операторы сравнения
Оператор/Операция Описание Приоритет
== Равенство Проверяет две величины на совпадение, допуская преобразование типов. Возвращает true , если операнды совпадают, и false , если они различны. 9
!= Неравенство Возвращает true , если операнды не равны 9
=== Идентичность Проверяет два операнда на «идентичность», руководствуясь строгим определением совпадения. Возвращает true , если операнды равны без преобразования типов. 9
!== Неидентичность Выполняет проверку идентичности. Возвращает true , если операнды не равны без преобразования типов. 9
> Больше Возвращает true , если первый операнд больше второго, в противном случае возвращает false . 10
>= Больше или равно Возвращает true , если первый операнд не меньше второго, в противном случае возвращает false . 10
Возвращает true , если первый операнд меньше второго, в противном случае возвращает false . 10
Возвращает true , если первый операнд не больше второго, в противном случае возвращает false . 10
5 == "5"; // вернет true 5 != -5.0; // вернет true 5 === "5"; // вернет false false === false; // вернет true 1 !== true; // вернет true 1 != true; // вернет false, так как true преобразуется в 1 3 > -3; // вернет true 3 >= "4"; // вернет false

5. Логические операторы

Логические операторы позволяют комбинировать условия, возвращающие логические величины. Чаще всего используются в условном выражении if .

(2 < 3) && (3===3); // вернет true, так как выражения в обеих скобках дают true (x < 10 && x > 0); // вернет true, если значение x принадлежит промежутку от 0 до 10 !false; // вернет true

6. Побитовые операторы

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

Таблица 6. Побитовые операторы
Оператор/Операция Описание Приоритет
& Побитовый И Если оба бита равны 1 , то результирующий бит будет равен 1 . В противном случае результат равен 0 . 8
| Побитовый ИЛИ Если один из операндов содержит в позиции 1 , результат тоже будет содержать 1 в этой позиции, в противном случае результат в этой позиции будет равен 0 . 6
^ Исключающее ИЛИ Если одно, и только одно значение содержит 1 в какой-либо позиции, то и результат будет содержать 1 в этой позиции, в противном случае результат в этой позиции будет равен 0 . 7
~ Отрицание Выполняется операция побитового отрицания над двоичным представлением значения выражения. Любая позиция, содержащая 1 в исходном выражении, заменяется на 0 . Любая позиция, содержащая 0 в исходном выражении, становится равной 0 . Положительные числа начинаются с 0 , отрицательные - с -1 , поэтому ~ n == -(n+1) . 14
Оператор сдвигает биты первого операнда влево на число битовых позиций, установленных вторым операндом. Для заполнения позиций справа используются нули. Возвращают результат того же типа, что левый операнд. 11
>> Побитовый сдвиг вправо Оператор сдвигает биты первого операнда вправо на число битовых позиций, установленных вторым операндом. Цифры, сдвинутые за пределы диапазона, удаляются. Самый старший бит (32й) не меняется, чтобы сохранить знак результата. Если первый операнд положителен, старшие биты результата заполняются нулями; если первый операнд отрицателен, старшие биты результата заполняются единицами. Сдвиг значения вправо на одну позицию эквивалентен делению на 2 (с отбрасыванием остатка), а сдвиг вправо на две позиции эквивалентен делению на 4 и т. д. 11
>>> Побитовый сдвиг вправо без учета знака Оператор сдвигает биты первого операнда вправо на число битовых позиций, установленных вторым операндом. Слева добавляются нули независимо от знака первого операнда. Цифры, сдвинутые за пределы диапазона, удаляются. 11
var x = 9, y = 5, z = 2, s = -5, result; // 9 эквивалентно 1001, 5 эквивалентно 0101 result = x & y; // вернет 1 (эквивалентно 0001) result = x | y; // вернет 13 (эквивалентно 1101) result = x ^ y; // вернет 12 (эквивалентно 1100) result = ~ y; // вернет -6 (эквивалентно 1100) result = x << y; // вернет 288 (эквивалентно 100100000) result = x >> z; // вернет 2 (эквивалентно 10) result = s >>> z; // вернет 1073741822 (эквивалентно 111111111111111111111111111110)

7. Строковые операторы

Существует несколько операторов, которые работают со строками особым образом.

"1" + "10"; // вернет "110" "1" + 10; // вернет "110" 2 + 5 + " цветных карандашей"; // вернет "7 цветных карандашей" "Цветных карандашей " + 2 + 5; // вернет "Цветных карандашей 25" "1" > "10"; // вернет false "10" <= 10; // вернет true "СССР" == "ссср"; // вернет false x = "micro"; x+= "soft"; // вернет "microsoft"

8. Специальные операторы

Таблица 8. Специальные операторы
Оператор/Операция Описание Приоритет
. Обращение к свойству Осуществляет доступ к свойству объекта. 15
, Множественное вычисление Вычисляет несколько независимых выражений, записанных в одну строку. 1
Индексация массива Осуществляет доступ к элементам массива или свойствам объекта. 15
() Вызов функции, группировка Группирует операции или вызывает функцию. 15
typeof Определение типа данных Унарный оператор, возвращает тип данных операнда. 14
instanceof Проверка типа объекта Оператор проверяет, является ли объект экземпляром определенного класса. Левый операнд должен быть объектом, правый - должен содержать имя класса объектов. Результат будет true , если объект, указанный слева, представляет собой экземпляр класса, указанного справа, в противном случае - false . 10
in Проверка наличия свойства В качестве левого операнда должна быть строка, а правым - массив или объект. Если левое значение является свойством объекта, вернется результат true . 10
new Создание объекта Оператор создает новый объект с неопределенными свойствами, затем вызывает функцию-конструктор для его инициализации (передачи параметров). Также может применяться для создания массива. 1
delete Удаление Оператор позволяет удалять свойство из объекта или элемент из массива. Возвращает true , если удаление прошло успешно, в противном случае false . При удалении элемента массива его длина не меняется. 14
void Определение выражения без возвращаемого значения Унарный оператор, отбрасывает значение операнда и возвращает underfined . 14
?: Операция условного выражения Тернарный оператор, позволяет организовать простое ветвление. В выражении участвуют три операнда, первый должен быть логическим значением или преобразовываться в него, а второй и третий - любыми значениями. Если первый операнд равен true , то условное выражение примет значение второго операнда; если false - то третьего. 3
document.write("hello world"); // выводит на экран строку hello world i = 0, j = 1; // сохраняет значения в переменных function1(10, 5); // вызов функции function1 с параметрами 10 и 5 var year = ; // создает массив с элементами typeof {a:1}; // вернет "object" var d = new Date(); // создаем новый объект с помощью конструктора Date() d instanceof Date; // вернет true var mycar = {make: "Honda", model: "Accord", year: 2005}; "make" in mycar; // вернет true var obj = new Object(); // создает пустой объект var food = ["milk", "bread", "meat", "olive oil", "cheese"]; delete food; // удаляет четвертый элемент из массива food x > 10 ? x * 2: x / 2; // возвращает значение x * 2, если x > 10, в противном случае x / 2

9. Комментарии в JavaScript

Однострочный комментарий: перед текстом комментария нужно поставить символы // .

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

Есть множество различных способов объявить функцию:

Function A() {}; // декларация функции var B = function () {}; // функциональное выражение var C = (function () {}); // функциональное выражение с оператором группировки var D = function foo () {}; // именованное функциональное выражение var E = (function () {})(); // самовызывающееся функциональное выражение var F = new Function(); // конструктор функции var G = new function() {}; // вырожденный случай: конструктор объекта
В таком обилии сложно не запутаться, не так ли? Как правило, в повседневной жизни мы используем не более трех различных типов объявления функций, и это отлично работает. Однако если копнуть поглубже, то может оказаться, что большинство из нас даже не подозревает какой объём таинств и подводных камней хранит в себе операция объявления функции.

Согласно документации ECMA синтаксис определения функции следующий:
ДекларацияФункции: function Идентификатор (Параметры) { ТелоФункции } ФункциональноеВыражение: function Идентификатор (опционально) (Параметры) { ТелоФункции }
Хоть и выглядят эти определения вполне схожими, но между декларацией функции и функциональным выражением есть большая разница. Декларация функции (Function Declaration ) создается до выполнения любого кода, в то время как функциональное выражение (Function Expression ) будет создано только в момент, когда интерпретатор дойдёт до данной строки кода.

Функциональное выражение - это объявление функции в контексте какого-либо выражения.

Рассмотрим несколько примеров функциональных выражений:

Оператор присваивания
var a = function() {};
Это классический пример задания функционального выражения через присваивание. Оператор присваивания ожидает справа выражение, именно поэтому функция становится частью выражения.
Немного пофантазировав, можно придумать следующие примеры:

Var a = function() { return 1; }() + 12; // 13 var b = function() { return 1; } + ""; // function (){return 1} var c = function() { return 1; } + "" - 1; //NaN

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

Function foo() { return 1; } // undefined function foo() { return 1; }(); // Uncaught SyntaxError: Expected () to start arrow function, but got "}" instead of "=>" (function foo() { return 1; }()) // 1 (function foo() { return 1; })() // 1
Принципиальной разницы между третьим и четвертым вариантом нет, так как в первом случае мы выполняем выражение, которое определяет и сразу же исполняет функцию, а во втором случае выполняется выражение, определяющее функцию, которая затем будет выполнена.

Оператор запятая
Оператор запятая вычисляет значение каждого своего операнда (слева направо) и возвращает значение последнего операнда.

0, function() { return 1; }(); // 1

Операторы (+, -, !, ~, void)
+function() { return false; }(); // 0 -function() { return false; }(); // -0 !function() { return false; }(); // true ~function() { return false; }(); // -1 void function() { return false; }(); //undefined
Комбинированные операторы:
!-function () { return false; }(); // true var c = 5 * (2 - function () {return 1}()) // 5 var c = 5 * 2 - -~function () {return 1}() // 8

Отличие именованных функциональных выражений от не именованных:

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

Var f = function getFactorial (n) { return n ? n * getFactorial(n - 1) : 1; }; f(5); // 120

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