Відкрийте функціональний JavaScript було визнано однією з найкращих нових книг про функціональне програмування від BookAuthority !
JavaScript має примітиви, об'єкти та функції. Всі вони - цінності. Усі вони розглядаються як об’єкти, навіть примітивні.
Примітиви
Число, логічне значення, рядок undefined
і null
є примітивами.
Кількість
У JavaScript є лише один тип числа, 64-розрядний двійковий тип із плаваючою комою. Арифметика десяткових чисел неточна.
Як ви вже можете знати, 0.1 + 0.2
не робить 0.3
. Але з цілими числами арифметика є точною, тому 1+2 === 3
.
Числа успадковують методи від Number.prototype
об'єкта. Методи можна викликати за номерами:
(123).toString(); //"123" (1.23).toFixed(1); //"1.2"
Є функції для перетворення рядків у число: Number.parseInt()
, Number.parseFloat()
а Number()
:
Number.parseInt("1") //1 Number.parseInt("text") //NaN Number.parseFloat("1.234") //1.234 Number("1") //1 Number("1.234") //1.234
Недійсні арифметичні операції або недійсні перетворення не спричинять виняток, але призведуть до значення NaN
"Не-число". Number.isNaN()
може виявити NaN
.
+
Оператор може додати або конкатенації.
1 + 1 //2 "1" + "1" //"11" 1 + "1" //"11"
Рядок
Рядок зберігає серію символів Unicode. Текст може бути всередині подвійних лапок ""
або одинарних лапок ''
.
Рядки успадковують методи від String.prototype
. У них є такі методи , як: substring()
, indexOf()
і concat()
.
"text".substring(1,3) //"ex" "text".indexOf('x') //2 "text".concat(" end") //"text end"
Рядки, як і всі примітивні, незмінні. Наприклад concat()
, не змінює існуючий рядок, а створює новий.
Логічна
Логічне значення має два значення: true
і false
.
Мова має неправдиве та хибне значення.
false
, null
, undefined
, ''
(Порожній рядок), 0
і NaN
є falsy. Усі інші значення, включаючи всі об'єкти, є істинними.
Істинне значення обчислюється true
при виконанні в логічному контексті. Оцінюється значення хибності false
. Погляньте на наступний приклад відображення false
гілки.
let text = ''; if(text) { console.log("This is true"); } else { console.log("This is false"); }
Оператор рівності є ===
. Не рівним оператором є !==
.
Змінні
Змінні можна визначити, використовуючи var
, let
і const
.
var
оголошує та додатково ініціалізує змінну. Змінні, оголошені з, var
мають область функцій. Вони розглядаються як заявлені у верхній частині функції. Це називається змінним підйомом.
let
Декларація має блочну область видимості.
Значення змінної, яка не ініціалізується, є undefined
.
Змінна, оголошена з, const
не може бути перепризначена. Однак його значення все ще може бути змінним. const
заморожує змінну, Object.freeze()
заморожує об'єкт. const
Декларація має блочну область видимості.
Об'єкти
Об'єкт - це динамічна сукупність властивостей.
Ключ властивості - це унікальний рядок. Коли в якості ключа властивості використовується не рядок, він буде перетворений у рядок. Значенням властивості може бути примітив, об'єкт або функція.
Найпростіший спосіб створити об'єкт - це використання літералу об'єкта:
let obj = { message : "A message", doSomething : function() {} }
Існує два способи доступу до властивостей: крапкова нотація та нотація дужок. Ми можемо читати, додавати, редагувати та видаляти властивості об’єкта в будь-який час.
- отримаємо:
object.name
,object[expression]
- набір:
object.name = value,
object[expression] = value
- видалити:
delete object.name
,delete object[expression]
let obj = {}; //create empty object obj.message = "A message"; //add property obj.message = "A new message"; //edit property delete obj.message; //delete property
Об’єкти можна використовувати як карти. Просту карту можна створити за допомогою Object.create(null)
:
let french = Object.create(null); french["yes"] = "oui"; french["no"] = "non"; french["yes"];//"oui"
Усі властивості об’єкта є загальнодоступними. Object.keys()
може використовуватися для перебору всіх властивостей.
function logProperty(name){ console.log(name); //property name console.log(obj[name]); //property value } Object.keys(obj).forEach(logProperty);
Object.assign()
копіює всі властивості з одного об'єкта на інший. Об’єкт можна клонувати, скопіювавши всі його властивості в порожній об’єкт:
let book = { title: "The good parts" }; let clone = Object.assign({}, book);
Незмінний об'єкт - це об'єкт, який після створення не може бути змінений. Якщо ви хочете зробити об’єкт незмінним, використовуйте Object.freeze()
.
Примітиви проти об’єктів
Примітиви (крім null
і undefined
) трактуються як об’єкти в тому сенсі, що вони мають методи, але вони не є об’єктами.
Числа, рядки та логічні значення мають обертки, еквівалентні об'єкту. Це Number
, String
і Boolean
функції.
Щоб дозволити доступ до властивостей примітивів, JavaScript створює обгортковий об'єкт, а потім знищує його. Процес створення та знищення обгорткових об'єктів оптимізований механізмом JavaScript.
Примітиви незмінні, а предмети змінні.
Масив
Масиви - це індексовані набори значень. Кожне значення є елементом. Елементи впорядковуються та отримують доступ до них за номером індексу.
JavaScript має масивоподібні об'єкти. Масиви реалізовані за допомогою об'єктів. Індекси перетворюються у рядки та використовуються як імена для отримання значень.
Простий масив типу let arr = ['A', 'B', 'C']
імітується з використанням такого об’єкта, як наведений нижче:
{ '0': 'A', '1': 'B', '2': 'C' }
Зверніть увагу , що arr[1]
дає таке ж значення , як arr['1']
: arr[1] === arr['1']
.
Видалення значень із масиву за допомогою delete
залишить дірки. splice()
можна використовувати, щоб уникнути проблеми, але вона може бути повільною.
let arr = ['A', 'B', 'C']; delete arr[1]; console.log(arr); // ['A', empty, 'C'] console.log(arr.length); // 3
JavaScript’s arrays don’t throw “index out of range” exceptions. If the index is not available, it will return undefined
.
Stack and queue can easily be implemented using the array methods:
let stack = []; stack.push(1); // [1] stack.push(2); // [1, 2] let last = stack.pop(); // [1] console.log(last); // 2 let queue = []; queue.push(1); // [1] queue.push(2); // [1, 2] let first = queue.shift();//[2] console.log(first); // 1
Functions
Functions are independent units of behavior.
Functions are objects. Functions can be assigned to variables, stored in objects or arrays, passed as an argument to other functions, and returned from functions.
There are three ways to define a function:
- Function Declaration (aka Function Statement)
- Function Expression (aka Function Literal)
- Arrow Function
The Function Declaration
function
is the first keyword on the line- it must have a name
- it can be used before definition. Function declarations are moved, or “hoisted”, to the top of their scope.
function doSomething(){}
The Function Expression
function
is not the first keyword on the line- the name is optional. There can be an anonymous function expression or a named function expression.
- it needs to be defined, then it can execute
- it can auto-execute after definition (called “IIFE” Immediately Invoked Function Expression)
let doSomething = function() {}
Arrow Function
The arrow function is a sugar syntax for creating an anonymous functionexpression.
let doSomething = () => {};
Arrow functions don’t have their own this
and arguments
.
Function invocation
A function, defined with the function
keyword, can be invoked in different ways:
- Function form
doSomething(arguments)
- Method form
theObject.doSomething(arguments) theObject["doSomething"](arguments)
- Constructor form
new Constructor(arguments)
- Apply form
doSomething.apply(theObject, [arguments]) doSomething.call(theObject, arguments)
Functions can be invoked with more or fewer arguments than declared in the definition. The extra arguments will be ignored, and the missing parameters will be set to undefined
.
Functions (except arrow functions) have two pseudo-parameters: this
and arguments
.
this
Methods are functions that are stored in objects. Functions are independent. In order for a function to know on which object to work onthis
is used. this
represents the function’s context.
There is no point to use this
when a function is invoked with the function form: doSomething()
. In this case this
is undefined
or is the window
object, depending if the strict mode is enabled or not.
When a function is invoked with the method form theObject.doSomething()
,this
represents the object.
When a function is used as a constructor new Constructor()
, this
represents the newly created object.
The value of this
can be set with apply()
or call()
:doSomething.apply(theObject)
. In this case this
is the object sent as the first parameter to the method.
The value of this
depends on how the function was invoked, not where the function was defined. This is of course a source of confusion.
arguments
The arguments
pseudo-parameter gives all the arguments used at invocation. It’s an array-like object, but not an array. It lacks the array methods.
function log(message){ console.log(message); } function logAll(){ let args = Array.prototype.slice.call(arguments); return args.forEach(log); } logAll("msg1", "msg2", "msg3");
An alternative is the new rest parameters syntax. This time args
is an array object.
function logAll(...args){ return args.forEach(log); }
return
A function with no return
statement returns undefined
. Pay attention to the automatic semi-colon insertion when using return
. The following function will not return an empty object, but rather an undefined
one.
function getObject(){ return { } } getObject()
To avoid the issue, use {
on the same line as return
:
function getObject(){ return { } }
Dynamic Typing
JavaScript has dynamic typing. Values have types, variables do not. Types can change at run time.
function log(value){ console.log(value); } log(1); log("text"); log({message : "text"});
The typeof()
operator can check the type of a variable.
let n = 1; typeof(n); //number let s = "text"; typeof(s); //string let fn = function() {}; typeof(fn); //function
A Single Thread
The main JavaScript runtime is single threaded. Two functions can’t run at the same time. The runtime contains an Event Queue which stores a list of messages to be processed. There are no race conditions, no deadlocks.However, the code in the Event Queue needs to run fast. Otherwise the browser will become unresponsive and will ask to kill the task.
Exceptions
JavaScript has an exception handling mechanism. It works like you may expect, by wrapping the code using the try/catch
statement. The statement has a single catch
block that handles all exceptions.
It’s good to know that JavaScript sometimes has a preference for silent errors. The next code will not throw an exception when I try to modify a frozen object:
let obj = Object.freeze({}); obj.message = "text";
Strict mode eliminates some JavaScript silent errors. "use strict";
enables strict mode.
Prototype Patterns
Object.create()
, constructor function, and class
build objects over the prototype system.
Consider the next example:
let servicePrototype = { doSomething : function() {} } let service = Object.create(servicePrototype); console.log(service.__proto__ === servicePrototype); //true
Object.create()
builds a new object service
which has theservicePrototype
object as its prototype. This means that doSomething()
is available on the service
object. It also means that the __proto__
property of service
points to the servicePrototype
object.
Let’s now build a similar object using class
.
class Service { doSomething(){} } let service = new Service(); console.log(service.__proto__ === Service.prototype);
All methods defined in the Service
class will be added to theService.prototype
object. Instances of the Service
class will have the same prototype (Service.prototype
) object. All instances will delegate method calls to the Service.prototype
object. Methods are defined once onService.prototype
and then inherited by all instances.
Prototype chain
Objects inherit from other objects. Each object has a prototype and inherits their properties from it. The prototype is available through the “hidden” property __proto__
.
When you request a property which the object does not contain, JavaScript will look down the prototype chain until it either finds the requested property, or until it reaches the end of the chain.
Functional Patterns
JavaScript has first class functions and closures. These are concepts that open the way for Functional Programming in JavaScript. As a result, higher order functions are possible.
filter()
, map()
, reduce()
are the basic toolbox for working with arrays in a function style.
filter()
selects values from a list based on a predicate function that decides what values should be kept.
map()
transforms a list of values to another list of values using a mapping function.
let numbers = [1,2,3,4,5,6]; function isEven(number){ return number % 2 === 0; } function doubleNumber(x){ return x*2; } let evenNumbers = numbers.filter(isEven); //2 4 6 let doubleNumbers = numbers.map(doubleNumber); //2 4 6 8 10 12
reduce()
reduces a list of values to one value.
function addNumber(total, value){ return total + value; } function sum(...args){ return args.reduce(addNumber, 0); } sum(1,2,3); //6
Closure is an inner function that has access to the parent function’s variables, even after the parent function has executed. Look at the next example:
function createCount(){ let state = 0; return function count(){ state += 1; return state; } } let count = createCount(); console.log(count()); //1 console.log(count()); //2
count()
is a nested function. count()
accesses the variable state
from its parent. It survives the invocation of the parent function createCount()
.count()
is a closure.
A higher order function is a function that takes another function as an input, returns a function, or does both.
filter()
, map()
, reduce()
are higher-order functions.
A pure function is a function that returns a value based only of its input. Pure functions don’t use variables from the outer functions. Pure functions cause no mutations.
In the previous examples isEven()
, doubleNumber()
, addNumber()
and sum()
are pure functions.
Conclusion
The power of JavaScript lies in its simplicity.
Knowing the JavaScript fundamentals makes us better at understanding and using the language.
Learn functional React, in a project-based way, with Functional Architecture with React and Redux.
Discover Functional JavaScript was named one of thebest new Functional Programming books by BookAuthority!
For more on applying functional programming techniques in React take a look atFunctional React.
Follow on Twitter