(开源)JavaScript原型OO示例
赏金编辑:(开源)JavaScript原型OO示例,javascript,oop,prototypal-inheritance,ecmascript-5,prototypal,Javascript,Oop,Prototypal Inheritance,Ecmascript 5,Prototypal,赏金编辑: <!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 sleepin
<!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>
<!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>
"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);
};
//================================================================================//
})();
我正在寻找用纯原型OO范式(think Self)编写的代码。不是典型OO和经典OO的混合体。我不想看到通用的OO包装,只想简单地使用原型OO技术,并且只使用原型OO技术
参考相关问题:
<!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>
<!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>
"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);
};
//================================================================================//
})();
在上面的问题中,我主要集中在
可以这样编写原型OO吗
我们是否需要构造函数和初始化逻辑,有哪些替代方案
新问题:
<!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>
<!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>
"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);
};
//================================================================================//
})();
基本上,在大型开源项目中是否有javascript原型OO的好例子
澄清:
<!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>
<!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>
"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);
};
//================================================================================//
})();
我必须澄清我对典型OO的意思:
- 没有课。只有对象
- 类的概念没有仿真,同样,只有对象和克隆对象来创建新对象
<!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>
<!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>
"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);
};
//================================================================================//
})();
JavaScript中的原型OO和经典OO仿真之间的差异是一个非常灰色的区域。我并不重视避免经典OO。我想以学术的方式学习原型OO,而不是学习经典OO模拟和原型OO的(可能更优化)组合
这就是为什么我“禁止”类,只是为了让我能够以一种纯粹的方式看到这些技术,并扩展我自己的OO工具包
示例:
<!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>
<!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>
"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);
};
//================================================================================//
})();
像jQuery这样的流行示例无法满足第二个条件。jQuery
对象是一个大类仿真。它着重于从类中创建新对象,而不是克隆现有对象
如果我真的知道任何使用“纯”原型OO的例子,我会向你展示。我相信99%的JavaScript OO受经典仿真的影响太大了
奖励积分
如果
- 它有很好的报道/记录
- 有单元测试
- 在github上
我还将接受关于如何编写超越平凡的hello world应用程序的原型OO代码的文章/教程和示例。我不确定您在寻找什么,但我当前的框架允许您以OO方式编程,如下所示:
Cin.define({
name: 'MyApp.Logger',
extends: 'Cin.Component',
implements: ['MyApp.ILogger'],
mixes: {
SomeMixin: 'MyApp.SomeMixin'
},
init: function() {
},
method: function() {
},
statics: {
staticMethod: function() {}
}
});
然后您可以编写如下代码:
var instance = new MyApp.Logger();
instance.method();
MyApp.Logger.staticMethod();
在这里,我并不试图模仿经典的OO。我试图用一种方便而有用的方式来声明继承、混合、接口和一般的OO概念,这样开发人员就可以轻松地编写这样的OO代码。这也让我有机会完成我的自动加载组件,这样您就不再需要处理依赖关系,您可以进行自定义构建,并享受更快的开发速度,因为不需要在每个页面加载100个脚本
如果您想学习典型的OO概念,我认为您应该编写某种继承系统。看看或者。需要记住的一点是,基于原型的系统会扭曲和破坏,它们比基于类的OO语言更强大。在我看来,没有一种编写原型代码的正确方法
我担心大多数(如果不是所有的话)继承系统看起来像是在模仿经典的OO。在我看来,我的框架没有,但它甚至还没有完成。是JavaScript OO的一个很好的例子。它在JavaScript中实现了一个非常复杂的企业级OO层次结构,可以做很多开箱即用的事情。这可能是一个令人望而生畏的阅读(上次我在3.X中检查时,它是超过1MB的原始未压缩JavaScript),但它会给你很多想法。你可以从一开始就获得一个高层次的视图。可能(Crockford是原型继承的支持者,但我还没有仔细梳理它)。它看起来也比面向对象的更具功能性,但我认为真正包含原型继承的代码通常就是这样。您不会找到它
我前段时间去寻找这类东西,这就是我发现的:这篇文章讨论了Self的最佳实践,原始原型语言,最佳实践是使用“traits-object-idiom”,即让对象从只包含方法的“traits-objects”继承,并且没有特定于对象的数据。换句话说,一个可疑地像类的对象
甚至原始的原型语言也会模拟类。下面是一个示例,展示了您正在寻找的OO编程的基础。最好的现实例子可能是jQuery 在学习JavaScript时,您必须记住,它实际上更接近Scheme,而不是C或Java的根。基本上它是C语法中的Scheme 在JavaScript中几乎没有一种情况下应该使用“new”,尤其是在编写API时。似乎添加了“new”操作符,因为它不确定其原型框架。对于大多数开始使用C、C++和java等经典语言编程的人来说,这似乎很奇怪,因为“新”通常是我们所要寻找的,因为它很容易翻译。 你问我为什么不用“新”呢?好的,由于“new”的实现,您可能会在不经意间开始删除全局数据(记住,在JavaScript中不是函数中的所有内容)。如果您碰巧遇到了这种情况,那么您将不会看到任何错误或通知,而只会看到程序中不可预测的行为。同样,你的“类”中的“这个”究竟与什么有关,可能不清楚,也可能不清楚 在不知道的情况下擦除全局内存的问题主要发生在您编写一个打算用“new”调用的函数,而用户不使用“new”时。提示为什么在API中使用它会导致