Dans mon cadre, tout est un objet ou une "interface".
Les Interfaces à la définition de fonctions (méthodes / property_gets / property_sets) que les objets peuvent avoir.
Vous créez une interface comme ceci: var some_interface = GetInterface(constructor, interface_setup, parent_interface..)
Vous pouvez spécifier n'importe quel nombre de parent_interfaces. Donc, si interface_A
hérite à la fois interface_B
et interface_C
, vous pouvez créer interface_A en tant que telle: GetInterface(constructor, interface_setup, interface_B, interface_C);
Si interface_A
hérite interface_X
et interface_X
hérite interface_Y
, alors interface_A
aurez toutes les fonctions à la fois interface_X
et interface_Y
a.
Les Interfaces qui ne nécessitent pas un constructeur de quitter l'argument du constructeur comme null. Le interface_setup est une fonction qui ressemble à ceci:
function(proto){
}
L'objet que le proto argument points à a 4 méthodes: SetM
, ShadowM
, SetP
, et ShadowP
.
Vous utilisez ces 4 méthodes pour l'installation de votre interface.
Ce cadre permet aussi paresseux instanciation d'interfaces. (en d'autres termes, le code de programme d'installation ne sera jamais réellement être exécuté jusqu'à ce qu'il est en fait d'abord nécessaire).
Les limites de ce cadre nécessite au moins en charge pour Object.keys
, Object.getOwnPropertyDescriptor
et Object.defineProperty
. (En d'autres termes, il travaille dans les dernières versions de FireFox, IE, Chrome, Safari mais pas de l'Opéra)
TestPage2.html:
<!doctype html>
<script src="js.js"></script>
<script>
var human = GetInterface(function(){
},function(proto){
//alert("trace: initing human");
proto.$SetM("Sleep",function(){
alert(this.Name+" is sleeping");
});
proto.$SetP("Name",function(){
return this._name;
},function(value){
this._name=value;
});
});
var female = GetInterface(function(){
},function(proto){
//alert("trace: initing female");
proto.$SetM("Dance",function(){
alert(this.Name+" is dancing");
});
},human);
var male = GetInterface(function(){
},function(proto){
//alert("trace: initing male");
proto.$SetM("Fight",function(){
alert(this.Name+" is fighting");
});
proto.$ShadowP("Name",function(parent_get){
return "Mr. "+parent_get();
},function(parent_set,value){
parent_set(value);
});
},human);
var child = GetInterface(function(){
},function(proto){
//alert("trace: initing child");
proto.$SetM("Play",function(){
alert(this.Name+" is playing");
});
},human);
var adult = GetInterface(function(){
},function(proto){
//alert("trace: initing adult");
proto.$SetM("Work",function(){
alert(this.Name+" is working");
});
},human);
var mammal = GetInterface(function(){
},function(proto){
//alert("trace: initing mammal");
proto.$SetM("DoMammalStuff",function(){
alert("doing mammal stuff");
});
});
var john=new male();
john.Name="john";
john.Sleep();
var mary=new female();
mary.$IsA(child);
mary.$IsA(mammal);
mary.$Setup(function(proto){
proto.$ShadowP("Name",function(parent_get){
return "Miss "+parent_get.call(this);
},function(parent_set,value){
parent_set.call(this,value);
});
});
mary.Name="mary";
mary.Play();
</script>
TestPage.html:
<!doctype html>
<script src="js.js"></script>
<script>
var human_interface = GetInterface(function(){
},function(proto){
alert("trace: initing human");
proto.$SetM("Sleep",function(){
alert(this.Name+" is sleeping");
});
proto.$SetP("Name",function(){
return this._name;
},function(value){
this._name=value;
});
});
var female_interface = GetInterface(function(){
},function(proto){
alert("trace: initing female");
proto.$SetM("Dance",function(){
alert(this.Name+" is dancing");
});
},human_interface);
var male_interface = GetInterface(function(){
},function(proto){
alert("trace: initing male");
proto.$SetM("Fight",function(){
alert(this.Name+" is fighting");
});
},human_interface);
var child_interface = GetInterface(function(){
},function(proto){
alert("trace: initing child");
proto.$SetM("Play",function(){
alert(this.Name+" is playing");
});
},human_interface);
var adult_interface = GetInterface(function(){
},function(proto){
alert("trace: initing adult");
proto.$SetM("Work",function(){
alert(this.Name+" is working");
});
},human_interface);
var mammal_interface = GetInterface(function(){
},function(proto){
alert("trace: initing mammal");
proto.$SetM("DoMammalStuff",function(){
alert("doing mammal stuff");
});
});
var john={};
john.$IsA(adult_interface);
//the above 2 lines are equal to simply doing:
//var john=new adult_interface();
//you can think of it as a shortcut
john.$IsA(mammal_interface);
john.DoMammalStuff();
john.Name="john";
john.Sleep();
var mary=new female_interface();
mary.$IsA(child_interface);
mary.$IsA(mammal_interface);
mary.DoMammalStuff();
mary.Name="mary";
mary.Play();
mary.Dance();
</script>
Js.js:
"use strict";
var GetInterface;
(function(){
//================================================================================//
//(constructor:Function, setup:Function?, parent_interfaces:Function..):Function
GetInterface = function (constructor, setup) {
var parent_classes = GetParray(arguments, 2);
var output = function () {
output.$Init();
for (var x = parent_classes.length - 1; x >= 0; --x) {
parent_classes[x](this);
}
if(constructor===null){
constructor.apply(this, arguments);
}
};
output.$Init = Mize(function () {
var output_proto = output.prototype;
parent_classes.forEach(function (parent_class) {
parent_class.$Init();
Infect(output_proto, parent_class.prototype);
});
init_proto(output_proto,setup);
if(setup!==undefined){
setup(output_proto);
}
});
return output;
};
var init_proto=function(proto){
$defineProperty(proto, "$SetM", { value: set_m, writable: true, configurable: true });
$defineProperty(proto, "$ShadowM", { value: shadow_m, writable: true, configurable: true });
$defineProperty(proto, "$SetP", { value: set_p, writable: true, configurable: true });
$defineProperty(proto, "$ShadowP", { value: shadow_p, writable: true, configurable: true });
};
var set_m = function (method_name, method) {
this[method_name] = method;
};
var set_p = function (property_name, getter, setter) {
$defineProperty(this, property_name, { get: getter, set: setter, enumerable: true, configurable: true });
};
var shadow_m = function (method_name, supplied_method) {
var old_method = this[method_name];
this[method_name] = function () {
var args = GetParray(arguments);
args.unshift(old_method.bind(this));
supplied_method.apply(this, args);
};
};
var shadow_p = function (property_name, getter, setter) {
var old_descriptor = $getOwnPropertyDescriptor(this, property_name);
var old_get = old_descriptor.get;
var old_set = old_descriptor.set;
$defineProperty(this, property_name, { get: function () {
return getter.call(this, old_get.bind(this));
}, set: function (value) {
setter.call(this, old_set.bind(this), value);
}, enumerable: true, configurable: true
});
};
var $slice=Array.prototype.slice;
var $defineProperty=Object.defineProperty;
var $getOwnPropertyDescriptor=Object.getOwnPropertyDescriptor;
if($defineProperty===undefined){
throw "Object.defineProperty, Object.getOwnPropertyDescriptor, Object.keys are required";
}
//================================================================================//
//(victim:Object, disease:Object):void
var Infect=function (victim, disease, excludes) {
var keys=Object.keys(disease);
if(excludes!==undefined){
excludes.forEach(function(exclude){
ForEach(keys,function(key,x){
if(key===exclude){
keys.splice(x,1);
return false;
}
});
});
}
keys.forEach(function(key){
$defineProperty(victim, key, $getOwnPropertyDescriptor(disease, key));
});
};
//================================================================================//
//(args:Object # arguments object #, start_index:int?):Array
var GetParray = function (args, start_index) {
if (start_index === undefined) {
start_index = 0;
}
return $slice.call(args, start_index);
};
//================================================================================//
//(array:Array, f:Function(item:Object|null, index:pint):boolean?):Object
var ForEach=function(array,f){
for (var x = 0, xx = array.length, last_index=xx-1; x < xx; ++x) {
var result = f(array[x], x, last_index);
if (result !== undefined) {
return result;
}
}
};
//================================================================================//
//provides memoization.
//(f:Function, arity_fixed:boolean?true):Function
//caching is done according to the inputs. the results of calling function(undefined) and function() are cached differently.
//if arity is fixed, optimizations can be done
var Mize=function(f, arity_fixed) {
if (arity_fixed === undefined) {
arity_fixed = true;
}
var used; //for 0 arg
var result; //for 0 arg
var results; //for >0 args
var used_params; //for 1 arg
var used_param_sets; //for >1 args
var f_length = f.length;
var use_generic = !arity_fixed || f_length > 3;
if (use_generic) { //if `f_length` <= 3, it will be optimized (i.e. not using generic function)
results = [];
used_param_sets = [];
return function () {
var params = GetParray(arguments);
var result_found = false;
var result = ForEach(used_param_sets,function (used_param_set, x) {
if (used_param_set.length === params.length) {
var params_match = true;
ForEach(params,function (param, y) {
if (used_param_set[y] !== param) {
params_match = false;
return false;
}
});
if (params_match) {
result_found = true;
return results[x];
}
}
});
if (!result_found) {
used_param_sets.push(params);
result = f.apply(null, params);
results.push(result);
}
return result;
};
}
if (f_length === 0) {
used = false;
return function () {
if (!used) {
result = f();
used = true;
}
return result;
};
}
if (f_length === 1) {
used_params = [];
} else {
used_param_sets = [];
}
results = [];
switch (f_length) {
case 1:
return function (arg) {
var result_found = false;
var result = ForEach(used_params,function (used_param, x) {
if (arg === used_param) {
result_found = true;
return results[x];
}
});
if (!result_found) {
used_params.push(arg);
result = f(arg);
results.push(result);
}
return result;
};
break;
case 2:
return function (arg1, arg2) {
var result_found = false;
var result = ForEach(used_param_sets,function (used_param_set, x) {
if (arg1 === used_param_set[0] && arg2 === used_param_set[1]) {
result_found = true;
return results[x];
}
});
if (!result_found) {
used_param_sets.push([arg1, arg2]);
result = f(arg1, arg2);
results.push(result);
}
return result;
};
break;
case 3:
return function (arg1, arg2, arg3) {
var result_found = false;
var result = ForEach(used_param_sets,function (used_param_set, x) {
if (arg1 === used_param_set[0] && arg2 === used_param_set[1] && arg3 === used_param_set[2]) {
result_found = true;
return results[x];
}
});
if (!result_found) {
used_param_sets.push([arg1, arg2, arg3]);
result = f(arg1, arg2, arg3);
results.push(result);
}
return result;
};
break;
default:
throw "Invalid `f_length`: " + f_length;
}
};
//================================================================================//
Object.prototype.$Setup=function(setup){
setup(Object.getPrototypeOf(this));
};
//================================================================================//
Object.prototype.$IsA=function(_interface){
var excludes=GetParray(arguments,1);
if(this.$SetM===undefined){
this.$SetM=set_m;
this.$SetP=set_p;
this.$ShadowM=shadow_m;
this.$ShadowP=shadow_p;
}
_interface.$Init();
/*var this_proto={};
init_proto(this_proto);
Infect(this_proto,Object.getPrototypeOf(this));
this.__proto__=this_proto;*/
Infect(Object.getPrototypeOf(this),_interface.prototype,excludes);
};
//================================================================================//
})();