Avraam Mavridis
AvraamMavridis
avraamakis
avraam.mavridis
Web Developer
Javascript: The unknown parts
(part 1)
Why I am doing this talk?
1. This is my first technical talk
2. I wanted to share weird and interesting things that I have seen
Javascript is the only language that people feel that they don't have to understand before they start using it
-Doug Crockford
3. People criticise JS without really understand it
Functions
Functions have length
length is a property of a function object, and indicates how many arguments the function expects
var Espresso = function( _coffee ) {
return {
coffee: _coffee
}
}
var Cappuccino = function( _coffee, _milk ){
return {
coffee: _coffee,
milk: _milk
}
}
console.log( Espresso.length ); // 1
console.log( Cappuccino.length ); // 2
Why is useful?
The Decorator pattern
A design pattern that allows behavior to be added to an individual object, either statically or dynamically.
Decorate functions based on the arguments they accept
var ExtraSugarDecoratorGenerator = function( Coffee ){
if( Coffee.length === 1 ){
return function( _coffee ){
var espresso = new Coffee( _coffee ) ;
var EspressoWithExtraSugar = Object.assign( espresso );
EspressoWithExtraSugar.sugar = 100;
return EspressoWithExtraSugar;
}
}
else if( Coffee.length === 2 ){
return function( _coffee, _milk ){
var cappuccino = new Coffee( _coffee, _milk ) ;
var CappuccinoWithExtraSugar = Object.assign( cappuccino );
CappuccinoWithExtraSugar.sugar = 80;
return CappuccinoWithExtraSugar;
}
}
}
*Don't confuse the Decorator Pattern with the ES7 decorators
import React from 'react';
import CSSModules from 'react-css-modules';
import styles from './table.css';
@CSSModules(styles)
export default class extends React.Component {
render () {
return <div styleName='table'>
<div styleName='row'>
<div styleName='cell'>A0</div>
<div styleName='cell'>B0</div>
</div>
</div>;
}
}
https://github.com/jayphelps/core-decorators.js
Length on a function constructor
function Banner( a, b ) {
this.heights = [];
}
Object.defineProperty( Banner.prototype, 'length',
{ get: function() {
return this.heights.length;
}});
var x = new Banner();
x.heights.push( 120 );
x.heights.push( 240);
x.heights.push( undefined );
x.heights.push( "undefined" );
x.heights.push( Infinity );
console.log( x.length ) // 5
length does not include the ...Rest paremeter
function foo( a, b, ...rest ) {
}
console.log( foo.length ); // 2
function foo( a, b, ...rest ) {
if( rest.length > 5 ){
console.log( 'Why so many values?' );
}
else{
console.log( 'Hello World' );
}
}
foo(1,2,3); // Hello World
foo(1,2,3,4); // Hello World
foo(1,2,3,4,5,6,7,8,9,10,11); // Why so many values?
The rest parameter syntax allows us to represent an indefinite number of arguments as an array.
Create an empty object
var o = {};
var ö = Object.create({});
var õ = new Object();
Hm, not so empty...
console.log(o.constructor); //function Object() { [native code] }
console.log(ö.constructor); //function Object() { [native code] }
console.log(õ.constructor); //function Object() { [native code] }
What if I want a "real" empty object?
var ø = Object.create(null);
console.log(ø.constructor); // undefined
Mathematical operations over objects
The C++ way
class Cents
{
private:
int m_nCents;
public:
Cents(int nCents) { m_nCents = nCents; }
friend Cents operator+(const Cents &c1, const Cents &c2);
int GetCents() { return m_nCents; }
};
// note: this function is not a member function!
Cents operator+(const Cents &c1, const Cents &c2)
{
return Cents(c1.m_nCents + c2.m_nCents);
}
int main()
{
Cents cCents1(6);
Cents cCents2(8);
Cents cCentsSum = cCents1 + cCents2;
std::cout << "I have " << cCentsSum.GetCents() << " cents." << std::endl;
return 0;
}
The Groovy way
@ToString
class Pizza {
String name
List<Topping> toppings
double price
}
@ToString
class Topping {
String name
double price
}
//Method with name "plus" will override the "+" operator.
Pizza plus(Topping topping) {
new Pizza(name: name, toppings: toppings + [topping], price: price + topping.price)
}
Pizza pizza = new Pizza(name: "Veg Extravaganza Pizza", price: 323.32, toppings: [])
Topping cheeseBurst = new Topping(name: "cheese Burst", price: 80)
Topping extraCheese = new Topping(name: "Extra Cheese", price: 40.43)
println (specialPizza)
/*
Pizza(Veg Extravaganza Pizza, [Topping(cheese Burst, 80.0), Topping(Extra Cheese, 40.43)], 514.4)
*/
The Python way
class Book:
title = ''
pages = 0
def __init__(self, title='', pages=0):
self.title = title
self.pages = pages
def __add__(self, other):
return self.pages + other.pages
book1 = Book('Harry Potter', 700)
book2 = Book('Animal Farm', 250)
print (book1 + book2) # 950
The Javascript way
function Book(title, pages){
this.pages = pages;
this.title = title;
}
Book.prototype.valueOf = function(){
return this.pages;
}
var a = new Book('Harry Potter', 700);
var b = new Book('Animal Farm', 250);
console.log( a + b ); // 950
Mandatory arguments in function calls
var err = function( variable ){
throw new Error( `${variable} is mandatory` );
}
var foo = function( a = err('a'), b = err('b') ){
return a + b;
}
foo( undefined , 2 ) // Error, a is mandatory
foo( 10 ) // Error, b is mandatory
ES6
Explain why you "return" using the void operator
var find = function( arr, el ){
var i = arr.length;
while( i ){
if( arr[ i - 1] === el ){
return el;
}
i--;
}
return void('The element is not found');
}
var find = function( arr, el ){
var i = arr.length;
while( i ){
if( arr[ i - 1] === el ){
return el;
}
i--;
}
return;
}
===
Oh, it floats...
// This throws an error
42.toFixed( 3 ); // SyntaxError
// But all these are valid js syntax ¯\(⊙︿⊙)/¯
(42).toFixed( 3 ); // "42.000"
42.0.toFixed( 3 ); // "42.000"
42..toFixed( 3 ); // "42.000"
42 .toFixed(3); // "42.000"
To NaN or not to NaN
isNaN should check if
something === NaN
but...
isNaN( 5 ); // false
isNaN( NaN ) ; // true
isNaN('I am a string'); // true ???
Fortunately we have Number.isNaN
Number.isNaN( 5 ); // false
Number.isNaN( NaN ); // true
Number.isNaN('I am a string'); // false
...or maybe not
Oh, IE... So how can we test if something is NaN?
NaN is the only value in JS that is not equal to itself. So, lets write a safe isNaN function.
function isNaNSafe( a ){
return a !== a;
}
var b = NaN;
isNaNSafe(b); // true
isNaNSafe('something'); //false
isNaNSafe(5); //false
But how can we check that two values are equal if both are NaN ?
We can use Object.is
var a = NaN;
var b = NaN;
Object.is( a, b ) ; // true
...or maybe not
OK, lets write a polyfil
if (!Object.is) {
Object.is = function(x, y) {
if (x === y) {
// +0 != -0
return x !== 0 || 1 / x === 1 / y;
} else {
// NaN == NaN
return x !== x && y !== y;
}
};
}
*Note: JS has -0 and +0
-0 === +0 //true
Create new Array();
var a = new Array( 3 );
// older Firefox versions [,,,] or [,,]
// newer Chrome versions, Firefox [ undefined x 3 ]
var b = [ undefined, undefined, undefined ];
"0" in a; // false
"0" in b; // true
Lets operate on a new Array
var a = new Array( 3 );
a.map(function(){ return 1; });
// We would expect [1,1,1] but... we get [ undefined x 3 ]
Hm...
map, filter, foreach
do NOT operate on an array with empty slots
map calls a provided callback function once for each element in an array, in order, and constructs a new array from the results. callback is invoked only for indexes of the array which have assigned values
Maybe we can declare a new Array specifying directly the length
var t = [];
t.length = 2;
t.map(function(){ return 1; }); // [undefined × 2]
// Hm, we cant...
So, how can we create a new Array of 100 elements without having to write 100 times undefined and in which map,filter,foreach will work?
var a = Array.apply(this, new Array(100));
/*[undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined]*/
a = a.map(function(){ return 1; });
/*[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]*/
// My fav
Array.apply( null, { length: 100 } );
Array.from(new Array(100));
Or if we dont care for IE/Opera users:
Array.of(...(new Array(5)));
Or if we also dont care for Safari
Array.apply(this, new Array(10)).map(function(){return 0; });
// [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
And what if we want to fill the Array with some values?
Array.apply( this, new Uint16Array(10) );
// [0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
It should be a simpler way, what about:
(new Array(100)).fill(0);
Or...
Or...
[...Array(100)].map(() => 0);
Or...
Array.from(Array(100), () => 0)
Object.keys( Array.apply(null, Array(100))).map(() => 0);
Or...
Or...
Object.keys(new Int8Array(100)).map(()=> 0);
A little bit about Native Constructors
String()
Number()
Boolean()
Array()
Object()
Function()
RegExp()
Date()
Error()
Symbol()
Array and Error Natives as Consructors don't require the new operator
var a = new Array(10);
var b = new Error('message');
var a = Array(10);
var b = Error('message');
===
Regular expressions defined in the literal form are strongly preferred, not just for ease of syntax but for performance reasons. The JS engine precompiles and caches them before code execution.
var regx1 = /^a*b+/gi;
var regx2 = new RegExp('^a*b','gi');
If you call Date() without new, you'll get back a string representation of the date/time at that moment. The exact form of this representation is NOT specified in the language spec
Date() //"Mon Nov 23 2015 10:18:11 GMT+0100 (CET)"
Object() constructor does NOT always create objects
var s = new Object('Hello');
var k = ' world'
console.log( s + k); // "something good"
var n = new Object(5);
var c = 10;
console.log( n + c ); // 15
Apart from the Error(..) constructor we also have:
EvalError(..),
RangeError(..),
ReferenceError(..),
SyntaxError(..),
TypeError(..),
URIError(..)
try {
foo.bar();
} catch (e) {
if (e instanceof EvalError) {
alert(e.name + ': ' + e.message);
} else if (e instanceof RangeError) {
alert(e.name + ': ' + e.message);
}
// ... etc
}
Be careful with bind, apply, call
function foo(a){
function boo(c){
this.$ = c;
}
boo.call(null, a );
}
foo(10);
console.log(window.$); // 10 ooooops!
Passing null or undefined to bind,apply,call is the same as passing window
function foo(a){
function boo(c){
this.$ = c;
}
// It is safer to pass an empty object
boo.call({}, a );
}
foo(10);
console.log(window.$); // jQuery, Alles gut
Promiseland
How to know if something is a Promise?
Given that Promises are constructed by the new Promise(..) syntax, we can check for p instanceof Promise
No, its not enough.
1. Mainly, you can receive a Promise value from another browser window (iframe, etc.), which would have its own Promise different from the one in the current window/frame, and that check would fail to identify the Promise instance.
2. Moreover, a library or framework may choose to vend its own Promises and not use the native Promise implementation to do so.
If we assume that any Promise follows the specs it should have a then
function isPromise(p){
return p !== null &&
( typeof p === "object" || typeof p === "function" ) &&
typeof p.then === "function";
}
...still, not enough
Object.prototype.then = function(){};
Array.prototype.then = function(){};
var v1 = { hello: "world" };
var v2 = [ "Hello", "World" ];
Both v1 and v2 will be assumed to be thenables. You can't control or predict if any other code accidentally or maliciously adds then(..) to any of the native prototypes.
There is not a safe way to identify that something is a Promise
Error Handling in the streets of Promiseland
Error Handling
try {
foo();
}
catch (err) {
// handle error
}
The try..catch here works only from the perspective that the foo() call will either succeed or fail immediately, synchronously. If foo() was itself its own async completing function, any async errors inside it would not be catchable.
Error Handling
If you use the Promise API in an invalid way and an error occurs that prevents proper Promise construction, the result will be an immediately thrown exception, not a rejected Promise. Some examples of incorrect usage that fail Promise construction:
new Promise(null);
Promise.all();
Promise.race(42);
Error Handling
var p = Promise.resolve( 42 );
p.then(
function fulfilled(msg){
foo(msg); // throw error, foo undefined
}
)
.catch( handleErrors );
What happens if handleErrors(..) itself also has an error in it? Who catches that?
You can't just stick another catch(..)
More promises
Promise.race()
Promise.all()
Promise.none() //not part of the standard
Promise.some() //not part of the standard
Promise.any() //not part of the standard
Notes:
A "race" requires at least one "runner," so if you pass an empty array, Promise will never resolve.
My problem with the JS standard
You are requesting data from various APIs. If one fails you want to be able to present data to the users from the other APIs. How can you do that in an effective way without using nested promises if the only thing that you have is Promise.all?
Promise.settle
Promise.settle( arrayOfPromises ).then(function(results){
for(var i = 0; i < results.length; i++){
if(results[i].isFulfilled()){
// results[i].value to get the value
}
}
});
Promise.settle polyfill
function settle(promises) {
return Promise.resolve()
.then(function () {
return promises;
})
.then(function (promises) {
if (!Array.isArray(promises)) throw new TypeError('Expected an array of Promises');
var promiseResults = promises.map(function (promise) {
return Promise.resolve()
.then(function () {
return promise;
})
.then(function (result) {
return promiseResult(result);
})
.catch(function (err) {
return promiseResult(null, err);
});
});
return Promise.all(promiseResults);
});
}
Promise.settle polyfill
function promiseResult(result, err) {
var isFulfilled = (typeof err === 'undefined');
var value = isFulfilled
? returns.bind(result)
: throws.bind(new Error('Promise is rejected'));
var isRejected = !isFulfilled;
var reason = isRejected
? returns.bind(err)
: throws.bind(new Error('Promise is fulfilled'));
return {
isFulfilled: returns.bind(isFulfilled),
isRejected: returns.bind(isRejected),
value: value,
reason: reason
};
}
function returns() {
return this;
}
function throws() {
throw this;
}
IIFE
(function(){
console.log('>>>');
})();
(function(){
console.log('>>>');
}());
(function(){
console.log('>>>');
}).call(null);
!function(){console.log('>>>')}();
+function(){console.log('>>>')}();
-function(){console.log('>>>')}();
~function(){console.log('>>>')}();
true&function(){console.log('>>>')}();
true,function(){console.log('>>>')}();
true||function(){console.log('>>>')}();
true^function(){console.log('>>>')}();
new function(){console.log('>>>')}();
//My fav
void function(){console.log('>>>')}();
Falsy Objects
var a = new Boolean( false );
var b = new Number( 0 );
var c = new String( "" );
Someone could think that we are referring to objects wrapped around obviously falsy values
But obviously...
Boolean(a); // true
!!b // true
a & b & c // true
So what is "falsy objects"
There are certain cases where browsers have created their own sort of exotic values behavior, namely this idea of "falsy objects," on top of regular JS semantics.
document.all // HTMLAllCollection[582]
Boolean(document.all) // false
The () set on a constructor call (a function called with new) is optional if there are no arguments to pass
var foo = function(){
return {};
}
var c = new foo;
var d = foo();
var e = new foo();
console.log(c); //{}
console.log(d); //{}
console.log(e); //{}
Randomness
Randomness
var cryptoStor = new Uint16Array(8);
window.crypto.getRandomValues(cryptoStor);
// [22237, 22562, 59970, 4209, 47246, 63294, 21811, 6254]
To guarantee enough performance, implementations are not using a truly random number generator, but they are using a pseudo-random number generator seeded with a value with enough entropy.
Why not Math.random()
More: https://medium.com/@betable/tifu-by-using-math-random-f1c308c4fd9d#.yoelzmgpo
Hausaufgaben
1. How can we make this happen?
new Number(2) == 3; // true
2. Write a function that will output "Hello World":
-Without assigning any variable directly equal to a string,number,null, undefined,boolean or any operation between them
-Without using Regex, typeof, instanceof
-Without declaring any function with the name "hello world" or similar
-Without declaring any array or any object
(Contact me for the solutions)
Thank you
AvraamMavridis
avraamakis
avraam.mavridis
Javascript: The unknown parts
By Avraam Mavridis
Javascript: The unknown parts
- 2,219