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

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