Pages

Affichage des articles dont le libellé est object. Afficher tous les articles
Affichage des articles dont le libellé est object. Afficher tous les articles

Object.create

Les fonctions et les objets ont un [[Prototype]]. 
  • Il assure la chaîne de délégation.
Seule la fonction possède un prototype. 
  • Il regroupe les méthodes et objets à partager, 
  • Il est pointé par la chaîne de délégation
-> Un objet n'a pas de prototype !

let animal = {
        legs: 4
    },

let dog = Object.create(animal);

dog
-> Une fonction possède un prototype !
function Animal() { }; //SuperClass
function Dog() { }; // SubClass

Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog; // mise à jour, car l'objet a été ecrasé


subclass-superclass

Dans le cas des fonctions, Il ne faudra pas confondre la propriété __proto__ ou [[Prototype]] avec la propriété prototype.

le prototype permet de partager les fonctions communes des instances d’un même objet. Et cet objet est pointé par le lien __proto__ d’autres objets.


Vous pouvez vérifier sur le code suivant les structures !


Capture.PNG

class

var _createClass = function () { 
  
  function defineProperties(target, props) { 
    for (var i = 0; i < props.length; i++) { 
      var descriptor = props[i]; 
      descriptor.enumerable = descriptor.enumerable || false; 
      descriptor.configurable = true; 
      
      if ("value" in descriptor) descriptor.writable = true; 
      
      Object.defineProperty(target, descriptor.key, descriptor); 
    } 
  } 
  
  //create class
  return function (Constructor, protoProps, staticProps) { 
    
    if (protoProps) defineProperties(Constructor.prototype, protoProps); 
    if (staticProps) defineProperties(Constructor, staticProps); 
    
    return Constructor; 
  }; 
}();

function _possibleConstructorReturn(self, call) { 
  if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } 
  
  return call && (typeof call === "object" || typeof call === "function") ? call : self; 
}

function _inherits(subClass, superClass) { 
  if (typeof superClass !== "function" && superClass !== null) { 
    throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); }
  
  subClass.prototype = Object.create(superClass && superClass.prototype, { 
    constructor: { value: subClass, enumerable: false, writable: true, configurable: true } 
  }); 
  
  if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }

function _classCallCheck(instance, Constructor) { 
  if (!(instance instanceof Constructor)) { throw new         TypeError("Cannot call a class as a function"); } }

var Animal = function () {
  function Animal(name) {
    _classCallCheck(this, Animal);

    this.name = name;
  }

  _createClass(Animal, [{
    key: 'speak',
    value: function speak() {
      console.log(this.name + ' makes a noise.');
    }
  }]);

  return Animal;
}();

var Dog = function (_Animal) {

  _inherits(Dog, _Animal);

  function Dog() {
    _classCallCheck(this, Dog);

    return _possibleConstructorReturn(this, Object.getPrototypeOf(Dog).apply(this, arguments));
  }

  _createClass(Dog, [{
    key: 'speak',
    value: function speak() {
      console.log(this.name + ' barks.');
    }
  }]);

  return Dog;
}(Animal);

http://jsbin.com/zayoro/1/

perte du this : objet

Voici un exemple montrant la perte de la référence du this dans les objets.

function P() {

  this.age = 0;

  setInterval(function () {
    this.age++; // this ref sur global et non l'objet
  }, 1000);
}
var pPerte = new P();


En voici une version corrigée ! Les fonctions fléchées capturent la valeur this de leur contexte.

function Personne() {
  this.age = 0;

  setInterval(() => this.age++, 1000);
}

var p = new Personne();

setInterval(() => console.log(p.age), 3000);



Une autre solution fût l'utilisation de bind

Ecriture de boucle : sur des objets

Object.defineProperty(Object.prototype, "hiddensweetHome",
                      {enumerable: false, value: "lune"});

Enumerable on jsbin.com

Ecriture de boucle

Il est recommandé d'écrire les boucles sous cette forme :

for (var name in map) {
  if (map.hasOwnProperty(name)) {
    // ... this is an own property
  }
}

get et set : En Action

Exemple d'utilisation : 

Object.defineProperty(this, 'val', {

    get: function() {
      return val;
    },
    set: function(value) {
      val = value;
      archive.push({ value: val });
    }
  });

JS Bin on jsbin.com

Object

Comment créer un objet en JS.

var Cours = {
  lieu : "Université d'Evry",
  année : new Date(),

  toString : function () {
     return this.lieu + '\n' + this.année.getFullYear();
  },

  extend : function ( config ) {
     var inst = Object.create( this );
     for ( var key in config ) {
         if ( config.hasOwnProperty( key ) ) {
             inst[key] = config[key];
         }
      }
      return inst;
  }

};



var HTML = Object.create ( Cours );


var CSS = Cours.extend( {prof : "dupont",
                          lieu : "IBGBI"} );


JS Bin on jsbin.com


+ d'info sur Object.create et la délégation

video : object

youtube : objet static


var youtube = {
    
    /**
     * Expects an argument that is either a youtube URL or a ID,
     * and returns back the ID.
     */
    getIdFromUrl: function(videoIdOrUrl) {
        if (videoIdOrUrl.indexOf('http') === 0) {
            return videoIdOrUrl.split('v=')[1];
        } else {
            return videoIdOrUrl;
        }
    },
    
    /**
     * Expects an argument that is either a youtube URL or a ID,
     * and returns back the URL of the thumbnail for that video.
     */
    generateThumbnailUrl: function(videoIdOrUrl) {
        return 'http://i3.ytimg.com/vi/' + youtube.getIdFromUrl(videoIdOrUrl) + '/default.jpg';
    },

    /**
     * Expects an argument that is either a youtube URL or a ID,
     * and returns back the URL for that video.
     */
    generateWatchUrl: function(videoIdOrUrl) {
        return 'https://www.youtube.com/watch?v=' + youtube.getIdFromUrl(videoIdOrUrl);
    },
    
    /**
     * Expects an argument that is either a youtube URL or a ID,
     * and returns back the embed URL for that video.
     */
    generateEmbedUrl: function(videoIdOrUrl) {
        return 'http://www.youtube.com/embed/' + youtube.getIdFromUrl(videoIdOrUrl);
    }

}

lien

Test simple : assert

Mise en place d'une fonction de test simple : assert

function assert(test, message) {  
  if (!test) {
    throw "ERROR: " + message;
  }
  return true;
}

Exemples

Objet static

JS Bin

Constructeur

JS Bin

Prototype sur les objets du langage

Idée :

Nous pouvons utiliser la propriété prototype  pour proposer de nouvelles méthodes sur les objets du langage.

Illustration : 

String.prototype.repeat = function( num ){ return new Array( num + 1 ).join( this ); }

Exemple :

"-".repeat(100);






Du CSS à l'objet en passant par le procédural.

Vous aller étudier trois façons de créer une boite d'information.


  1. Code pur HTML5
  2. Code procédurale (modification du DOM)
  3. Code Objet (mémorisation d'un état)


Voici le comportement de base.

   

Code HTML5 CSS3



Code procédural



code objet

Objet Message : in action

Pour afficher dans un jeu des messages de début ou de fin de jeu, nous pouvons définir un objet.

L'idée principale est de regrouper en une partie commune "create" l'ensemble des fonctions à répéter pour chaque message.

var Screen = {
        welcome: function() {
            // Setup base values
            this.text = 'SORT the Buddles';
            this.textSub = 'find the criteria';
            this.textColor = 'red';
            // Create screen
            this.create();
        },

        gameover: function() {
            this.text = 'We have a Winner';
            this.textSub = 'Winner !';
            this.textColor = 'red';

            this.create();
        },

        create: function() {
            // Background
            ctx.fillStyle = 'black';
            ctx.fillRect(0, 0, canvas.width, canvas.height);

            // Main text
            ctx.fillStyle = this.textColor;
            ctx.textAlign = 'center';
            ctx.font = '40px helvetica, arial';
            ctx.fillText(this.text, canvas.width / 2, canvas.height / 2);

            // Sub text
            ctx.fillStyle = '#999999';
            ctx.font = '20px helvetica, arial';
            ctx.fillText(this.textSub, canvas.width / 2, canvas.height / 2 + 30);
        }
    };

Speudoclassical subclass

voici comment créer des sous classes en JavaScript.


Nous allons tenter de comprendre ce code.

Recopie

L'idée est simple, nous voulons écrire qu'un taxi "ressemble" à une voiture.

Pourquoi ne pas écrire le code suivant.


L'idée est simple, un taxi a les mêmes propriétés qu'une voiture donc recopions simplement ces propriétés dans le constructeur.

Si l'idée semble ici séduisante, il faut penser que le nombre de propriétés à recopier ainsi peut être important et qu'un cas de modification d'une propriété, nous aurons à répéter cette modification en deux endroits. C'est bien pour cette raison que nous voulons éviter la recopie.

instancier Car

Pourquoi ne pas créer une instance de Car dans le constructeur.


Cette solution pose un certains nombre de problèmes. 
Coté mémoire, nous créons une instance de Car à chaque fois que nous appellerons la fonction Taxi. Mais, en appelant new Car() dans le constructeur de cette façon, this dans le constructeur Car sera une nouvelle instance de Car et non de Taxi. 

Rappelons nous que le langage en interne crée à chaque appel de new un objet this.


Mais, il n'y a pas de lien (mis à part le nom) entre ces deux objets this. Il y a bien deux objets distincts.

Il serait tentant d'écrire le code suivant. Mais, l’interpréteur du langage ne le permet pas et de toute façon il y aurait encore deux objets this.


Appel de la fonction

Pourquoi ne pas appeler simplement la fonction Car dans le constructeur. Encore une fois, la réponse est simple, l'objet this dans car ne représente pas une instance de Taxi mais l'objet global. Puisque la fonction est simplement invoquée dans le contexte global.


Finalement, il suffirait de passer à cette fonction Car le this instance de taxi.
La solution utilise donc l'appel à la fonction call.

Il est parfois difficile d'appréhender son utilisation mais c'est en fait assez simple. Elle a pour rôle de faire correspondre les deux this qui au départ représente deux objets différents ! 

Une bonne explication est donnée sur la vidéo 11'30





Ainsi c'est le this de Taxi auquel on ajoutera une propriété pos. Il n'y aura donc aucun objet supplémentaire crée ni de recopie de propriétés.

Superclass et Subclass

Reprenons l'exemple de la functional Class.


Un problème classique est de définir des catégories de Voiture ayant des spécificités bien particulières.

Il faut a tout pris éviter de recopier le code comme le montre la figure suivante.


Il est clair que nous devons limiter la recopie de code commun. Par exemple la fonction move est dupliquée.

Nous allons définir une fonction (la super Fonction) qui regroupe le code commun. Nous créons ensuite d'autres fonction (les sous Fonction) qui décorent la super fonction. Mais, nous pouvons utiliser le mot classe à la place de fonction. Puisque la super classe a pour rôle de générer un grand nombre d'objets.





Speudoclassical pattern

Revenons un instant sur le code de prototype class.


Nous voyons que chaque fois que nous voulons définir une fonction class, il faut créer un objet avec Object.create et le retourner.

Modifions le code précédant en introduisant this à la place de obj.


Et finalement puisque nous devons pour chaque class créer et retourner cette objet pourquoi ne pas l'intégrer directement dans le Langage et ainsi ne plus l'écrire dans le code. Pour valider ce type d'écriture (pour ne pas le confondre avec un autre type d'objet à modifier) il suffira d'appeler la fonction avec l'opérateur new.

Finalement, nous écrirons


Examinons une dernière fois ce code


En Partie 1, nous spécifions les parties spécifiques de chaque objet. Ces différences seront écrire à l'intérieur du constructeur.

En partie 2, nous aurons le code commun à partager entre tous les objets instanciés de la classe. Ces similarités seront stockées dans l'objet prototype de la classe.

Prototype Class


Reprenons l'exemple de functional-classes



La fonction Car, crée un objet copie les propriétés et retourne un objet.

Nous avons vue la différences entre la fonction extend et l'utilisation de object.create.

Nous allons utiliser cette délégation pour transformer notre code. Il suffit de remplacer avantageusement  extend par Objet.create.


Functional Classes

Nous allons voir encore une fois, l'importance des fonctions dans le langage JavaScript et comment elle peuvent créer la base des classes.

Dans l'article du pattern décoration, nous avons vu comment une fonction peut augmenter (décorer) un objet existant.

Reprenons cet exemple :

A noter l'erreur de code loc = pos car la fonction carlike.



La fonction carlike décore l'objet obj qui lui est passé en paramètre.

Nous allons renommer et modifier la fonction pour qu'elle génère les objets.


Le paramètre obj est remplacé par une déclaration de variable locale var obj = {...}.
A l'inverse de la fonction de décoration qui modifie un objet existant, la fonction Car crée ici l'objet obj et le retournera.

Notez la convention d'écriture de cette fonction : Le nom commence par une Majuscule.

Nous venons de définir ce que l'on appelle un constructeur. L'appel de cette fonction, une instanciation, retourne un objet de type Car. L'objet aCar est une instance. Les objets générés possèdent une propriété pos et une fonction move.

Nous avons déjà vu dans l'article pattern décoration que chaque objet possède sa propre fonction move. En sortant la fonction du constructeur, chaque objet va pointer sur la fonction move ce qui évite la duplication coûteuse en mémoire.


En sortant la fonction move du constructeur, nous n'avons pas conservé la closure et nous remplaçons obj par this. This sera l'objet placé à gauche de l'appel de la fonction. lorsque nous écrirons bcar.move() this sera lié à bcar.

Amélirations

Nous devons améliorer notre écriture, en effet, considérons que nous avons une vingtaine de fonctions identiques à move à écrire, ces fonctions apparaîtrons à deux endroits. 

Nous regroupons les fonctions dans un objet methods. L'ajout d'une nouvelle fonction en ligne 12 ne modifie en rien (ligne 4) le constructeur Car.


Mais, en lisant le code, nous remarquons que l'objet methods n'est en rien lié à Car. Ainsi, pour améliorer la lisibilité, nous ajoutons methods comme propriété de la fonction Car. Ce qui évite en plus la création d'une variable globale methods.

Rappel : une fonction est un objet, un objet peut avoir des propriétés et méthodes. Il en est de même pour les fonction ! Une différence est que les fonctions peuvent être appelées. 


Dans cette article, nous avons vue comment une fonction peut être considérée comme une classe. Dans d'autres articles, nous introduirons des amélioration de performance avec la chaîne de prototype : un des fondement de JS.

Annexe : 

Pour la fonction extend(obj1,obj2), on pourra trouver des exemples de code sur le web.

Dans la bibliothèque jquery

L'idée générale est recopier chaque propriété de l'objet2 dans  l'objet1.
var __slice = [].slice;

function extend () {
  var consumer = arguments[0],
      providers = __slice.call(arguments, 1),
      key,
      i,
      provider;

  for (i = 0; i < providers.length; ++i) { 
    provider = providers[i];
    for (key in provider) { // pour toutes les methodes et propriétés
      if (provider.hasOwnProperty(key)) { //oublions la chaîne de prototype 
        consumer[key] = provider[key]; // création d'une prop key et recopie
      };
    };
  };
  return consumer;
};

extend(sam, person);

sam.rename