Professional Documents
Culture Documents
1 Ecmas 2015 F: Cript Eatures 1.1 A L T
1 Ecmas 2015 F: Cript Eatures 1.1 A L T
// Expression bodies
var odds = evens.map(v => v + 1);
var nums = evens.map((v, i) => v + i);
// Statement bodies
nums.forEach(v => {
if (v % 5 === 0)
fives.push(v);
});
// Lexical this
var bob = {
_name: "Bob",
_friends: [],
printFriends() {
this._friends.forEach(f =>
console.log(this._name + " knows " + f));
}
};
// Lexical arguments
function square() {
let example = () => {
let numbers = [];
for (let number of arguments) {
numbers.push(number * number);
}
return numbers;
};
return example();
}
console.log(square(2, 4, 7.5, 8, 11.5, 21)); // returns: [4, 16, 56.25, 64,
132.25, 441]
1.2 CLASSES
ES2015 classes are a simple sugar over the prototype-based OO pattern. Having a single convenient
declarative form makes class patterns easier to use, and encourages interoperability. Classes support
prototype-based inheritance, super calls, instance and static methods and constructors.
class SkinnedMesh extends THREE.Mesh {
constructor(geometry, materials) {
super(geometry, materials);
this.idMatrix = SkinnedMesh.defaultMatrix();
this.bones = [];
this.boneMatrices = [];
//...
}
update(camera) {
//...
super.update();
}
static defaultMatrix() {
return new THREE.Matrix4();
}
}
class SkinnedMesh extends THREE.Mesh {
constructor(geometry, materials) {
super(geometry, materials);
this.idMatrix = SkinnedMesh.defaultMatrix();
this.bones = [];
this.boneMatrices = [];
//...
}
update(camera) {
//...
super.update();
}
static defaultMatrix() {
return new THREE.Matrix4();
}
}
The __proto__ property requires native support, and was deprecated in previous ECMAScript
versions. Most engines now support the property, but some do not. Also, note that only web
browsers are required to implement it, as it's in Annex B. It is available in Node.
// Basic literal string creation
`This is a pretty little template string.`
// Multiline strings
`In ES5 this is
not legal.`
// Interpolate variable bindings
var name = "Bob", time = "today";
`Hello ${name}, how are you ${time}?`
// Unescaped template strings
String.raw`In ES5 "\n" is a line-feed.`
// Construct an HTTP request prefix is used to interpret the replacements and
construction
GET`http://foo.org/bar?a=${a}&b=${b}
Content-Type: application/json
X-Credentials: ${credentials}
{ "foo": ${foo},
"bar": ${bar}}`(myOnReadyStateChangeHandler);s
1.5 DESTRUCTURING
Destructuring allows binding using pattern matching, with support for matching arrays and objects.
Destructuring is fail-soft, similar to standard object lookup foo["bar"], producing undefined values
when not found.
// list matching
var [a, ,b] = [1,2,3];
a === 1;
b === 3;
// object matching
var { op: a, lhs: { op: b }, rhs: c }
= getASTNode()
// object matching shorthand
// binds `op`, `lhs` and `rhs` in scope
var {op, lhs, rhs} = getASTNode()
// Can be used in parameter position
function g({name: x}) {
console.log(x);
}
g({name: 5})
// Fail-soft destructuring
var [a] = [];
a === undefined;
// Fail-soft destructuring with defaults
var [a = 1] = [];
a === 1;
// Destructuring + defaults arguments
function r({x, y, w = 10, h = 10}) {
return x + y + w + h;
}
r({x:1, y:2}) === 23
function f(x, y = 12) {
// y is 12 if not passed (or passed as undefined)
return x + y;
}
f(3) == 15
function f(x, ...y) {
// y is an Array
return x * y.length;
}
f(3, "hello", true) == 6
function f(x, y, z) {
return x + y + z;
}
// Pass each elem of array as argument
f(...[1, 2, 3]) == 6
1.7 LET + CONST
Block-scoped binding constructs. let is the new var. const is single-assignment. Static restrictions
prevent use before assignment.
function f() {
{
let x;
{
// this is ok since it's a block scoped name
const x = "sneaky";
// error, was just defined with `const` above
x = "foo";
}
// this is ok since it was declared with `let`
x = "bar";
// error, already declared above in this block
let x = "inner";
}
}
let fibonacci = {
[Symbol.iterator]() {
let pre = 0, cur = 1;
return {
next() {
[pre, cur] = [cur, pre + cur];
return { done: false, value: cur }
}
}
}
}
for (var n of fibonacci) {
// truncate the sequence at 1000
if (n > 1000)
break;
console.log(n);
}
Generators
Generators simplify iterator-authoring using function* and yield. A function declared as function*
returns a Generator instance. Generators are subtypes of iterators which include additional next and
throw. These enable values to flow back into the generator, so yield is an expression form which
returns a value (or throws).
var fibonacci = {
[Symbol.iterator]: function* () {
var pre = 0, cur = 1;
for (; ;) {
var temp = pre;
pre = cur;
cur += temp;
yield cur;
}
}
}
for (var n of fibonacci) {
// truncate the sequence at 1000
if (n > 1000)
break;
console.log(n);
}
1.9 MODULES
Language-level support for modules for component definition. Codifies patterns from popular
JavaScript module loaders (AMD, CommonJS). Runtime behaviour defined by a host-defined default
loader. Implicitly async model – no code executes until requested modules are available and
processed.
// lib/math.js
export function sum(x, y) {
return x + y;
}
export var pi = 3.141593;
// app.js
import * as math from "lib/math";
console.log("2π = " + math.sum(math.pi, math.pi));
// otherApp.js
import { sum, pi } from "lib/math";
console.log("2π = " + sum(pi, pi));
Some additional features include export default and export *:
export * from "lib/math";
export var e = 2.71828182846;
export default function (x) {
return Math.exp(x);
}
// app.js
import exp, { pi, e } from "lib/mathplusplus";
console.log("e^π = " + exp(pi));
// Sets
var s = new Set();
s.add("hello").add("goodbye").add("hello");
s.size === 2;
s.has("hello") === true;
// Maps
var m = new Map();
m.set("hello", 42);
m.set(s, 34);
m.get(s) == 34;
// Weak Maps
var wm = new WeakMap();
wm.set(s, { extra: 42 });
wm.size === undefined
// Weak Sets
var ws = new WeakSet();
ws.add({ data: 42 });
// Because the added object has no other references, it will not be held in th
e set
class MyArray extends Array {
constructor(...args) { super(...args); }
}
var arr = new MyArray();
arr[1] = 12;
arr.length == 2
Number.EPSILON
Number.isInteger(Infinity) // false
Number.isNaN("NaN") // false
Math.acosh(3) // 1.762747174039086
Math.hypot(3, 4) // 5
Math.imul(Math.pow(2, 32) - 1, Math.pow(2, 32) - 2) // 2
"abcde".includes("cd") // true
"abc".repeat(3) // "abcabcabc"
Array.from(document.querySelectorAll("*")) // Returns a real Array
Array.of(1, 2, 3) // Similar to new Array(...), but without special one-arg be
havior
[0, 0, 0].fill(7, 1) // [0,7,7]
[1, 2, 3].findIndex(x => x == 2) // 1
["a", "b", "c"].entries() // iterator [0, "a"], [1,"b"], [2,"c"]
["a", "b", "c"].keys() // iterator 0, 1, 2
["a", "b", "c"].values() // iterator "a", "b", "c"
Object.assign(Point, { origin: new Point(0, 0) })
1.13 PROMISES
Promises are a library for asynchronous programming. Promises are a first class representation of a
value that may be made available in the future. Promises are used in many existing JavaScript
libraries.
function timeout(duration = 0) {
return new Promise((resolve, reject) => {
setTimeout(resolve, duration);
})
}
var p = timeout(1000).then(() => {
return timeout(2000);
}).then(() => {
throw new Error("hmm");
}).catch(err => {
return Promise.all([timeout(100), timeout(200)]);
})
2 NODE & HAPI
2.1 INTRO
1. Create a new directory myproject, and from there:
2. Run: cd myproject, this goes into the created project folder.
3. Run: npm init and follow the prompts. This will generate a package.json file for you.
4. Run: npm install @hapi/hapi, this will install the latest version of hapi as a dependency in your
package.json.
5. Run: npm instal nodemon, this package will give us auto reload server on development.
6. Create index.js in root folder
/**
* index.js
*/
const Hapi = require('@hapi/hapi');
const server = new Hapi.Server({
host: 'localhost',
port: 3101,
});
const main = async () => {
await server.start()
return server
};
main().then(server => {
console.log('Server running at:', server.info.uri)
}).catch(err => {
console.log(err)
process.exit(1)
})
Hapi has a built-in async method on the server called start(), and you’d think that’s all you need. But
soon, we’ll do a lot of things to our server before it actually starts, and it’ll be helpful to keep them
all in one place. It’s also going to handle any errors by printing them to the console and exiting out of
the node process.
After we define the function, we call it at the end of our file, which finally starts our server. Now,
let’s add some routes!
You add routes by using the server object’s route() method. The method takes either a config object
or an array of objects.
/**
* index.js
*/
// import hapi
const Hapi = require('@hapi/hapi');
// create a server with a host and port
const server = new Hapi.Server({
host: 'localhost',
port: 3101,
});
// add each route
server.route([
{
method: 'GET',
path: '/',
handler: (request, h) => {
return 'I am root route';
},
},
{
method: 'GET',
path: '/hello',
handler: (request, h) => {
return { msg: 'I am hello' };
},
}
]);
// define server start function
const main = async () => {
await server.start()
return server
};
// start server
main().then(server => {
console.log('Server running at:', server.info.uri)
}).catch(err => {
console.log(err)
process.exit(1)
})
2.2 PLUGINS
hapi has an extensive and powerful plugin system that allows you to very easily break your
application up into isolated pieces of business logic, and reusable utilities. You can either add an
existing plugin to your application, or create your own.
Plugins are very simple to write. At their core they are an object with a register property, that is a
function with the signature async function (server, options). Additionally, the plugin object has a
required name property and several optional properties including version.
Let’s separate the route into plugin and use it in the Index.js.
/**
* /src/routes/users.js
*/
'use strict';
exports.plugin = {
pkg: require('../../package.json'),
name: 'route-books',
register: async (server, options, next) => {
const basePath = '/api/v1/';
server.route([
{
method: 'GET',
path: basePath + 'users',
handler: (request, h) => {
const data = { data: 'hello from users' }
return h.response(data).code(200)
}
},
{
method: 'GET',
path: basePath + 'users/{id}',
handler: (request, h) => {
return 'Hello from user ' + request.params.id;
}
}
]);
}
};
// index.js
'use strict';
const Hapi = require('@hapi/hapi');
const server = Hapi.server({
port: 3000,
host: 'localhost'
})
server.route({
method: 'GET',
path: '/hello',
handler: (request, reply) => {
return 'I am hello';
}
});
const main = async () => {
// register the plugin
await server.register(require('./src/routes/users.js'));
await server.start()
}
main().then(() => {
console.log('Server running on %s', server.info.uri);
}).catch(err => {
console.log(err)
process.exit(1)
})
2.3 VALIDATION
In hapi, validation can be handled by Joi plugin to simplify things. To use it lets install Joi to our
project by registering it to our dependencies in package.json file:
"@hapi/joi": "15.1.1",
*note: for now, just use Joi 15.
/**
* /src/routes/users.js
*/
'use strict';
const Joi = require('@hapi/joi');
exports.plugin = {
pkg: require('../../package.json'),
name: 'route-books',
register: async (server, options, next) => {
const basePath = '/api/v1/';
server.route([
{
method: 'GET',
path: basePath + 'users',
handler: (request, h) => {
const data = { data: 'hello from users' }
return h.response(data).code(200)
}
},
{
method: 'GET',
path: basePath + 'users/{id}',
options: {
validate: {
params: {
id: Joi.number().required().min(1)
},
query: {
page: Joi.number().min(0).default(1),
}
}
},
handler: (request, h) => {
return 'Hello from user ' + request.params.id;
}
}
]);
}
};
We put options parameter in our route and validate it. Params equal request parameter, and query
is query parameter.
http://localhost:3000/api/v1/users/a
or
http://localhost:3000/api/v1/users/9?page=a
2.4.1 Schemes
A scheme is a method with the signature function (server, options). The server parameter is a
reference to the server the scheme is being added to, while the options parameter is the
configuration object provided when registering a strategy that uses this scheme.
This method must return an object with at least the key authenticate. Other optional methods that
can be used are payload and response.
You can either write your own authentication scheme, or use one of the many hapi auth plugins,
such as hapi-auth-basic or hapi-auth-cookie.
2.4.2 authenticate
The authenticate method has a signature of function (request, h), and is the only required method in
a scheme.
In this context, request is the request object created by the server. It is the same object that
becomes available in a route handler, and is documented in the API reference.
When authentication is successful, you must call and return h.authenticated({ credentials,
artifacts }). credentials property is an object representing the authenticated user (or the credentials
the user attempted to authenticate with). Additionally, you may also have an artifacts key, which can
contain any authentication related data that is not part of the user's credentials.
The credentials and artifacts properties can be accessed later (in a route handler, for example) as
part of the request.auth object.
If authentication is unsuccessful, you can either throw an error or call and return
h.unauthenticated(error, [data]) where error is an authentication error and data is an optional object
containing credentials and artifacts. There's no difference between calling return
h.unauthenticated(error) or throwing an error if no data object is provided. The specifics of the error
passed will affect the behavior. More information can be found in the API documentation for
server.auth.scheme(name, scheme). It is recommended to use boom for errors.
2.4.3 payload
The payload method has the signature function (request, h).
Again, the standard hapi response toolkit is available here. To signal a failure throw an error, again
it's recommended to use boom for errors.
Once any decoration is complete, you must return h.continue, and the response will be sent.
If an error occurs, you should instead throw an error where the error is recommended to be a boom.
2.4.5 cookie
@hapi/cookie is a plugin that will store a cookie in the users browser once they are authenticated.
This has the option of keeping the user logged in, even after they leave the site. Here is an example
of setting up @hapi/cookie. In this example, the home route, "/", is restricted and can only be
accessed once a user has authenticated themselves: (don’t forget to install bcrypt and
@hapi/cookie)
'use strict';
const Bcrypt = require('bcrypt');
const Hapi = require('@hapi/hapi');
const users = [
{
username: 'john',
password: '$2a$10$iqJSHD.BGr0E2IxQwYgJmeP3NvhPrXAeLSaGCj6IR/XU5QtjVu5T
m', // 'secret'
name: 'John Doe',
id: '2133d32a'
}
];
const start = async () => {
const server = Hapi.server({ port: 4000 });
await server.register(require('@hapi/cookie'));
server.auth.strategy('session', 'cookie', {
cookie: {
name: 'sid-example',
password: '!wsYhFA*C2U6nz=Bu^%A@^F#SF3&kSR6',
isSecure: false
},
redirectTo: '/login',
validateFunc: async (request, session) => {
const account = await users.find(
(user) => (user.id === session.id)
);
if (!account) {
return { valid: false };
}
return { valid: true, credentials: account };
}
});
server.auth.default('session');
server.route([
{
method: 'GET',
path: '/',
handler: function (request, h) {
return 'Welcome to the restricted home page!';
}
},
{
method: 'GET',
path: '/login',
handler: function (request, h) {
return ` <html>
<head>
<title>Login page</title>
</head>
<body>
<h3>Please Log In</h3>
<form method="post" action="/login">
Username: <input type="text" name="userna
me"><br>
Password: <input type="password" name="pa
ssword"><br/>
<input type="submit" value="Login"></form>
</body>
</html>`;
},
options: {
auth: false
}
},
{
method: 'POST',
path: '/login',
handler: async (request, h) => {
const { username, password } = request.payload;
const account = users.find(
(user) => user.username === username
);
if (!account || !(await Bcrypt.compare(password, accoun
t.password))) {
return h.view('/login');
}
request.cookieAuth.set({ id: account.id });
return h.redirect('/');
},
options: {
auth: {
mode: 'try'
}
}
}
]);
await server.start();
console.log('server running at: ' + server.info.uri);
};
start();
First, you need to do is register the @hapi/cookie plugin with server.register. Once the plugin is
registered, you configure your strategy by calling server.auth.strategy. server.auth.strategy takes
three parameters: name of the strategy, what scheme you are using, and an options object. For your
strategy, you name it session. For the scheme, you will be using the cookie scheme. If you were using
@hapi/basic, this parameter would be basic. The last parameter is an options object. This is how you
can customize your auth strategy to fit your needs.
The first property you configure is the cookie object. In your strategy, you will configure three
properties of the cookie object. First, you set the name of the cookie, in this case sid-example. Next,
you set the password that will be used to encrypt the cookie. This should be at least 32 characters
long. Last, you set isSecure to false. This is ok for development while working over HTTP. In
production, this should be switched back to true, which is the default setting.
The next property is redirectTo. This will tell the server where to redirect to if an unauthenticated
user tries to access a resource that requires authentication.
The last property is the validateFunc function. The validateFunc validates that a current cookie is still
valid. For example, if a user authenticates themselves successfully, receives a cookie, and then
leaves the site. Once they return, the validateFunc will check if their current cookie is still valid.
You setup the default strategy by calling server.auth.default('session'). This will set the default auth
strategy for all routes.
Once your strategy is set up, you need to set up route that will validate the provided username and
password. In this case, your POST route to '/login' will do just that. First, it will pull the username and
password from request.payload, which the user provided in the form from the '/login' 'GET' route.
Next, you find the user from the database by searching for their username:
);
If the user doesn't not exist, or if the provided password is wrong, you redirect the user back to the
login page. You use Bcrypt to compare the user provided password with the hashed password from
the database.
Lastly, if the user does exist, and the passwords match, the user is then redirected to the homepage.
npm i inert --save
2. After that we require inert in the server.js by require command like below:
const inert = require('inert');
3. After that we need to update the bootUpServer function with below line of code of snippet
await server.register(inert);
4. In the last step, we create a route for rendering the HTML file on browser. For creating HTML
file, we need to create a public folder in the root location of the project and creating HTML
inside the public folder.
server.route({
method: 'GET',
path: '/staticpage',
handler: (req, h) => {
return h.file('./public/static.html')
}
})