parent
82cb2ebae9
commit
a3b2404a32
@ -0,0 +1,261 @@
|
||||
## Scripting Languages
|
||||
|
||||
Javascript is a scripting language. That means that it was intended to be used by both professional and amateur developers to automate various activities. In the case of Javascript, it was to script activities that occurred in a web page. The official name for Javascript is ECMA-262. ECMA is a European standards committee that is responsible for managing the definition of the language.
|
||||
|
||||
A characteristic of scripting languages is that they tend to be not only weakly typed, but are very tolerant of programming errors. Javascript will attempt to coerce the type of a value instead of raising a type error. This can lead to confusing bugs when strings mysteriously become numbers, or vice versa. Javascript will also try to fix programs that don't compile by putting semicolons at the ends of lines until it does compile.
|
||||
|
||||
## Javascript and the Web
|
||||
|
||||
Javascript is very heavily used in web development. It originated as a scripting language that could be included in a web page. To do this, a number of event based programming practices were put in place. Because Javascript virtual machines were very efficient, a version that runs as an application was created known as node or nodejs. The event and function closure coding patterns used heavily in web pages also turned out to be well suited to implementing web servers. Many modern web sites will use the Apache or NginX web servers as a front end and redirect traffic to node based servers that provide the actual web pages. Because Javascript does not have to fork processes or spawn threads with each new network connection, it scales better to large numbers of network connections.
|
||||
|
||||
## Javascript the Language
|
||||
|
||||
Named values may be declared as constants or variables. The keyword **const** followed by a name and an initializer declares a constant. They keywords **var** or **let** followed by a name and optional initializer will declare a variable. The difference between var and let is that vars may be hoisted. This means that even if I declare a var in the body of a nested loop, the language is allowed to act as if I declared the variable at the beginning of the immediately enclosing function. A variable declared with let may not be hoisted. A let variable will always have the local scope visible in the code.
|
||||
|
||||
Best practice: Use **let** to declare variables.
|
||||
|
||||
Javascript is an object based language. It supports objects as a fundamental data type. Objects in Javascript do not need to have classes. Version 6 of Javascript adds syntax for declaring classes that simplifies using standard object design patterns.
|
||||
|
||||
An object in Javascript is a collection of named properties.
|
||||
* A property may have one or more attributes, such as Writeable.
|
||||
* A property may hold a primitive value or function closure.
|
||||
* The primitive values are: undefined, null, true, false, and kinds of Number, String, Object.
|
||||
* A property with a value that is a function closure is called a method.
|
||||
|
||||
Javascript supports the same arithmetic, relational, and logical operators as Java.
|
||||
|
||||
Any place a boolean value is expected, the Javascript language will attempt to coerce values that are not boolean to an equivalent boolean value. The key thing to know is the values that are considered equivalent to false. **Everything not equivalent to false, is considered to be equivalent to true**.
|
||||
|
||||
* false is equivalent to false (obviously)
|
||||
* 0 is equivalent to false
|
||||
* "" the empty string is equivalent to false
|
||||
* null is equivalent to false
|
||||
* undefined is equivalent to false
|
||||
* NaN not a number is considered equivalent to false
|
||||
|
||||
Javascript functions may be called with any number of arguments.
|
||||
* The first n argument values get associated with the first n parameter names.
|
||||
* If there are an insufficient number of arguments, the last parameters will have a value of undefined.
|
||||
* If there are more arguments than parameters, then a pseudo-variable named arguments may be used to access them.
|
||||
* The arguments variable is an array like list. You may use array indexing to access its values. It is not an Array, however, so it does not understand messages like forEach, map, filter, and reduce.
|
||||
|
||||
In a function defined to expect a varying number of arguments, it is common to see a declaration at the beginning of the method to turn this list into a full Array instance.
|
||||
|
||||
let args = Array.from(arguments);
|
||||
|
||||
There is a simplified form of function known as an arrow function.
|
||||
|
||||
* Arrow functions don't have their own bindings to this, arguments or super, and should not be used as methods.
|
||||
* Arrow functions don't have access to the new.target keyword.
|
||||
* Arrow functions aren't suitable for call, apply and bind methods, which generally rely on establishing a scope.
|
||||
* Arrow functions cannot be used as constructors.
|
||||
* Arrow functions cannot use yield, within its body.
|
||||
|
||||
Consider the following function:
|
||||
```
|
||||
function (n){
|
||||
return n + 1;
|
||||
}
|
||||
```
|
||||
The arrow function version of this function would be:
|
||||
```
|
||||
n => n + 1
|
||||
```
|
||||
OR
|
||||
```
|
||||
(n) => {return n + 1}
|
||||
```
|
||||
|
||||
* If an arrow function has more than one parameter, you must use parentheses.
|
||||
* If the arrow function consists of more than a single expression, you must use curly braces.
|
||||
|
||||
Both Javascript functions and arrow functions are first class objects. Many of the most important features of the language depend upon this fact.
|
||||
|
||||
## Javascript Objects
|
||||
|
||||
There are many kinds of objects in Javascript. All of them take up different amounts of memory and have different performance characteristics. What those differences are depends upon the implementation. Sigh.
|
||||
|
||||
The easiest way to create a simple object is with the literal object constructor {}. **A simple object may be declared by enclosing a comma separated list of key:value pairs in curly braces**.
|
||||
|
||||
As of Javascript 6 there is a class keyword that may be used to create classes. This is the preferred way of declaring classes in Javascript today. The following is an example of declaring the class Rectangle.
|
||||
```
|
||||
class Rectangle {
|
||||
constructor( width, height){
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
area(){
|
||||
return this.width * this.height;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Notice that there are no keywords introducing the method definition area. Note also that the keyword static may be used to introduce class methods, and you may declare your superclass using the keyword extends just like in Java.
|
||||
|
||||
### Creating object constructors in Javascript 5 and earlier.
|
||||
Older versions of Javascript do not have support for classes built into the language. In base Javascript, a constructor is just a function. Functions in Javascript have both a **prototype** and a **this** field in its activation. A constructor function is just a function that declares instance variables by assigning values to this.varName. The following example shows the declaration of the constructor for an Employee and a subclass Drone.
|
||||
```
|
||||
// Constructor for Employee
|
||||
function Employee(name, dept){
|
||||
this.name = name || "<no name>";
|
||||
this.dept = dept || "<no dept>";
|
||||
}
|
||||
|
||||
// Constructor for Drone that inherits from Employee
|
||||
function Drone(projects){
|
||||
this.projects = projects || [];
|
||||
}
|
||||
Drone.prototype = new Employee;
|
||||
```
|
||||
Note that the or operator, ||, will evaluate its left hand side value, if that value is like true, it will return that value. If the left hand value is like false, it will return the value of its right hand side.
|
||||
|
||||
This particular usage of the || operator is deprecated. In it place, you should use the *nullish coalescing operator*, ??. To set a var with a default value use the following method.
|
||||
```
|
||||
this.name = name ?? "<no name>";
|
||||
```
|
||||
|
||||
## Filter, Map, and Reduce
|
||||
|
||||
Arrays in Javascript understand three very important methods found in most functional programming languages: filter, map, and reduce.
|
||||
|
||||
The filter function takes a one argument function closure that returns a boolean value. When the method filter is sent to an array, a new array is create whose value those for which the function closure returned true.
|
||||
```
|
||||
[1,2,3].filter( (n) => n%2 == 0 ) // returns [2]
|
||||
```
|
||||
The map function takes a one argument function closure that returns a value. When the method map is sent to an array, a new array is created with the values that were returned when the function closure was invoked with each of the arguments.
|
||||
```
|
||||
[1,2,3].map( (n)=>n*n ) // returns [1, 4, 9]
|
||||
```
|
||||
The reduce function takes two arguments. The first argument is a two argument function closure, and the second is the initial value to feed into the reduce. The function closure's first argument represents the accumulating value, and the second is the current item.
|
||||
```
|
||||
[3, 2, 1].reduce( (product, each)=>product * each, 1) //returns 6
|
||||
```
|
||||
In this case,
|
||||
* the first time the function closure gets called it has arguments 1, 3.
|
||||
* the second time it is called with the arguments 3, 2.
|
||||
* the third time it is called with the arguments 6, 1.
|
||||
|
||||
## Javascript Misuse
|
||||
Objects in Javascript behave like a dictionary or map. Sometimes UNIX utilities will call these data structures "associative arrays" because they hold a collection of objects accessed by name instead of index.
|
||||
The Javascript literal object, {}, looks just like a Python dictionary. Even worse, it behaves just like a dictionary or map.
|
||||
```
|
||||
let myDict = {};
|
||||
myDict["name"] = "Raoul";
|
||||
// myDict["name"] === myDict.name
|
||||
```
|
||||
The map or dictionary data structure is useful for a variety of reasons. This abuse of the literal object should be avoided. The object will behave just like a dictionary, but it has a prototype slot because it is an object. There are some predefined keys which will cause unpredictable behavior if overwritten. (keys are unique)
|
||||
|
||||
Instead, use the builtin types Set or Map. Both of these are hashed collection types. If you need a collection of unique elements, use a Set. If you need a collection of values keyed by name, use a Map.
|
||||
|
||||
## Modules
|
||||
|
||||
### Modules on the server: Node and CommonJS
|
||||
|
||||
Javascript has a module system to make it easy to load functionality. Actually, Javascript has a number of module systems that have been developed over time. Historically, the most common is the CommonJS module system, recognized by the use of the function require. The function require takes a pathname of a module as an argument, and returns an object whose properties are the exported features of the module.
|
||||
|
||||
The rules of where require finds the files can be a little complex, but a simple rule of thumb is that if the file doesn't start with "./" or "/", then it is either considered a core module (and the local Node.js path is checked), or a dependency in the local node_modules folder. If the file starts with "./" it is considered a relative file to the file that called require. If the file starts with "/", it is considered an absolute path. NOTE: you can omit ".js" and require will automatically append it if needed.
|
||||
|
||||
When the require function is called, a module directory is examined to see if this module has already been loaded. If so, then the exports object is returned to the caller. If not, the contents of the module file are retrieved, and a preamble and epilog are added to the contents to make it into a function.
|
||||
```
|
||||
function (exports, ... ){ // prolog
|
||||
// contents of module go here
|
||||
} //epilog
|
||||
```
|
||||
This function is then called with an empty object as the value of the argument for the exports parameter. The code in the module is responsible for adding properties to the exports object. A entry is added to the module directory for this module name and the exported objects are cached for future reference. In other words, if the same module is required multiple times in one file, it will only be loaded and evaluated the first time.
|
||||
|
||||
A module with a private function and a public function would look like the following:
|
||||
```
|
||||
function prvateFunction( ){
|
||||
// do something very private
|
||||
}
|
||||
|
||||
exports.publicFunction = function (){
|
||||
// function that will be a property of the exports object.
|
||||
}
|
||||
```
|
||||
Note that any top level value const, var, let, function, or class may be exported. Internally nested definitions may not be exported. Also note that the exports object is the first parameter to the function prolog added to the module prior to its evaluation by require.
|
||||
### Modules in the Browser: Modern Usage
|
||||
|
||||
Modern Javascript has an updated version of module support for code that lives in a web browser on the client. You may use the javascript file extension, '.js', but many suggest using the extension, '.mjs', to indicate that the file holds a javascript module.
|
||||
|
||||
To export a definition, you may either put the keyword 'export' in front of the definition, or you may add the keyword export followed by a curly-brace enclosed list of comma separated names at the end of the module.
|
||||
```
|
||||
// export individual definitions
|
||||
export const pi = 3.14159;
|
||||
export function area(radius) {return pi * radius * radius;}
|
||||
|
||||
// export at end of module
|
||||
const pi = 3.14159
|
||||
function area(radius){return pi * radius * radius;}
|
||||
export {pi, area}
|
||||
```
|
||||
The simplest way to import definition from a module is using import/from.
|
||||
```
|
||||
import {pi, area} from './myModule.js'
|
||||
```
|
||||
The imported names are added to the scope of the file that does the import. The new module system also supports a module with a single defined deault export, as well as the ability to rename a definition upon import.
|
||||
|
||||
## Regular Expressions and Javascript
|
||||
Regular expressions as formally described in the previous section are defined mathematically. That is to say, the definition is minimal and expressed in a way that facilitates proving theorems with respect to regular expressions.
|
||||
|
||||
In practice, there is a different syntax used for regular expressions. This syntax does not add to the power of regular expressions, it just makes their patterns more compact and easier to write.
|
||||
|
||||
The web site https://regexr.com is great for testing regular expressions. It lets you write a regular expression and see the matches highlighted. It also has a section that explains what each part of your pattern is doing.
|
||||
|
||||
## Special Characters used in Javascript Regular Expressions
|
||||
|
||||
the alphabet is assumed to be the character set used by the operating system
|
||||
there are special characters... some of the most important are
|
||||
* '|' a union, an occurrence of the preceding pattern, or the following pattern
|
||||
* '.' match any character
|
||||
* '\s' match whitespace, ie space, tab, cr, lf
|
||||
* '\d' match a digit between 0 and 9
|
||||
* '^' match the beginning of input (except in a character union)
|
||||
* '$' match the end of input
|
||||
* '\'. escape character: treat the next character as a letter, not a special symbol
|
||||
* '*' match zero or more occurrences of the preceding pattern
|
||||
* '+' match one or more occurrences of the preceding pattern
|
||||
* '?' match zero or one occurrence of the preceding pattern
|
||||
* '^' match the beginning of input
|
||||
* '$' match the end of input
|
||||
* {n} match n occurrences of the preceding pattern
|
||||
* {n,} match n or more occurrences of the preceding pattern
|
||||
* {n,m} match n to m occurrences of the preceding pattern
|
||||
* a union of characters may be included in square brackets
|
||||
* [cho] will match either the letter 'c', the letter 'h', or the letter 'o'
|
||||
* [0-9] will match the digits 0 through 9
|
||||
* [^a] will match any letter except the letter 'a'
|
||||
|
||||
A full cheatsheet of special characters in Javascript regular expressions may be found here, https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions/Cheatsheet
|
||||
|
||||
### Declaring a Javascript Regular Expression
|
||||
|
||||
A literal regular expression delimited by '/'. A 'g' following a pattern means to allow the pattern to be reused globally.
|
||||
```
|
||||
let myPattern = /(\d{1,2})\s([A-Za-z]{3})/g
|
||||
```
|
||||
### Construction of a regular expression object.
|
||||
let myPattern = new RegExp("(\d{1,2})\s([A-Za-z]{3})")
|
||||
In general, prefer the literal regular expression to the object constructor. Only use the object constructor version if your code is programmatically creating search patterns.
|
||||
|
||||
### Using parentheses to define subpatterns
|
||||
Putting a regular expression in parentheses not only makes a group of a regular expression, but also identifies it as a sub pattern. Each sub pattern has an index. The index of a sub pattern is found by counting '(' characters until you get to the opening parenthesis of the sub pattern.
|
||||
|
||||
### Using a Javascript Regular Expression
|
||||
|
||||
There are a number of different ways to invoke a regular expression match, but the most common is exec().
|
||||
```
|
||||
let searchResult = myPattern.exec('The dates are 13 Feb.")
|
||||
```
|
||||
The searchResult returned will be an array if a match was found, or null if no match.
|
||||
|
||||
In this case the array is ['13 FEB', '13', 'FEB', index: 12, input: 'The date is 13 FEB.', groups: undefined ]
|
||||
|
||||
searchResult[0] is the entire matching string
|
||||
searchResult[1] is the first substring match
|
||||
|
||||
searchResult[2] is the second substring match
|
||||
|
||||
index is where the next search would begin
|
||||
|
||||
input is the entire input string
|
Loading…
Reference in new issue