Showing posts with label JavaScipt. Show all posts
Showing posts with label JavaScipt. Show all posts

Monday, March 27, 2017

Underscore.js


Underscore.js


As JavaScript is slowly moving out of the browser, several tools have emerged that significantly improve JavaScript's robustness.
One such tool is called Underscore.js and that's what we're going to take a look at today. Let's get started!
Meet Underscore.js
So what exactly does Underscore do?
Underscore is a utility-belt library for JavaScript that provides a lot of the functional programming support that you would expect in Prototype.js (or Ruby), but without extending any of the built-in JavaScript objects.
One of the nicer things about working in Python or Ruby are the fancy constructs like map that make life a lot easier. The current version of JavaScript, sadly, is fairly barebones when it comes to low level utilities.
As you read above, Underscore.js is a nifty little JavaScript library that brings in a ridiculous amount of functionality at a mere 4kb.
Underscore in Action
"Enough yapping about the library", I can hear you say. Right you are! Let's take a look at Underscore in action first before I resume my yapping.
Let's assume you have a random array of test scores and you need a list of those with 90+ score. You'd usually write up something like so:
01
02
03
04
05
06
07
08
09
10
11
12
var scores = [84, 99, 91, 65, 87, 55, 72, 68, 95, 42],
topScorers = [], scoreLimit = 90;

for (i=0; i<=scores.length; i++)
{
    if (scores[i]>scoreLimit)
    {
        topScorers.push(scores[i]);
    }
}

console.log(topScorers);
It's pretty simple and even with optimization, it's fairly verbose for what we're trying to do.
Let's look at what we can achieve with Underscore next.
1
2
3
4
5
6
var scores = [84, 99, 91, 65, 87, 55, 72, 68, 95, 42],
topScorers = [], scoreLimit = 90;

topScorers = _.select(scores, function(score){ return score > scoreLimit;});

console.log(topScorers);
I don't know about you but I just had a nerdgasm. That's some incredibly concise and readable code right there.
Sweet but Do I Really Need This?
Well, it all depends on what you're trying to do. If your use of JavaScript is limited to merely playing around with the DOM, then the answer is mostly no since jQuery does most of what you'd want to do.
Yes.
On the other hand, if you're dealing with non-DOM code or even complex, think MVC, front end code, Underscore is an absolute boon.
While some of the functionality exposed by the library is slowly making its way into the ECMA specifications, it's not available in all browsers and making your code work cross browser is another nightmare on its own. Underscore provides you with a nice set of abstractions that work everywhere.
And if you're a performance oriented person, as you should be, Underscore falls back to native implementations, if available, to make sure performance is as optimal as possible.
Getting Started
Just grab the source here, include it in your page and you're good to go.
If you were expecting a big set up process, you're going to be sorely disappointed. Just grab the sourcehere, include it in your page and you're good to go.
Underscore creates and exposes all its functionality via a single object, in global scope. This object is the titular underscore character, _.
If you're wondering, yes, this is quite similar to how jQuery works with the dollar [$] symbol. And just like jQuery, you can remap this character in case you run into conflicts. Or if you're like me and have an irrational love for the tilde.
Functional or Object Oriented?
While the official marketing blurb for the library states that it adds functional programming support, there is actually another way of doing things.
Let's take our earlier code as an example:
1
2
3
4
5
var scores = [84, 99, 91, 65, 87, 55, 72, 68, 95, 42], topScorers = [], scoreLimit = 90;

topScorers = _.select(scores, function(score){ return score > scoreLimit;});

console.log(topScorers);
This method above is the functional, or procedural, approach. You can also use a more straightforward, probably more apparent, object oriented approach.
1
2
3
4
5
var scores = [84, 99, 91, 65, 87, 55, 72, 68, 95, 42], topScorers = [], scoreLimit = 90;

topScorers = _(scores).select(function(score){ return score > scoreLimit;});

console.log(topScorers);
There is no real 'right' way to do things but keep in mind that you can jQuery-esque method chaining with the latter method.
Checking Out the Functionality
Underscore provides a little more than 60 functions that span a number of functionalities. At their core, they can be classified into groups of functions that operate on:
  • Collections
  • Arrays
  • Objects
  • Functions
  • Utilities
Let's take a look at what each does and if applicable,one or two of my favorites from each section.
Collections
A collection can either be an array or an object, an associate array in JavaScript if I'm to be semantically correct.
Underscore provides a lot of methods that operate on collections. We saw the select method earlier. Here are a few more incredibly useful ones.
Pluck
Let's say you have a nice little array containing key value pairs and you'd like to extract just a specific property from each. With Underscore, it's a cinch.
1
2
3
4
5
6
var Tuts = [{name : 'NetTuts', niche : 'Web Development'}, {name : 'WPTuts', niche : 'WordPress'}, {name : 'PSDTuts', niche : 'PhotoShop'}, {name : 'AeTuts', niche : 'After Effects'}];
var niches = _.pluck(Tuts, 'niche');

console.log(niches);

// ["Web Development", "WordPress", "PhotoShop", "After Effects"]
Using pluck is as simply as passing in the target object or array as well as which property to pick out. Here, I'm merely extracting the niche for each site.
Map
Map creates an array from a collection where each element can be mutated or otherwise changed through a function.
Let's take the earlier example and extend it a bit.
1
2
3
4
5
6
7
var Tuts = [{name : 'NetTuts', niche : 'Web Development'}, {name : 'WPTuts', niche : 'WordPress'}, {name : 'PSDTuts', niche : 'PhotoShop'}, {name : 'AeTuts', niche : 'After Effects'}];

var names = _(Tuts).pluck('name').map(function (value){return value + '+'});

console.log(names);

// ["NetTuts+", "WPTuts+", "PSDTuts+", "AeTuts+"]
Since I noticed the names missing the plus sign at the end, I'm adding them in the extracted array.
You're not limited to simple concatenation here. You're free to modify the passed value to your heart's desires.
All
all is useful if you need to check every value in a collection passes a certain criteria. To check whether a student has passed in every subject, for example.
1
2
3
4
5
6
var Scores = [95, 82, 98, 78, 65];
var hasPassed = _(Scores).all(function (value){return value>50; });

console.log(hasPassed);

// true
Arrays
Underscore has a bunch of functions that work exclusively on arrays which is highly welcome since, compared to other languages, JavaScript provides awfully few methods dealing with arrays.
Uniq
This method basically parses an array and removes all duplicate elements providing you with only unique elements.
1
2
3
4
5
var uniqTest = _.uniq([1,5,4,4,5,2,1,1,3,2,2,3,4,1]);

console.log(uniqTest);

// [1, 5, 4, 2, 3]
This comes in extremely handy when you're parsing huge datasets and need to weed out the duplicates. Keep in mind that only the first instance of an element is counted so the original order is kept.
Range
An extremely handy method that lets you create a 'range' or list of numbers. Let's look at a super quick example.
1
2
3
4
5
var tens = _.range(0, 100, 10);

console.log(tens);

// [0, 10, 20, 30, 40, 50, 60, 70, 80, 90]
The method's parameters are, in order, starting value, ending value and step value. If you're wondering, using a negative step value leads to a decrementing range.
Intersection
This method compares two arrays to each others and returns the list of elements that are found in all of the passed arrays i.e. an intersection in set theory.
Let's extend the earlier example to see how this works.
1
2
3
4
5
6
7
var tens = _.range(0, 100, 10), eights = _.range(0, 100, 8), fives = _.range(0, 100, 5);

var common = _.intersection(tens, eights, fives );

console.log(common);

// [0, 40, 80]
Easy, right? You just pass in the list of arrays to compare and Underscore does the rest.
Objects
In addition to the fairly expected is checks, Underscore provides various methods to clone, extend and other manipulate objects.
Here are a few of my favorites.
Keys and Values
Have a massive object where you need only the keys or only the values? It's so darn easy with Underscore.
1
2
3
4
5
6
var Tuts = { NetTuts : 'Web Development',  WPTuts : 'WordPress',  PSDTuts : 'PhotoShop', AeTuts : 'After Effects'};
var keys = _.keys(Tuts), values = _.values(Tuts);

console.log(keys + values);

// NetTuts,WPTuts,PSDTuts,AeTutsWeb Development,WordPress,PhotoShop,After Effects
Defaults
This method is quite useful when you need to create objects with sensible defaults when one might not be used when creating it.
1
2
3
4
5
6
7
8
var tuts = { NetTuts : 'Web Development'};
var defaults = { NetTuts : 'Web Development', niche: 'Education'};

_.defaults(tuts, defaults);

console.log(tuts);

// Object { NetTuts="Web Development", niche="Education"}
Functions
As wonky as it sounds, Underscore has functions that work on functions. Most of the functions tend to be fairly complicated to explain here, so we'll take a look at the simplest.
Bind
this is an elusive part of JavaScript and tends to leave a lot of developers really confused. This method seeks to make it a bit easier to tackle.
1
2
3
4
5
6
var o = { greeting: "Howdy" },
    f = function(name) { return this.greeting +" "+ name; };

  var greet = _.bind(f, o);

  greet("Jess")
It's a bit confusing so stay with me here. The bind functions basically lets you keep the value of thiswhenever and wherever the function is called.
This is specially useful when you're working with event handlers where this is hijacked.
Utilities
And to further sweeten the deal, Underscore provides a ton of utility functions. Since we're fairly out of time, let's just look at the biggie.
Advertisement
Templating
There are already a ton of templating solutions out there but Underscore makes its solution worth a look by being fairly tiny in its implementation whilst being fairly powerful.
Let's take a look at a quickie example.
1
2
3
4
5
6
7
var data =   {site: 'NetTuts'}, template =   'Welcome! You are at <%= site %>';

var parsedTemplate = _.template(template,  data );

console.log(parsedTemplate);

// Welcome! You are at NetTuts
First up, we create the data to populate the template followed by the template itself. By default, Underscore uses ERB style delimiters though this is entirely customizable.
With those in place, we can simply call the template passing in our template and the data. We store the result in a separate string to be used later to update content, as needed.
Keep in mind that this is an extremely simple demonstration of Underscore's templating. You can find use any JavaScript code inside the template using the delimiters. When you need to iterate over complex objects, say JSON sources, you can pair up with Underscore's excellent collection functions to create templates rapidly.

JavaScript Tricks

Hier einige JavaScript Tricks. Vielleicht kennt ihr schon den ein oder anderen.

Trick 1: Slice call

Sie können eine Array-Objektmethode mit den Slice- und Aufrufmethoden (Array.prototype.slice.call() oder [].slice.call()) kombinieren, um ein Array aus einer Array-ähnlichen Liste zurückzugeben. Eine Array-ähnliche Liste ist eine Liste mit der length Eigenschaft, sowie weiteren numerisch indizierbaren Eigenschaften.
slice.call
//using Array.prototype.slice.call
var myobject = {
  length: 4,
  '0': 'zero',
  '1': 'one',
  '2': 'two',
  '3': 'three'
}
 
console.log( Array.prototype.slice.call(myobject) );
// ["zero", "one", "two", "three"]
 
//using [].slice.call
var myobject = {
  length: 4,
  '0': 1,
  '1': 2,
  '2': 3,
  '3': 4
}
console.log( [].slice.call(myobject) );
// [1, 2, 3, 4]
oder alternativ könnte man Elemente greifen durch querySelectorAll zurückgegeben:
querySelectorAll
var nodesArray = [].slice.call(document.querySelectorAll("div"));
//nodesArray enthält nun ein array aller divs im DOM

Trick 2: Strings können in einem Array wie Mode verwendet werden

console.log( “Nick”[0] === “N” ); // true
console.log( "Nick"[1] === "i" ); // true

Trick 3: Wie man ein Array kopiert

Array kopieren
//ES5 und darunter
var newArr = Arr.slice(0);
//ES6
var newArr = [...oldArr];

Trick 4: Erstellen einer einfachen Query-Funktion (Singular)

Query-Funktion
var $ele = document.querySelector.bind(document);
 
// Sie können nun $ele(selector) verwenden, um die DOM zu suchen
var myEle = $ele("#id");

Trick 5: Erstellen einer einfacheren Query-Funktion (multiple)

Hinweis: Dies ist schneller als das Dokument. querySelectorAll und Return ein Live-nodeList.
Query-Funktion
var $all = function(selector) {
    switch (selector[0]) {
      case '#':
        return [document.getElementById(selector.slice(1, selector.length))];
      case '.':
        var classes = selector.replace(/\./g, ' ');
        return [].slice.call(document.getElementsByClassName(classes));
      default:
        return [].slice.call(document.getElementsByTagName(selector));
  }
}
console.log($all(".myClass")); 
// returns array von Elementen zurück, die dem Selector entsprechen.

Trick 6: Verkürzung console.log()


var cl = console.log.bind(document);
cl('hi'); // hi

Trick 7: Schalten Nummer Binary

var i = 4;
console.log( i.toString(2) ); // 100

Trick 8: Teilen Zahl in der Hälfte, abrunden

console.log( 26 >> 1 ); // 13
console.log( 27 >> 1 ); // 13
console.log( 4 >> 1); // 2
console.log( 5 >> 1); // 2

Trick 9: Objekt klonen

// Starck klon ein objekt
var newObj = JSON.parse(JSON.stringify(obj));
// shallow clone an object
var newObj = Object.assign({}, obj);

Trick 10: Ist Zahl (n) Teilbar durch x

var n = 56;
console.log(n % 5 == 0); // false
  
n = 5;
console.log(n % 5 == 0); // true

Trick 11: Ist Nummer (n) gerade oder ungerade?

//with modulo operator
var n = 10;
console.log(n % 2 === 0); // true
//Mit bittweisem OR
var n = 6;
console.log(n & 1 === 0); // true

Trick 12: Voreinstellung wenn Wert nicht vorhanden ist (Kurzschluss)

var a;
a = a || 10;
console.log(a); // 10
var a = 3;
a = a || 10;
console.log(a); // 3

Trick 13: Verwenden Sie den Ternäre-Operator, wenn Leistung aus (Wahr oder Falsch)

//syntax
//(evaluation) ? ifTrue : ifFalse;
var a = 2;
(a === 2) ? console.log(a): alert(a);
// wird auf das console log gedruckt.
var a = 3;
(a === 2) ? console.log(a): alert(a);
// wird eine Warnung auf dem Bildschirm angezeigt.

Trick 14: Verwenden Sie "e"   für übermäßige Nullen.

console.log( 1e4 ); // 10000
console.log( 2e1); // 20

Trick 15: JavaScript konvertiert Hexadezimal zu einer Zahl automagisch.

console.log(0x1) // 1
console.log(0xa) // 10
console.log(0xff) // 255

Trick 16: void (0) kann anstelle von undefiniert verwendet werden

console.log( void(0) === undefined ); // true

Trick 17: Konvertiere einen Wert in Boolean mit !

console.log(!!""); // false
console.log(!!" "); // true
console.log(!!0); // false
console.log(!!1); // true

Trick 18: Funktionsdeklarationen können mit Hilfe von! Zeichen, den void-Bezeichner oder durch Umhüllen der Funktion in Klammern.

 Sobald ein Ausdruck aktiviert ist, kann eine Funktion aufgerufen werden, indem sie Klammern an ihrem Ende platziert.
// ! character
!function(x) { console.log(x); }(1); // 1
// void identifier
void function(x){console.log(x); }(3); // 3
// wrapped in parentheses
(function(x) { console.log(x); } )(5); // 5

Trick 19: Destructuring

Sie können Informationen aus einem Objekt herausziehen, indem Sie Ihre Parameter auf die gleichen Werte wie die Objektschlüsselnamen einstellen (dies wird als zerstörung „destructuring“ bezeichnet)
function howOld({name, date}, age) { 
  console.log(name + " is " + age);
  console.log("on " + date); 
};
console.log( howOld({name:'tim', date: '11/10/16'}, 13) );
// tim is 13
// on 11/10/16
// Das funktioniert auch von einem externen Objekt!
var externalObj = {
  name: 'john',
  date: '06/06/06'
}
console.log( howOld(externalObj, 66) );
// John is 66
// on 06/06/06

Trick 20: Deklarieren von Standard-Funktionsparameter

function whoAmI(name = 'Nick', job ='web-Entwickler') { 
  console.log(name + ' der ' + job);
}
whoAmI(); // Nick der web-Entwickler'
whoAmI('nick', 'entwickler'); // nick der entwickler

Trick 21: Sie können die  .call()-Methode einer Funktion verwenden, um einen Aufruf dieser Funktion mit einem angegebenen Kontext und Argumenten auszuführen.

function.call(this context, arguments);
Wir können dies verwenden, um einen Workaround zu erstellen und permanente Werte einer Funktion zu übergeben, die wir nicht neu schreiben wollen.
Als Beispiel:
function add(x, y) {
  return x + y;
}
function add3(y) {
  return add.call(this, 3, y);
}
console.log(add(2, 5)); // 7
console.log(add3(6)); // 9

Trick 22: Einzel-parameter-Fat-Arrow-Deklarationen:

Wenn Sie eine Fettpfeil-Funktion mit einem einzelnen Parameter übergeben, müssen Sie keine Klammern um ihn herum schreiben.
var newFunction = text => console.log(text);
// vs
var newFunction = (name, text) => console.log(name + ': ' + text);
Dies funktioniert auch, wenn Sie eine Fat-Arrow-Funktion als Parameter übergeben.
arr.map(item => console.log(item));
// vs 
arr.map( (item, index) => console.log(item + ' index ' + index) );
Denken Sie daran, dass eine Einschränkung der Fat Arrow Funktion ist, dass sie nicht binden einen neuen "this" Kontext. Sie behalten den " this " Kontext, von dem sie aufgerufen werden.

Trick 23: Bestimmen Sie eine True- oder False-Anweisung aus einer String-Methode indexOf.

var myString = "aloha peeps";
console.log( !!~(myString.indexOf('aloha')) ); // true
console.log( !!~(myString.indexOf('hello')) ); // false
Trick 24: Ruft ein Array aller Links im Dokument ab.
var allLinks = document.links;
Trick 25: Styling aus dem Browser
var ele = document.getElementById("eleID");
var fontSize = getComputedStyle(ele).getPropertyValue("font-size");
console.log( fontSize ); // 16px
Trick 26: (Diese ist eigentlich eine Regel als alles andere)
Verwenden document.write aus irgendeinem Grund nicht.
// einen text-node hinzufügen:
var otherText = document.createTextNode('whodunnit');
document.body.appendChild(otherText);
 
//Ein element hinzufügen:
var myEle = document.createElement("H1");
var myText = document.createTextNode('blah blah');
myEle.appendChild(myText);
document.body.appendChild(myEle);
Trick 27: Kommunikation mit einem IFrame
Wenn Sie Informationen zwischen einem IFrame hin und her senden möchten.
 Sie können tatsächlich so lange Ihr Hauptfenster eine Nachricht aus dem IFrame senden, wie Sie nicht die Regeln der Cross-Site-Scripting brechen.
//Dieser Code würde in der IFrame JavaScript-Datei platziert werden:
parent.postMessage('this is a message', '*')
Um vom Hauptfenster aus dem Containerfenster zu hören, fügen Sie einfach einen Event-Listener zum Fenster hinzu.
window.addEventListener("message", function(e){ 
 console.log(e.data); 
});
Der obige Code druckt den Inhalt der Nachricht an Ihre Konsole.
Durch das Analysieren des Inhalts der Nachricht können Sie eine Funktionalität erstellen, die sowohl mit dem Hauptfenster als auch mit dem IFrame arbeitet.
Methoden zum Navigieren des DOM-Baums:
ele.parentNode  // finden die parent von ele
ele.firstChild  // finden den ersten child-node von ele
ele.nextSibling // finden die nächste Geschwister(sibling)von ele
ele.previousSibling //finden die vorherigen sibling von ele
Methoden zum Entfernen aus dem DOM-Baum:
ele.replaceChild(x, y) //Ersetzen Sie child x von ele mit node y
ele.removeChild(x) // Entfernen den child x von ele
Methoden zum Kopieren von node:
ele.cloneNode(false); // klone nur der node (falche kopie)
ele.cloneNode(true); // klone den node und seine kinder(tiefe kopie)
ele.importNode(node); // Importieren einen node in ele
Methoden für allgemeine Informationen:
ele.nodeName; // Gibt das Namensattribut oder den DOM-Namen zurück
ele.nodeType; // Gibt eine Zahl a.e zurück. 3 ist Text, 8 ist Kommentar
ele.hasChildNodes(); // Gibt Boolean zurück,wenn es knder hat
ele.childNodes; // Gibt die node-Liste der kinder zurück
ele.textContent; // gets text von ele, wenn vorhaden
ele.nodeValue;  // Neigt dazu, das gleiche wie textContent zu arbeiten
ele.contains(x); // Gibt true oder false zurück, wenn ele x enthält


Ressourcen:  
https://developer.mozilla.org/en-US/docs/Web/JavaScript
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators
https://developer.mozilla.org/en-US/docs/Web/API/Node
http://www.w3schools.com/js/js_function_invocation.asp