Javascript Notes
Just notes from various books/websites on JavaScript. Have tried to reference as thoroughly as possible.
Page Contents
References
Useful Resources
- https://jsconf.com/
- https://javascriptweekly.com/
- https://frontendfoc.us/
- http://jstherightway.org/
Testing
To test with IE, Microsoft offers virtual machines with different versions of IE installed. The downloads seem to be about 1GB in size so be patient!
Firefox can have multiple versions on the same machine.
Loops
for ... of
Ref: Loops and Iteration.
Iterate over iterable objects:
my_array = ["This", "is", "a", "test"];
for (let [idx, array_entry] of my_array.entries()) { // entries() returns an Array Iterator
console.log(`${idx} -> ${array_entry}`);
}
// Outputs:
// 0 -> This
// 1 -> is
// 2 -> a
// 3 -> test
Scope In JavaScript
Scope is the context in which code is executed, and there were three types, now 4, in JavaScript.
- global scope,
- local scope (aka "function scope"),
- eval scope,
- block scope (>=ES6) - use
letorconst.
If you come from a C-ish background you may notice a pretty big ommission in the above list... there is no block scope!
JavaScript does not have block scope (pre ECMAScript 2015). Any variables declared in a block, i.e, { ... var xxx; ...} are local to the function in which they are declared or, worse, the global scope.
Note, however, that recently (at the time of writing) the ECMAScript 2015 has added something called the let statement to declare block scope local variables. May not be well supported for some time.
One thing that really got me was the use of var, or more precisely, what happens when you do not use var to declare local variables. If you do not declare a variable using var in a function it actually gets created in the global scope! One to watch out for.
Variables declared in a function that do NOT use var in the declaration are created in the global scope and not the function/local scope as you might expect.
To resolve a symbol, JavaScript goes down the scope chain: first look up symbol in the function/local scope, then in the parent scope, the grandparent scope, and so on, until the global scope is reached. The first symbol found is used. This is why closures work...
Every scope has a this object associated with it. In the global scope, this will most likely be the web browser window. In the function scope it will reference the object it is attached to (unless specifically modified - see later). The value of this is determined entirely at run time which is very different from languages like C++.
Every scope has a this reference associated with it. It is determined at run time.
For, example, if you define a function in your web browser's console so that it prints the contents of the this reference, you will see it pointing to the global scope object, which for the browser is the Window object:
function jehtech() { console.log(this); }
jehtech()
// Outputs:
// > Window {top: Window, location: Location, document: document, window: Window, ...}
If you do the same for a vanilla object you get the following:
jt = { objMethod : jehtech }
jt.objMethod()
// Outputs:
// > Object {}
// >> objMethod: function jehtech()
// >> __proto__: Object
So... you can see that when the same function is called in different contexts, it's this reference "points" to different objects: The value of this is based on the context in which the function is called and is determined at run time.
Closures In JavaScript
Closures work due to the scope chain that was described above. From parent function return a reference to the child function contained within it. When this child function (nested function) is invoked, it still has access to the parent function's scope because of the scope chain. See Richard Cornford's article [3] for further info.
Arrays
See array API on MDN.
- new_array = array1.concat(array2, ..., arrayN)
new_array is the concatenation of array1 with all the other arrays in the concat() argument list. - array.indexOf(x), array.lastIndexOf(x)
Returns index of first/last instance of x in array or -1 if x is not in the array. - string = array.join()
Concatenates all elements of array into a string - new_length = array.push(x1, x2, ..., xN)
Pushes elements, in order, onto end of array. - item = array.pop()
Removes element off end of array. - array.reverse()
Reverses array elements in place. - sub_array = array.slice(start, end)
Takes slice [start, end). Note that range does not include end index. Returns slice as new array, original not effected. - array.splice(start, deleteCount [,i1, i2... iN])
Removes items from array and potentially also adds new. - item = array.shift()
Like pop() but removes from start of array. - array.unshift(x)
Like push() but puts at start of array.
Objects In JavaScript
I wanted to play with the HTML5 canvas to produce the little resitor calculator on the electronics page. To do this I decided to learn a little bit about JavaScript classes...
These notes are basically just notes made on the books referenced above and applied to creating some objects to draw boxes etc on a canvas.
How Objects Are Stored (vs. Primatives)
Primatives (things like ints, floats etc) are stored directly in
the variable.
Everything else is stored as a reference type which is just a
pointer to a memory location.
This means that primatives are deep copies. Modifying a copy of a primative will not affect the original primative:
var origVar = 999;
var copyVar = origVar;
copyVar = 123;
console.log(copyVar); // prints 123
console.log(origVar); // prints 999 - the original
// has NOT been modified!
Objects are not primatives. They are stored as references. This means that when a variable that points to an object is copied, all that is really copied is the reference to that object. Thus if the copied variable is modified, the original is modified because both variables refer to the same object:
var o1 = new Object(); var o2 = o1 o1.newAttr = "J" //< Note: can add property at any time console.log(o1.newAttr); // o1.newAttr == "J" console.log(o2.newAttr): // o2.newAttr == o1.newAttr == "J"
One thing to note in the above is the automatic way to add a property to an object. By assigning to a property that doesn't exist, the property is created for the object instance (note: not class, only this specific object instance).
The following are the main built-in objects (i.e., objects that are automatically available to you).
- Array
- Data
- Object
- Error
- Function
- RegExp
Declaring Objects
Javascript objects are just dictionaries that map keys to values. A property is the key name that maps to its value.
New Operator
Use the new operator:
var o1 = new Object(); o1.prop1 = "something"; o1.prop2 = 101;
Object Literals
Using object literals we can create the exact equivalent of the above:
var o1 = { prop1: "something", prop2 : 101 }
Note how we don't need quotes around the object properties.
This looks a little bit like a dictionary in some scripting languages like python, and in fact we can use an object in that manner most of the time.
Object.create()
Allows the prototype object for the object being create to be specified:var LiteralClassDef = {
myFunc: function() { console.log(this); }
}
var blah = Object.create(LiteralClassDef);
blah.myFunc();
Declaring Arrays
Can declare using the new operator (var a1 = Array(1,2)) or using array literals (var a1 = [1, 2] - identical to the previous instantiation using new).
Declaring Functions And Function Expressions
Declarations
A function declaration is a lot like a function declaration in any language and will look familiar:
my_new_func(123);
function my_new_func(param)
{
// Do something
}
You might notice in the above example that the function is actually called before it is declared. This would be unexpected if you were thinking in C terms, where a function must be declared before it can be used. So what's happening? The reason the above works is that declared functions are hoisted to the top of the enclosing scope. This is why, in the example the function could be called before it was declared.
Function declarations are hoisted to the top of the enclosing scope.
Expressions
In JavaScript functions are first class objects and can be created as such. The most common syntax for this is:
my_new_func(123); //< This is an error!
var my_new_func = function(param) {
// Do something
};
my_new_func(123); // This is OK.
Note the trailing semi-colon after the function definition. It's important not to miss this. The function object is created from the function literal and a reference to it stored in the variable my_new_func.
Note, however, that using the function before it is defined using an expression will result in an error. Why is this? It is because function expressions are not hoisted to the top of the current scope!
Function expressions are NOT hoisted to the top of the enclosing scope. They can only be used after they are defined.
Parameters
Functions can have any number of parameters and can be called with fewer or more parameters than which they are defined! When called with fewer the latter parameters are automatically set to "undefined". For example:
function dummy(a,b,c) {
console.log(a);
console.log(b);
console.log(c);
}
dummy(123, "jeh-tech");
//Outputs:
// 123
// jeh-tech
// undefined
Using this fact, default values for parameters can be created. For example, let's say we want to give the parameter c in the above example a value of "tech-jeh". We can re-write the function as follows:
function dummy(a,b,c) {
c = typeof options === 'undefined' ? "tech-jeh" : c;
console.log(a);
console.log(b);
console.log(c);
}
dummy(123, "jeh-tech");
//Outputs:
// 123
// jeh-tech
// tech-jeh
However, this can be written much more neatly as follows:
function dummy(a,b,c) {
c = c || "tech-jeh"; // Looks better!
... <snip> ...
}
Because functions are first class objects they have properties, which you can query from inside your function. One property bound to a function when it is called is the arguments object.
function dummy(a,b,c) {
console.log(dummy.length); // Expected #arguments
console.log(dummy.arguments.length); // Actual #arguments
console.log(dummy.arguments.callee); // Object reference this this function
for(var idx=0; idx < dummy.arguments.length; ++idx) {
console.log(dummy.arguments[idx]); // The nth function argument
}
}
dummy("param1", "param2", "param3", "param4")
//Ouputs:
// 3
// 4
// function dummy(a, b, c)
// param1
// param2
// param3
// param4
One use for arguments.callee I've seen is in the use of timer callback functions...
setTimeout( function() {
...
if (condition)
setTimeout(arguments.callee, timeInMilliseconds);
...
});
Other points to note include:
- Functions can have arbitrary number of parmeters
- function_name.length gives number of arguments function expects. I.e., number of arguments explicity listed in signature. Function can have more or less.
- You can access arbirary arguments using function_name.arguments[] array.
- Functions can't be overloaded as lack of a solid parameters list means lack of real signature.
Object Methods: Properties That Reference Functions & Changing "this"
Object methods are just properties that reference functions. When an object method is called it's this reference is set to "point" to the associated object, as we saw briefly in the section on scope.
It is possible to change the object to which the this reference is bound when calling a function using the function method call() (remember functions are first class objects so have methods and properties associated with them).
-
func_name.call(this_value, arg1, ..., argN)
Calls the function but binds this to this_value, overriding its default binding. -
func_name.apply(this_value, [arg1, ..., argN])
Like call() except function parameters specified in array. Useful if you want to dynamically build the argument list. -
func_name.bind(this_value, param1, ..., paramN)
Creates a new function object using func_name as the template with the function#s this value bound to this_value, overriding the default. It optionally also binds some of the parameters.
Cookies
AJAX
Server Sent Events
Websockets
REST
GraphQL
State
https://indepth.dev/posts/1488/state-machines-in-javascript-with-xstate
Rough Notes
Javscript Internals and Promises/Async Functions
REF -- https://www.udemy.com/course/the-complete-javascript-course/learn
JAVASCRIPT INTERTALS
====================
JavaScript hosted in an enironment. E.g. your browser, or nodejs.
The host, has a Javascript engine that takes the code and executes it. E.g. of engines include
Google V8, rhINO,and Spider Monkey to name just a few.
All JS code must run inside something... this is the execution context. It is a "wrapper" or
"container" of sorts, that stores variables and in which a piece of code is evaluated and runs.
See http://dmitrysoshnikov.com/ecmascript/chapter-1-execution-contexts/.
From SO - https://stackoverflow.com/a/9384894/1517244 and https://stackoverflow.com/a/7722057/1517244
Execution context is a concept in the language spec that, in layman's terms, roughly equates to
the 'environment' a function executes in; that is, variable scope, function arguments, and the
value of the this object. The context stack is a collection of execution contexts.
Execution context is different and separate from the scope chain in that it is constructed at the
time a function is invoked (whether directly – func(); – or as the result of a browser invocation,
such as a timeout expiring). The execution context is composed of the activation object (the
function's parameters and local variables), a reference to the scope chain, and the value of this.
The call stack can be thought of as an array of execution contexts. At the bottom of the stack is
the global execution context. Each time a function is called, its parameters and this value are
stored in a new 'object' on the stack.
From the Spec, with some - https://www.ecma-international.org/publications/files/ECMA-ST/ECMA-262.pdf:
An execution context is a specification device that is used to track the runtime evaluation of
code by an ECMAScript implementation.
At any point in time, there is at most one execution context per agent that is actually executing
code. This is known as the agent's running execution context. All references to the running execution
context in this specification denote the running execution context of the surrounding agent.
The execution context stack is used to track execution
contexts. The running execution context is always the top element of this stack. A new execution
context is created whenever control is transferred from the executable code associated with the
currently running execution context to executable code that is not associated with that execution
context. The newly created execution context is pushed onto the stack and becomes the running
execution context.
An execution context contains whatever implementation specific state is necessary to track the
execution progress of its associated code. Each execution context has at least these state
components:
1. Code evaluation state
All state needed to perform, suspend, and resume evaluation of the code
associated with this execution context
2. Function.
If this execution context is evaluating the code of a function object, then the
value of this component is that function object. If the context is evaluating the
code of a Script or Module, the value is null.
3. Realm
The Realm Record from which associated code accesses ECMAScript resources.
4. Script or Module
The Module Record or Script Record from which associated code originates. If there
is no originating script or module, as is the case for the original execution context
created in InitializeHostDefinedRealm, the value is null.
Execution contexts for ECMAScript code have these additional state components:
5. Lexical Environment
Identifies the Lexical Environment used to resolve identifier references made by code within
this execution context.
6. Variable Environment
Identifies the Lexical Environment whose EnvironmentRecord holds bindings created by
VariableStatements within this execution context.
The DEFAULT EXECUTION CONTEXT is the GLOBAL EXECUTION CONTEXT
- Code not inside any function
- Associated with the global object (e.g., Window if running in a browser)
Also, callbacks from from things like timeouts execute in the GLOBAL execution context.
In the browser console type...
> var b = 39393939393;
< undefined
>
> b
< 39393939393
> # Now we can see that code in the global execution context belongs to the global object, which
> # for a browser, is the window object.
> window.b
< 3939393939
Same with functions, for example...
> function JEH() { var a; }
< undefined
> JEH
< f JEH() { var a; }
> window.JEH
< f JEH() { var a; }
EXECUTION CONTEXT OBJECT: 2 phases: creation and then execution.
|
+---- Variable Object
| Code is scanned at load time for function declarations. For each function an entry into the VO is made that records
| things like the arguments passed into the function. Also done for variables, which are added as properties to the
| VO and initially set to undefined. This is refered to as HOISTING - they are available before the execution phase
| starts, which is why we can write:
| my_func();
| ...
| function my_function() { ... }
|
| Note, how this wouldn't work if we used a function variable, because although the VARIABLE my_func is hoisted,
| it is hoisted and undefined (until it is defined lol).
| my_func(); # Can't call undefined
| var my_func = function { ... };
|
|
| Another example of variable hoisting...
| function b() {
| console.log(bbb); // OK: bbb is defined in environment VO, with a value of undefined
| var bbb = 10;
| }
| b(); // Outputs "undefined"
|
| function c() {
| console.log(ccc); // ERROR: ccc is not defined (not in environment VO)
| ccc = 10;
| }
| c(); // Raises a ReferenceError exception!
|
+---- Scope Chain
| Scoping determines where, in terms location in the code, a variable is accessed. A variable scoped locally to a function
| can be accessed within that function, but not in the scope outside that function for example. However, a function A closed
| by another function B, can access variables in B's scope. Or a normal function can access variables from the global scope.
|
| JS is lexically scoped which means that scope is determined by somethings position in the code.
|
+---- "This" variable.
Set in the creation phase.
What "this" refers to depends on the context. In the global context it refers to the global object (window for browsers).
In a regular function context it also refers to the global object.
For a method call this points to the object that is calling the method.
NOTE: This is not assigned a value until a function where it is defined is actually called, Thus, "this" is NOT
lexically scoped!
E.g.:
function test1()
{
console.log(this);
}
test1(); // Outputs Test1 == [object Window]
var test2 = {
my_method: function() {
console.log("Test2 == " + this);
function test3() {
console.log("Test3 == " + this);
}
test3();
},
};
test2.my_method(); // Outputs Test2 == [object Object]
// Test3 == [object Window] << NOTE: MAY SURPRISE YOU!
EXECUTION STACK = order in which functions are called
SCOPE CHAIN = order in which functions are written in the code - i.e. their lexical relationship to one another.
Thus the execution stack does NOT define where variables can be accessed... it is the scope chain that does this! The execution context
will store the scope chain, but do not effect the scope chain.
PROMISES
========
Callback hell
-------------
function get_pr_requests(account_details) {
ask_server_for_pr_reqs(
server_address,
account_details,
(status, pr_list) => {
if (status == OK) {
pr_list.map( pr_item => {
ask_server_for_pr_details(
server_address,
account_details,
(status, pr_deets) => {
ask_server_for_files(
...,
(status, file_list) => {
file_list.map( file => {
ask_server_for_file(
...
(status, file) => {
.... and so one ... nesting deeper and deeper!
}
)
})
}
)
}
)
})
}
}
);
}
This continual nesting of functions makes it very hard to read and understand this code. This
triangular shape of continuall nested callbacks is what is refered to as callback hell. Without the
ES6 arrow function it would look even worse as the binding of the "this" keyword would also need
to be managed!
Promises to the rescue (>=ES6)
-------------------------------
Promise:
- Object that keeps track about whether a certain event has happened already.
- Determines what happens after the event has happened
- Implements the concept of a future value that we are expecting
Promise states:
PROMISE PRODUCED
|
|
v
PENDING ---event---> SETTLES/RESOLVED ---succcess---> FULFILLED
|
+------------failure----> REJECTED
JS:
const my_promise = new Promise( executor );
^^^^^^^^
This is a function that is called as soon as the promise is
created and usually executes something ASYNCHRONOUS like a Fetch.
The executor function takes two arguments:
1. A CALLBACK function for when the executor wants to inform the
promise that the event it was handling was succcessfull. I.e.,
it wants to RESOLVE the promise. It takes one argument, which
is the result that the promise should return (the future
value that was expected).
2. A CALLBACK function for when the executor wants to inform the
promise that the event it has handling failred. It wants to
REJECT the promise.
EG:
//
// CREATE a promise
//
const get_pr_requests = new Promise((resolve, reject) => {
ask_server_for_pr_reqs( //< This is the async function our executor will run
server_address,
account_details,
(status, pr_list) => { //< This is the function "ask_server_for_pr_reqs" calls back
// when it has finished doing its work.
if (status == OK) { resolve(pr_list); } //< We then call the Promise resolve() or
else { reject(status); } // reject() depending on whether
// "ask_server_for_pr_reqs" succeeded.
}
)
});
//
// Create another promise, this time as a function that returns a promise.
//
const get_pr_deets = pr_id => {
return new Promise( (resolve, reject) => {
ask_server_for_pr_details(
...,
(status, pr_deets) => {
if (status == OK) { resolve(pr_deets); }
else { reject(status); }
}
});
};
//
// CONSUME a promise by using then() and catch() methods.
// then() is the callback to execute when the promise is RESOLVED
// catch() is the callback to execute when the promise is REJECTED
//
get_pr_requests.then( pr_list => { // pr_list is what get_pr_requests passed to resolve()
// The promise was RESOLVED - it completed successfully.
return get_pr_deets(pr_list[0]); // Return a NEW PROMISE allows us to **CHAIN** promises
// (rather than using the previous pattern of continually
// nesting callbacks, which is what lead to callback hell)
}).then( (pr_deets) => {
// Handle the promise returned by the previous next() - this is CHAINING!
}).catch( error => {
// The promise was REJECTED
console.log(error);
});
ASYN / AWAIT (>= ES8)
======================
Makes it easier to _consume_ promises. The promise creation still remains the same...
async function func_name(...) { ... } // New type of JS function that is asynchonrous
// so will keep running in the background on another thread
// the result of which will be popped back into the event
// Q when its ready.
An async function keeps running the the background, and importantly only in async functions can
await's be used. Importantly, like the previous promise consumption, the call to the asyn function
does not block... instead it just chuggs away in the background.
// The function will keep executing in the background. An await will block until the promise
// is fulfilled.
async function load_pr_stuff() {
try {
const pr_reqs = await get_pr_requests; // CONSUME promise using await.
// ^^^^ // instead of .next()
// ^^^^
// pr_reqs gets the RESULT of the promise.
const pr_deets = await get_pr_deets(pr_reqs[0]); // Like the chained .next() above.
}
catch (error) {
// Any REJECT is caught as an error.
console.log(error); // Handle the error somehow
}
}
This turns a ton of callbacks or chained promisises into something like looks a lot more proceedural
and is therefore a lot easier to grok! NICE NICE NICE!
An async function returns a promise. So if, inside the async you return something of interest, you
can get at it using the .next() method:
load_pr_stuff().next(...)
Basically async functions let us go from
X.then(
a => {
return someFuncA(a); // Return new promise
}
).then (
b => {
return someFuncB(b); // Return new promise
}
)
...
.then (
z => {
return someFuncZ(z); // Return new promise
}
)
To:
async function A() {
const a = await X()
...
const b = await someFuncA(a);
...
const c = await someFuncB(b);
...
const z = await someFuncY(y);
return z;
}
Which is a little neater and allows our reasoning to be more "linear" and flow, rather than having
to think about the "house keeping" of callbacks.
AJAX WITH PROMISES AND AWAIT
============================
Can use XMLHTTPRequest interface.
There is a newer version of this called FETCH. XMLHttpRequest has better browser support because it
is older, but FETCH is more modern.
fetch(URL) // Returns a promise... yay!
If you see error containing "No-Access-Control-Allow-Origin" it is talking about a JS policy that
prevents a page making AJAX requests to a domain different to its own domain.
Cross-Origin Resource Sharing (CORS). See https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS.
"
Cross-Origin Resource Sharing (CORS) is a mechanism that uses additional HTTP headers to tell
browsers to give a web application running at one origin, access to selected resources from a
different origin. A web application executes a cross-origin HTTP request when it requests a
resource that has a different origin (domain, protocol, or port) from its own...
...For security reasons, browsers restrict cross-origin HTTP requests initiated from scripts.
For example, XMLHttpRequest and the Fetch API follow the same-origin policy. This means that a
web application using those APIs can only request resources from the same origin the
application was loaded from unless the response from other origins includes the right CORS
headers.
"
Can use a proxy to get around this - e.g.,
crossorigin.me - it is a CORS proxy that you can use for free
To use prefix the URL of the API end-point you are using with "https://crossorigin.me/".
Eg, to use the metaweather free API, which does not implement CORS use:
fetch("https://crossorigin.me/https://metaweather.com/api/location/<num>").next(...)...;
So, to continue with FETCH:
fetch(URL).next( result => {
console.log(result);
return result.json(); // Returns a promise that should resolve to the JS object created
// from the JSON string returned by the API.
}).next( jsonObj => {
// Do something with the returned data which is now represented as a JS object for us.
}).catch ( error => {
console.log(`It failed due to ${error}`);
});
Or... use await:
async function load_pr_stuff() {
const result = await fetch(URL);
const data = await result.json();
return data; // < Returns a promise!
}
load_pr_stuff.then( data => console.log("The final data is " + data) );
// ^^^
// Remember this is the way to get the final promise returned by the async function
Javscript Objects, Prototical Inheritance etc
OBJECTS:
--------
Test For Object Properties
property_name in object_name
Does not evaluate the property just says if present
Checks for bowth own and prototype properties
obj.hasOwnProperty() to check for specifically own properties
Remove property
delete obj.property_name
NOTE: This only works on own properties
Enumerate properties:
for(property in object) {...} or
var props = Object.keys(object); for(var i=0; i < props.length; ++i) { ... }
The for-in loop also enumerates prototype properties, while Object.keys() returns only own (instance) properties
Constructor:
A constructor is simply a function that is used with new to create an object.
Constructors allow you to initialize an instance of a type in a consistent
way, performing all of the property setup that is necessary before the object can be used.
Make sure to always call constructors with new; otherwise, you risk
changing the global object instead of the newly created object.
Function name with capital is convention to represent object
Eg
var cat = {
name: "kitty",
speak: function() { console.log(this.name + " says meow"); }
}
Translates into
function Cat(name) {
this.name = name;
this.speak = function() {
console.log(this.name + " says meow");
};
}
Prototype:
A recipe for a object.
The shared nature of prototypes makes them ideal for defining methods
once for all objects of a given type. It’s much more efficient to put
the methods on the prototype and then use this to access the current instance.
function Person(name) {
this.name = name;
}
Person.prototype.sayName = function() {
console.log(this.name);
};
Or on mass
Person.prototype = {
constructor: NAME, // Using the object literal notation to overwrite the prototype changed
the constructor property so that it now points to Object u instead of Person.
This happened because the constructor property exists on the prototype,
not on the object instance. When a function is created, its prototype property
is created with a constructor
property equal to the function.
sayName: function() { ... },
...
}
Checking for properties in the prototype...
function hasPrototypeProperty(object, name) {
return name in object && !object.hasOwnProperty(name);
}
Each instance has pointer back to prototype through internal property [[Prototype]]
You can read the value of the [[Prototype]] property by using the Object.getPrototypeOf()
method on an object:
var prototype = Object.getPrototypeOf(object);
You can also test to see if one object is a prototype for another by
using the isPrototypeOf()
var object = {};
console.log(Object.prototype.isPrototypeOf(object));
You can also store other types of data on the prototype, but be careful
when using reference values. Because these values are shared across
instances, you might not expect one instance to be able to change values
that another instance will access.
Inheritance - Prototype Chaining:
Prototype is also an object, it has its own prototype and inherits properties
from that. This is the prototype chain: An object inherits from its prototype,
while that prototype in turn inherits from its prototype, and so on.
Methods inherited from Object:
valueOf() - lets you do +/-/<gt; etc operations by returning value
toString() - Called if valueOf() returns reference instead of primative. Also when JS expects string.
propertyIsEnumerable()
hasOwnProperty()
ifPrototypeOf()
Object.prototype - DONT CHANGE: All objects inherit from Object.prototype by
default, so changes to Object.prototype affect all objects.
Simple Inheritance
Explicitly specify [[Prototype]] with the Object.create(obj-for-proto, [prop-descr]) method:
var book = {
title: "The Principles of Object-Oriented JavaScript"
};
// is the same as
var book = Object.create(Object.prototype, {
title: {
configurable: true,
enumerable: true,
value: "The Principles of Object-Oriented JavaScript",
writable: true
}
});
Or do MyObject.prototype = new OtherObject();
MyObject.prototype.constructor = MyObject;
Or MyObject.prototype = Object.create(OtherObject.prototype, {
constructor: {
value: MyObject;
}});
Always make sure that you overwrite the prototype before adding properties to it,
or you will lose the added methods when the overwrite happens.
Calling SuperClass Constructor:
function Rectangle(length, width) {
this.length = length;
this.width = width;
}
Rectangle.prototype.getArea = function() {
return this.length * this.width;
};
Rectangle.prototype.toString = function() {
return "[Rectangle " + this.length + "x" + this.width + "]";
};
// inherits from Rectangle
function Square(size) {
Rectangle.call(this, size, size);
// optional: add new properties or override existing ones here
}
Square.prototype = Object.create(Rectangle.prototype, {
constructor: {
configurable: true,
enumerable: true,
value: Square,
writable: true
}
});
Call supertype method:
// call the supertype method
Square.prototype.toString = function() {
var text = Rectangle.prototype.toString.call(this);
return text.replace("Rectangle", "Square");
};
Module Pattern:
The module pattern is an object-creation pattern designed to create singleton
objects with private data. The basic approach is to use an immediately
invoked function expression (IIFE) that returns an object. An IIFE is a function
expression that is defined and then called immediately to produce a
result. That function expression can contain any number of local variables
that aren’t accessible from outside that function. Because the returned
object is defined within that function, the object’s methods have access
to the data.
var yourObject = (function() {
// private data variables
return {
// public methods and properties
};
}());
Scope safe constructors:
function Person(name) {
if (this instanceof Person) {
// called with "new"
this.name = name;
} else {
// called without "new"
return new Person(name);
}
}
HTML 5 Canvas
MDN Canvas Tutorials, which are rather good!
TODO: Read the following...
http://www.html5rocks.com/en/tutorials/canvas/performance/
https://dev.opera.com/articles/html5-canvas-basics/
Realllllly cool use of Cavas: JavaScript NES Emulator and Spectrum emulator.
RECTANGLES ---------- filling, stroking and clearing fillRect(x,y,w,h) - fills rect strokeRect(x,y,w,h) - draws outline. Uses current strokeStyle, lineWidth lineJoin and miterLimit setings. clearRect(x,y,w,h) fillStyle is the colour we'll fill with strokeStyle is the outline colour Current canvas state includes - Trsnformations - Clipping regtion - Attributes - globalAlpha - globalCompositeOperation - strokeStyle - textAlign - textBaseLine - lineCap, lineJoin, lineWidthm miterLmiit - fillStype - font - shardowBlur, shadowColor, shadowOffsetX, shadowOffsetY Not part of state - the current path/bitmap being manipulated. Save and restore canvas state using context.save() and context.restore() Paths ----- Use to create arbirary shapes: a list of points and lines to be drawn between them. Only one current path per context state. Current path is not saved when context.save() is called. Current path concept to transform ONLY the current path on the canvas. ctx.beginPath(), ctx.closePath() - start and stop a path. Current transformation effects only things drawn in the current path. ctx.moveTo(x,y) - move pen without drawing ctx.lineTo(x,y) - draw line from current pen position to new position ctx.stroke() - actually fillin the lines. ctx.lineWidth, lineJoin (miter, bebel, round), lineCap (butt, round, square) ctx.arc(x, y, radius, startAngle, endAngle, anticlockwise) ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) ctx.quadraticCurveTo(cpx, cpy, x, y) Clipping -------- Combining the save() and restore() functions with the Canvas clip region limits the drawing area for a path and its subpaths transforms ----------- apply to shapes and paths drawn after the setTransform() or other transformation function call We must move the point of origin to the center of our shape to rotate it around its own center ctx.setTransform(1,0,0,1,0,0); // Identity // then set point of origin ctx.translate // then rotate ctx.rotate Gradients -------- ctx.fillStyle = "black" ctx.fillStyle = "#rrggbb" ctx.fillStyle = rgba(r,b,g,alpha) ctx.fill() g = ctx.createLinearGradient(x1, y1, x2, y2); - draws gradient along line defined by (x1,y1) to (x2,y2) Next add color stops g.addColorStop(where, 'rgb(r,g,b)'); where is a number between 0 and 1 the second parameter is evaled by the function then set fillStyle ctx.fillStyle = g This can also be applied to the strokeStyle ctx.strokeStyle = g