Link zur Norm ECMAScript-262 (pdf 2.5MB) Typische ECMAScript-Code-Muster sind: Constructor Pattern, Module Pattern, Revealing Module Pattern, Singleton Pattern, Observer Pattern, Mediator Pattern, Prototype Pattern, Command Pattern, Facade Pattern, Factory Pattern, Mixin Pattern, Decorator Pattern, Flyweight Pattern.
In ECMAScript fehlt eine Methode str.trim()
,
die alle Leerzeichen am Stringanfang und am Stringende entfernt.
Hierzu können den bereits verfügbaren (eingebauten) String-Funktionen
eine weitere hinzu gefügt werden, etwa:
String.prototype.trim = function () { return this.replace(/^\s+|\s+$/g, ""); }; document.write( " hallo ".length + ", " + " hallo ".trim().length );
reverse
invertiert die Zeichenfolge:
String.prototype.reverse = function(){ return this.split('').reverse().join(''); } alert( '✡123456789'.reverse() );
Implizite Deklaration werden oft als Prototyp bezeichnet.
Mit der folgenden Hilfsfunktion Function.prototype.method
kann das
Schreiben von neuen, "quasi eingebauten" Funktionen vereinfacht werden.
function Constructor(...) { this.membername = value; } Constructor.prototype.membername = value;
Beispiel 1: Der eingebaute Konstruktor String-Konstruktor kann zu den vorhandenen Stringfunktionen kann um die Funktion trim erweitert werden:
Beispiel:
String-Konstruktor und RexExpr
a) mit RexExpr programmiert
String.prototype.trim = function () { return this.replace(/^\s+|\s+$/g,""); };
b) konservativ programmiert
function trim(s) {if (!s || s=="") return ""; while ((s.charAt(0)==' ') ||(s.charAt(0)=='\n')||(s.charAt(0,1)=='\r')) s=s.substring(1,s.length); while ((s.charAt(s.length-1)==' ') ||(s.charAt(s.length-1)=='\n') ||(s.charAt(s.length-1)=='\r'))s=s.substring(0,s.length-1); return s; }
Beispiel 2: Jetzt soll ein möglicher "Nachbau" der C-Funktion sprintf (variable Anzahl und unterschiedliche Parametern-Typen) angedeutet werden.
var fs = "%s %d"; // Formatstring var out = fs.sprintf("Text und ", 123)
Als Beispiel erfordert der Format-String
"hex: %x, hex: %4x, dez: %d, dez: %4d"
4 Parameter.
Der eingebaute Konstruktor String-Konstruktor
kann (zu den vorhandenen Stringfunktionen)
um die Funktion
sprintf
erweitert werden:
String.prototype.sprintf = function sprintf() { var p = new String(this); var args = String.prototype.sprintf.arguments; if(args.length < 2) return p; var p_ = p.split("%"); var n,bl, blank = "000000000000"; for(var i=0; i < args.length; i++) { var x = args[i]; switch(typeof x){ case 'string': if(/^\d*?s/.test(p_[i+1])) { n = parseInt(p_[i+1]); bl = ""; if(n>x.length)bl=blank.substr(0,n-x.length); p = p.replace(/(^[\s\S]*?)%\d*?s/,"$1"+bl+x); } break; case 'number': if(/^\d*?d/.test(p_[i+1])) { n = parseInt(p_[i+1]); x = ""+x; bl = ""; if(n>x.length)bl=blank.substr(0,n-x.length); p = p.replace(/(^[\s\S]*?)%\d*?d/,"$1"+bl+x); } if(/^\d*?x/.test(p_[i+1])) { n = parseInt(p_[i+1]); x = x.toString(16); bl = ""; if(n>x.length)bl=blank.substr(0,n-x.length); p = p.replace(/(^[\s\S]*?)%\d*?x/,"$1"+bl+x); } break; // demnaechst ... fuer float f ... var k = x.toFixed(2); default: // boolean,function,object,undefined break; } } return p; }
function Constructor(...) { var that = this; var membername = value; function membername(...) {...} }
Die Schreibweise
function membername(...) {...}
ist eine Kurzschreibweise für
var membername = function membername(...) {...};
function Constructor(...) { this.membername = function (...) {...}; }
Das folgende Beispiel zeigt das Prinzip von privaten Variablen, öffentlichen Variablen, privaten Methoden, öffentlichen Methoden, get-Methoden, set-Methoden.
//static object (it's like Math) var StaticObject = function() { /* Private members */ var privateVar = "Private"; function privateFunc(){ return "Private"; }; /* Public members */ return { publicVar: "Public", getPrivateVar: function(){ return privateVar; }, setPrivateVar: function(v){ privateVar = v; } }; }();
Hier kommt unvollständiger Quelltext, der das Prinzip einer Basis-Klasse (und abgeleiteter Objekte) aufzeigt:
//base class; inherits Object function BaseClass(arg1, arg2){ /* Private members; only accessible within this class */ var privateVar = "Private"; function privateFunc(){ return "Private"; }; /* Protected members; only accessible within this class and derived classes */ //Not supported in JavaScript /* Public members */ //override inherited toString() method this.toString = function(){ return "[object BaseClass]"; }; this.publicVar = "Public"; this.publicFunc = function(){ return "Public"; }; } //static method of BaseClass class; // this is not inherited (it's like String.fromCharCode()) BaseClass.staticMethod = function(){ return "Static"; }; //subclass; inherits BaseClass function SubClass(arg1, arg2){ BaseClass.apply(this, [arg1, arg2]); //construct this object using the BaseClass constructor /* Private and Public members specific to SubClass */ //override inherited toString() method this.toString = function(){ return "[object SubClass]"; }; //... } //put SubClass into the prototype chain as a subclass of BaseClass SubClass.prototype = new BaseClass();
Dieses Beispiel findet sich hier (Vergleich zwischen JavaScript und Java, Creating the Hierarchy).
JavaScript | Java |
---|---|
function Employee () { this.name = ""; this.dept = "general"; } |
public class Employee { public String name; public String dept; public Employee () { this.name = ""; this.dept = "general"; } } |
JavaScript | Java |
function Manager () { this.reports = []; } Manager.prototype = new Employee; function WorkerBee () { this.projects = []; } WorkerBee.prototype = new Employee; |
public class Manager extends Employee { public Employee[] reports; public Manager () { this.reports = new Employee[0]; } } public class WorkerBee extends Employee { public String[] projects; public WorkerBee () { this.projects = new String[0]; } } |
JavaScript | Java |
function SalesPerson () { this.dept = "sales"; this.quota = 100; } SalesPerson.prototype = new WorkerBee; function Engineer () { this.dept = "engineering"; this.machine = ""; } Engineer.prototype = new WorkerBee; |
public class SalesPerson extends WorkerBee { public double quota; public SalesPerson () { this.dept = "sales"; this.quota = 100.0; } } public class Engineer extends WorkerBee { public String machine; public Engineer () { this.dept = "engineering"; this.machine = ""; } } |
In "http://ejohn.org/blog/simple-javascript-inheritance/"
wird beschrieben, wie
var MYCLASS = Class.extend({ ... });
als "Class-factory" inspired by base2 and Prototype
angelegt wird. Hier ein Testbeispiel:
(function(){ var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
// The base Class implementation (does nothing) this.Class = function(){}; // Create a new Class that inherits from this class Class.extend = function(prop) { var _super = this.prototype; // Instantiate a base class (but only create the instance, // don't run the init constructor) initializing = true; var prototype = new this(); initializing = false; // Copy the properties over onto the new prototype for (var name in prop) { // Check if we're overwriting an existing function prototype[name] = typeof prop[name] == "function" && typeof _super[name] == "function" && fnTest.test(prop[name]) ? (function(name, fn){ return function() { var tmp = this._super; // Add a new ._super() method that is the same method // but on the super-class this._super = _super[name]; // The method only need to be bound temporarily, so we // remove it when we're done executing var ret = fn.apply(this, arguments); this._super = tmp; return ret; }; })(name, prop[name]) : prop[name]; } // The dummy class constructor function Class() { // All construction is actually done in the init method if ( !initializing && this.init ) this.init.apply(this, arguments); } // Populate our constructed prototype object Class.prototype = prototype; // Enforce the constructor to be what we expect Class.constructor = Class; // And make this class extendable Class.extend = arguments.callee; return Class; };
})();
Hier ein Test-Beispiel:
var A = Class.extend({ init: function(v1,v2){this.v1=v1; this.v2=v2;}, val: "A-val", getVal: function() { return this.val; } }); var B = A.extend({ init: function(v1){ this.v1=v1; }, val: "B-val" }); var C = B.extend({ init: function(v1){ this.v1=v1; this._super( false ); }, val: "C-val", getVal: function(){ return this._super(); } }); var a = new A("A-new"); var b = new B("B-new"); var c = new C("C-new"); var s = "<pre> a.v1 = " + a.v1 +" a.val = " + a.val +" a.v2 = " + a.v2 +" a.getVal() = " + a.getVal() +"" +" b.v1 = " + b.v1 +" b.val = " + b.val +" b.getVal() = " + b.getVal() +"" +" c.v1 = " + c.v1 +" c.val = " + c.val +" c.getVal() = " + c.getVal() +"" +" (a instanceof A) = " + (a instanceof A) +" (b instanceof A) = " + (b instanceof A) +" (c instanceof A) = " + (c instanceof A) +"</pre>"; document.write(s); </script>
Test-Beispiel-Ausgabe: