JavaScript: Abstraction, Data Types, and Expressions
Introduction
In this article, we will discuss the concept of abstraction, specifically control and data abstraction in the context of JavaScript, as well as the supported data types and expressions in JavaScript.
Abstraction:
To start, we will define and explore abstraction.
In general, abstraction is the act of hiding something. In computing, it means to hide simplistic details in order to focus on higher level problems.
Based on the definition above, programming languages are abstractions of the machine they run on.
There are two classes of abstraction mechanisms:
- Control abstraction
- Data abstraction
We will focus on control abstraction for now.
Control Abstraction:
Control abstraction can be defined as the process of extracting the essential characteristics of control by defining abstract mechanisms and their associated characteristics, while disregarding low-level details and the entities to be controlled. In other words, it is the act of keeping certain information hidden in order to focus on some specific points that are often more complex. It helps by providing the programmer with the ability to hide procedural data.
Abstraction is used by programmers at every level. Typically, when asked to figure out the solution to a complex problem, a programmer will decompose the problem into smaller, more manageable tasks. This helps manage the complexity of this seemingly overwhelming task and lays the groundwork for the steps needed to tackle the problem at hand. Once these tasks have been turned into functions, they can be called in order to simplify the process of the larger task down the road.
Functions exist specifically for breaking down more complex problems into manageable pieces. A function is declared with a name in order to be called to accomplish a specific task or return a value. Functions also can be classified into their own unique types, first order functions or higher-order functions.
First order functions are treated like any other variable. They are typically used to store purely data-driven behaviors like so:
These simplistic functions work just fine when focusing on much smaller, data-oriented tasks, but what if we need to multiply several numbers? We would have to call a function every time we want to multiply a set of numbers.
That’s where higher-order functions are used.
Higher-order functions are functions in a language that either take a function as an argument or return a function after execution. This can be helpful when trying to link several tasks together into a program.
Let’s say we wanted to raise a number to the power of three.
By taking advantage of abstraction, we can make more complex tasks without the hassle of constantly reinventing the wheel.
Data Abstraction:
Continuing with the concept of abstraction, data abstraction is used to handle complexity by hiding unnecessary details from the user which enables the programmer to implement more complex logic using the provided abstraction with no concerns about the hidden complexity. In other words, data abstraction reduces the details so that the developer can focus on a few concepts at a time. Therefore, data abstraction improves understandability and maintainability of the code.
JavaScript is a weakly typed language and does not have the classical built-in support for abstraction like OOP languages. Instead, JavaScript has the capability to support OOP, and thus can achieve OOP functionalities, which is known as Object Oriented JavaScript (OOJS). Data abstraction can be achieved by inheritance and composition.
In JavaScript, prototypes and closures can help us accomplish data abstraction. Interfaces and abstract classes are examples of data abstractions, but JavaScript does not have such built-in features. Alternatively we can create interfaces using prototypes. Here is an example of how prototypes enable us to attain abstraction by defining interfaces and using them. To have a better understanding, it is useful to mention that Prototype-based programming is an OOP model that doesn’t use classes, instead it accomplishes the behavior of a class and then reuse it.
In the code above, only the methods that Array interface will have are declared. In the next step, we will define an implementation for Array.
By defining interfaces, objects can be reused when created using dependency injection pattern. There are libraries that supports dependency injection, it can also be created using manual dependency injection on start of the application by defining every variable. Having defined dependencies at the beginning, we could easily change type of Array without touching any code but the configuration for dependencies.
We can also accomplish data abstraction and information hiding through closure, which is an abstraction mechanism that allows separation of concerns and helps to keep things private when necessary. A closure is a structure made from nested functions in such a way that allows us to break through the scope chain. By breaking the scope chain, closures allow access to data within inner scope that would otherwise be inaccessible by outer scope.
In the following example, we are separating instrumentation (counting calls) from semantics (an error-reporting API), therefore we use a warning function to count the attempts with no need to concern about how it is implemented as well as protecting it from modification.
JavaScript also provides some built-in abstractions that have already been made available to the programmers to use such as map, filter, and reduce. For example, let’s look at map function. Array.prototype.map() is a higher-order function that abstracts the process of creating a new array with the results of calling a provided function on every element in the calling array.
The map method is very useful and allows construction of a complex behavior with very small code. Below is a simple example of how map method enables us to apply a simple mathematical function (Y = 2X + 4) to every element of an array.
The techniques presented above to implement data abstraction are not the only ones that can be used in JavaScript, which is very flexible in terms of how object-oriented programming and its different characteristics such as data abstraction can be performed.
Data Types:
In JavaScript a variable can contain any type of data and can have the value of the variable change type at any time. This flexibility in data types makes JavaScript “dynamically typed”, meaning that the programming language supports many data types, but any particular variable is not bound to any one of them.
In JavaScript, there are seven basic data types that belong to two groups.
Primitive Data Types — A primitive data value is a single simple data value with no additional properties and methods
- Number
- String
- Boolean
- Null
- Undefined
Complex Data Types — characterized by the typeof operator, which can return an object or a function
Number Data Type:
Number types include both integers and floating point numbers. Several operations involving number types exist including but not limited to the following:
- Multiplication (*)
- Division (/)
- Addition (+)
- Subtraction (-)
Number types can also contain “special numeric values”, which pertain to the following types:
- Infinity — meaning something without bound or larger than any other number. Can be computed mathematically (such as dividing a number by zero) or can be represented with the Infinity property of the global object in JavaScript
- NaN — meaning “not a number” is a numeric data type value representing an undefined or unrepresentable value, in the context of JavaScript and most programming languages, Nan represents a computational error. Similar to Infinity, NaN can be computed with a mathematical function failure (such as attempting to get the square root of a negative number) or can be represented with the NaN property of the global object in JavaScript.
Mathematical operations are considered “safe” in JavaScript. This means that, we can do anything: divide by zero, treat non-numeric strings as numbers, etc. The script will never stop with a fatal error. At worst, we’ll get NaN as the result.
Special numeric values formally belong to the number data type, but they are not numbers in the traditional sense of this word.
String Data Type:
A String is a data type that contains a sequence of characters of varying lengths. They are used to hold words or simple phrases. Just like in most other programming languages, strings in JavaScript must be contained in a form of quotes.
As seen above, JavaScript includes support for three kinds of quotes, including the following:
- Double quotes: “Tuesday”
- Single quotes: ‘Single quoted string’
- Backticks: `This is a Back-tick String`
There are two kinds of Strings involved in JavaScript.
Simple — strings can be declared by using either double or single quotation marks.
Extended Functionality — enable the use of embed variables and expressions.
Boolean Data Type:
The boolean data type has only two values: ‘true’ or ‘false’
Comparisons can also yield boolean values:
NULL Data Type:
The null data type is used to describe a variable that contains nothing, not even 0 or an empty string.
In JavaScript, null is not a “reference to a non-existing object” or a “null pointer”, instead the value null represents the intentional absence of any object value. It is one of JavaScript's primitive values.
UNDEFINED Data Type:
The ‘undefined’ data type means that a “value is not assigned”
This is similar to the null data type above, but instead of being nothing, it is simply not yet assigned.
Technically, it is possible to assign undefined to any variable:
It is not recommend to assign the undefined value to a variable. Normally, we use null to assign an “empty” or “unknown” value to a variable, and we use undefined for checks like seeing if a variable has been assigned.
Typeof Operator:
The typeof can return one of two complex data types: object or function
Object Data Type:
Almost everything is an object in JavaScript, in fact all JavaScript values, excluding primitives, are objects. An object is a variables that can contain singular or multiple values. When these values are named they are called properties. Objects can be manipulated with methods, or actions performed on objects.
There are many other objects in JavaScript, such as Array, Function, Math, RegEx, Error, Set, etc.
Expressions:
What is an expression in JavaScript?
In the simplest terms, an expression in JavaScript is any valid unit of code that produces a value. There are two categories of expressions in JavaScript.
- The first assigns a value to a variable.
- The second type of expression, simply has a value.
An example of the first type would be a = 21. It’s an expression, because it evaluates itself to 21.
An example of the second would be 1+2. It is different from the first, because no result is assigned.
Other expression categories are:
Arithmetic Operators — evaluates a number. Can include operations such as addition (+), subtraction(-), multiplication(*), division(/), increments(++), decrements(- -), and unary negation(-)
Bitwise Logical Operators — Operands can be expressed as 0’s and 1’s. Once the bitwise operator is used, the resulting value is constructed bitwise. Can include operations such as AND, OR, XOR, NOR, etc.
String Operators— evaluates to a character string
Logical Operators — evaluates to true or false
Primary Expressions — Basic keywords and general expressions in JavaScript. characterized by the use of the this keyword
Grouping Operator — Use of left and right brackets, to determine precedence
Left-hand-side expressions — Left values are the destination of an assignment. For example, the new operator
Operator Precedence — Operator precedence is crucial to the understanding of expressions, because it determines what the expression is evaluated to.It also helps to determine the scope.
Here is the precedence of operators from highest to lowest for JavaScript:
- Call, member: () []
- Negation, increment: ! ~ -++- -
- Multiply, divide, modulus: * / %
- Addition, subtraction: + -
- Bitwise shift: << >> >>>
- Relational: < <= > >=
- Equality: == !=
- bitwise-AND: &
- bitwise-XOR: ^
- bitwise-OR: |
- logical-AND: &&
- logical-OR: ||
- Conditional: ?:
- Assignment: = += -= *= /= %= <<= >>= >>>= &= ^= |=
- Comma: ,