tnhu / jsface Goto Github PK
View Code? Open in Web Editor NEWSmall, fast, elegant, powerful, and cross platform JavaScript OOP library. Support main(), singleton, super call, private, mixins, plugins, AOP and more.
License: MIT License
Small, fast, elegant, powerful, and cross platform JavaScript OOP library. Support main(), singleton, super call, private, mixins, plugins, AOP and more.
License: MIT License
Hi there,
I love the library so far. Any reason why static methods are inherited at the instance level? It seems better for performance & encapsulation, if class/static methods only belong to the class. Would love to understand the philosophy behind this implementation.
Best,
Yi-Hsiao
Relevant lines of code:
https://github.com/tnhu/jsface/blob/master/jsface.js#L138
https://github.com/tnhu/jsface/blob/master/jsface.js#L153
$statics do not work in 2.4.0 and 2.4.1 on Google Chrome 42.0.2311.135.
All static methods and properties are undefined on derived objects
Code:
"use strict"; // Activates ECMA JavaScript 5 strict mode
var Person = Class({
$statics: {
MIN_AGE: 1,
MAX_AGE: 150,
isValidAge: function(age) {
return age >= this.MIN_AGE && age <= this.MAX_AGE;
}
},
constructor: function(name, age) {
this.name = name;
this.age = age;
}
});
var person = new Person("Rika", 20);
Person.MIN_AGE === person.MIN_AGE; // person.MIN_AGE == undefined
Person.MAX_AGE === person.MAX_AGE; // person.MAX_AGE== undefined
Person.isValidAge(0); // false
person.isValidAge(person.age); // person.isValidAge== undefined.
I am learning jsface, and have written a test class to debug the structure of how I want to write my classes before I code them full blown. Everything works great, unless I try to add EventEmitters to the object. I get the following error
f.updateConfig("new config");
^
TypeError: Object [object Object] has no method 'updateConfig'
By event code is as follows:
var jsface = require("jsface"),
Class = jsface.Class,
extend = jsface.extend,
b = require("bonescript"),
util = require("util"),
EventEmitter = require("events").EventEmitter;
var face = Class({
constructor: function(opts, callback) {
console.log("object created - " + opts);
this.name = opts;
//callback(null);
},
updateConfig: function(opts, callback) {
console.log("config updated - " + opts);
//callback(null);
},
setPower: function(power, callback) {
console.log("set power - " + power);
//callback(null);
},
setState: function(state, callback) {
console.log("state set - " + state);
//callback(null);
},
getData: function(callback) {
console.log("data requested");
callback(null, "data object");
},
getConfig: function(callback) {
console.log("config requested");
callback(null, "config object");
},
$statics: {
actions: {
subscribe: function() {
console.log("subscribe called");
},
setState: function() {
console.log("setState action called");
},
setPower: function() {
console.log("setPower action called");
},
updateConfig: function() {
console.log("updateConfig action called");
},
getConfig: function() {
console.log("getConfig action called");
}
}
}
});
module.exports = face;
//util.inherits(face, EventEmitter);
If I comment out the util.inherits line (as shown), I can access all of the class methods and everything works as expected (just no eventemitters). I have used this eventemitter api on non jsFace objects and it works well. Any ideas? Any alternatives to the EventEmitter?
Thanks.
It should allow the parent functions to be called. It's useful when creating static helpers, are a just a bunch of utility functions, that you may alter, and still reuse the code from the parent singleton.
Hi, I've recently registered you repository in bower package manager (http://bower.io) to simplify using it as a dependency.
One of the requirements of the Bower is tags in repository in semver (http://semver.org/) format.
So, could you please use tags in this repo?
Thanks.
Please, introduce $class field in each instance created. Using it the example provided:
var Student = Class(Person, {
constructor: function(id, name, age) {
this.id = id;
Student.$super.call(this, name, age); // Invoke parent's constructor
}
})
may be written as:
var api = {
constructor: function(id, name, age) {
this.id = id;
this.$class.$super.call(this, name, age); // Invoke parent's constructor
}
}
var Student = Class(Person, api)
This allows to use API mixins when the real class name is not known.
hello Tannhu
i have tested the new files from git, and regarding the issue of ie7 and ie8 the page of unit tests displays no errors. my congrats.
thank for your work fixing the issue. once again my congrats for such a helpful library.
i have a file with your implementations of the api, the examples from the github and i have 2 issues
in all browsers
Mixin with instance:
var person = new Person("Rika", 20);
extend(person, Options);
console.log(person);
/* - inspecting in console
age 20
name "Rika"
$super function()
toString function()
*/
person.setOptions({ foo: true });
gives this error:
person.setOptions is not a function
person.setOptions({ foo: true });
it appears that extends is not working, since it has no prop from the options class
another issue but only in ie7, ie8, works in ie9, ff, chrome, safari
regarding
Mixin with native classes:
extend(Array.prototype, {
trim: function() {
return this.replace(/^\s+|\s+$/g, "");
}
});
" Hello World ".trim(); // "Hello World"
in ie7 and ie8
SCRIPT438: Object doesn't support property or method 'trim'
thank you very much
When trying to create a class with private fields as described in the readme, you actually generate a class with private static fields. Pinpointed it to https://github.com/tannhu/jsface/blob/master/jsface.js#L91. When you pass in a function, this function will actually only be executed once. It should be executed for every new instance.
var App = Class({
$ready: function(clazz, api, parent) {
var fromSubClass = (this !== clazz);
console.log(this !== clazz);
if (fromSubClass) {
api.main.call(clazz);
}
}
});
var Foo = Class(App, {
constructor: function(foo) { this.foo = foo; },
main: function() {
console.log(new Foo("heloo").foo);
}
});
main is not executed in App's context.
hello tahhnhu
first of all my congrats for such a helper in creating something like jsface, a very lightweight oop library,i have already played with your library creating classes, subclasses, extending and augmenting, and using the super call.
will use definitely your library organizing both my client side scripts as well node scripts. so far only played in the client side with your library, cant wait to do the same in node. a oop library to work in both client side and in node, that is great.
when digging more in the examples of the api, i came across this example
extend(Array.prototype, {
trim: function() {
return this.replace(/^\s+|\s+$/g, "");
}
});
" Hello World ".trim(); // "Hello World"
works in ff, chrome, safari, ie9, but does not work in ie8, ie7 and probably ie6 as well
also when debugging, tested the units test page, the page does not display correctly in ie8 and ie7,
in the file /test/tests/core.js
there are some extra commas in the end of the object declaration
line 632 and line 598, extra commas
after fixing this , the page of unit tests display correctly, but reports errors in some unit tests in ie7 and ie8
issues:
unit test 5 - each returned value over a string (1, 0, 1) - invalid each returned value over a string -
unit test 11 - Check type with jsface.isMap on iframe (1, 0, 1) - 1 - Died on test #1: Object doesn't support this action - { "message": "Object doesn't support this action", "description": "Object doesn't support this action", "number": -2146827843, "name": "TypeError" }
unit test 13 - Check type with jsface.isArray on iframe (1, 0, 1) - 1 - Died on test #1: Object doesn't support this action - { "message": "Object doesn't support this action", "description": "Object doesn't support this action", "number": -2146827843, "name": "TypeError" }
unit test 15 - Check type with jsface.isFunction on iframe (1, 0, 1) - 1 - Died on test #1: Object doesn't support this action - { "message": "Object doesn't support this action", "description": "Object doesn't support this action", "number": -2146827843, "name": "TypeError" }
once again congrats for your work developing this library, it looks very promissing.
cheers
I have a node module (Output.js) that I wrote with jsface as follows:
module.exports = Output;
var Output = Class({
code, etc...
})
In the app.js I use is as follows:
var Output = require("./Output");
var output = new Output(config);
When I run app.js I get the following error:
/var/lib/brewbone/bb_hwc/test_output.js:23
var output = new Output(config);
^
TypeError: undefined is not a function
at Object. (/var/lib/brewbone/bb_hwc/test_output.js:23:14)
at Module._compile (module.js:456:26)
at Object.Module._extensions..js (module.js:474:10)
at Module.load (module.js:356:32)
at Function.Module._load (module.js:312:12)
at Function.Module.runMain (module.js:497:10)
at startup (node.js:119:16)
at node.js:902:3
As I am a noob, I assume I have done something stupid, but for the life of me I cannot pinpoint it. Any suggestions?
The link https://cdnjs.cloudflare.com/ajax/libs/jsface/2.4.0/jsface.js does not work. It seems only versions 2.3.0 and 2.2.0 are on cdnjs.
This link does work for me: http://cdn.jsdelivr.net/jsface/2.4.6/jsface.min.js
copied from the readme
http://jsfiddle.net/7xAES/4/
Well known, that new operator doesn't allow you to invoke it as Function.apply(this, arguments). To create instances in places where the actual number of arguments is not known, please, introduce Class.create() function. It may be used like this:
var Person = Class(Human, { ... })
function() { //<-- factory method
return Person.create.apply(Person, arguments)
}
or simply like this:
function() {
return Person.create(arguments)
}
or directly instead of new operator (as a 'statis' constructor):
var p = Person.create(id, name, ...)
I get this error when I try this example:
var Student = Class(Person, {
constructor: function(id, name, age) {
});
extend(Student, [ Options, Events ]);
I've tried both referencing jsface and include it in the script
Hi, jsface is very nice given its speed and simplicity. I only found one issue that instanceof is broken for classes created by jsface. Besides of fixing instanceof, I have one wish to have instanceof also works for mixins, which acts as interfaces. Thanks.
Hi, I'm trying to combine requirejs with jsface and i have a strange behavior.
Consider this simple project example layout:
+app/
+core/
Person.js
Student.js
I have created two classes declared in two seperate modules defined with requirejs:
Person.js:
define(["jsface"], function() {
Class = jsface.Class, extend = jsface.extend;
var Person = Class(
{constructor : function(name, age) {
this.name = name;
this.age = age;
},
});return Person;};
Studen.js:
define(["jsface","Person"], function(Person) {
Class = jsface.Class, extend = jsface.extend;
var Student= Class([Person],
{constructor : function(id,name, age) {
this.id=id;
Student.$super.call(this,name,age);
},
});return Student;};
when i use Student class constructor it fails on: Student.$super.call(this,name,age); the error thrown is "$super" is not a function.
It works well if i move the Person class declaration in the same requirejs module (inside the define function of Student.js...).
It would be nice to know if this is a normal behavior or there is a jsface problem such as requirejs incompatibility.
Thanks in advance.
Luigi
Testing in Chrome 26.0.1410.64 on Windows Server 2008 R2 / 7
ERROR
ReferenceError: jsface is not defined.
ReferenceError: my is not defined.
ReferenceError: JRClass is not defined.
ReferenceError: klass is not defined.
ReferenceError: Classy is not defined.
ReferenceError: PTClass is not defined.
What is $superp? What does it do? Unfortunately I am not able to find any documentation about $superp.
When no constructor defined, an empty function is created instead, which however does not call super constructor. Expected behavior is if sub class has no constructor it automatically calls super class one.
Suggested fix:
// construct constructor
clazz = singleton ? {} : (constructor ? (overload ? overload("constructor", constructor) : constructor) :
(isClass(parent) ? function(){ parent.apply(this, arguments); } : function(){}));
A better way may be using the p found in later code, which require rearrange the code a little bit
Hi! I'm using jsface for the new objects of my project, but I have a lot of "legacy objects", like:
var Robot = function() {
this.getRotation = function() { return 0; }
};
I'm trying to create a child of Robot using jsface, like:
var RobotChild = Class(Robot, function() {
return {
};
});
But, when I do:
var robot = new RobotChild();
I get an empty instance.
Regards
Hello,
Thank you for writing jsface, I think it is a powerful OOP library for JS.
I came across the following issue that occurs in 2.3. but not in 2.2.
In the following simplified code a child class C is defined based on a parent class P.
Defining C results in the constructor of P to be called, see the 'No Name!!' output.
Calling the constructor of parent P when defining child class C is unwanted behaviour in my opinion.
A fix would be awesome!
var Class = require('jsface').Class
var P = Class({
constructor : function(name) {
if (!name) {
console.error('No Name!!');
} else {
console.info('OK');
}
}
});
//We define child class C here based on parent class P, which results in
// the constructor of P to be called
var C = Class(P, {
constructor : function() {
C.$super.call(this, "TEST");
}
});
//CONSOLE OUTPUT:
No Name!!
var c = new C()
//CONSOLE OUTPUT:
OK
When i try to extend ES6 class (transpiled to ES5 with babel) as a parent class and I call MyJSFaceCLass.$super.call(this);
I get an error with pointing line to $super.call
as mentioned above
Cannot call a class as a function
Was anyone able to migrate from jsface to ES6 successfully? As far as I can tell, I'll need to fork jsface to use new
to construct parent object and then extend it, or find a non-standard babel transpiler for transforming classes into pure functions without checking new
keyword generated object instance.
Btw. Is this project dead?
When no constructor is present in an API, Class.overload() function is not invoked on the default constructor.
suppose I have two classes (in different files, in Node):
factory('Core', {
/* my stuff */
mystuff: function(){
/*do*/
}
});
now in a second file I do:
factory('MyClass', 'Core', {
newstuff: function(){
this.$superp.mystuff(); // this is {} and has no $superp
}
});
I want the user to drop the need to use variables, and define a class without having to assign it, because I'm using a repository pattern, when factory is called, a new class is instantiated inside a private repository
object automatically. factory
is merely a wrapper:
global.factory = (function(){
var repository = {};
return function(name, obj, opt){
if (_.isString(obj) && obj in repository) {
return repository[name] = jsface.Class(repository[obj], opt || {});
} else if (_.isUndefined(opt)) {
return repository[name] = jsface.Class(obj);
} else {
return repository[name] = jsface.Class(obj, opt);
}
}
})();
How can I go around this? How can I make it so the "this" points to the current class?
Just reopening this issue: #13
Actually it's a first OOP model I ever seen, which behaves this way. In most cases you expect parent's constructor to be called. If you don't need parent's class constructor to be called from child class, you could define constructor in child class without a $super call, right?
Nope. Not in this library. You have to create an empty constructors, containing only super's call here.
Hi buddy,
this is not enough:
oldClass = context.Class; // save current Class namespace
context.Class = Class; // bind Class and jsface to global scope
context.jsface = jsface;
jsface.noConflict = function() { context.Class = oldClass; }; // no conflict
noConflict
"pattern" should allow to preserve global context from pollution completely, you'll need to backup and return jsface too:
oldClass = context.Class; // save current Class namespace
context.Class = Class; // bind Class and jsface to global scope
var oldJsface = context.jsface;
context.jsface = jsface;
jsface.noConflict = function() {
context.Class = oldClass;
context.jsface = oldJsface;
return jsface;
}; // no conflict
Now people can use noConflict
like this:
var Class = jsface.noConflict().Class;
If you review this comment I can make a PR for you.
HI @tnhu,
I'm Peter from cdnjs.com,
I wonder if you would like to add cdn info in the readme ?
This is the page we provide cdn for jsface - https://cdnjs.com/libraries/jsface
Thanks!
in jsface.extend-method nested objects instead of extending just replace each other.
Example
var Man = Class({
config: {
prefix: 'Mr',
name: '',
eyes: 'green',
hair: 'blonde',
rightHanded: true
},
constructor: function (name) {
this.config.name = name;
},
echo: function () {
console.log(JSON.stringify(this.config));
}
});
var Woman = Class(Man, {
config: {
prefix: 'Ms',
eyes: 'blue'
}
});
var jack = new Man("Jack");
jack.echo();
var rika = new Woman("Rika");
rika.echo();
and as a result we've got
{"prefix":"Mr","name":"Jack","eyes":"green","hair":"blonde","rightHanded":true}
{"prefix":"Ms","eyes":"blue","name":"Rika"}
(name presents because of contructor)
In my opinion its incorrect behavior, all properties must inherit instead of whole object replacing, but only in case of simple objects (not for classes).
On line 60 of jsface.js, the ignored key $superb
is correct? isn't it supposed to be $superp
?
var Class = require('jsface').Class;
var A = Class(function() {
return {
constructor: function() {
console.log('A');
}
};
});
var B = Class(A, function() {
return {
constructor: function() {
this.$class.$super.call(this);
console.log('B');
}
};
});
var C = Class(B, function() {
return {
constructor: function() {
this.$class.$super.call(this);
console.log('C');
}
};
});
new A; // OK
console.log();
new B; // OK
console.log();
new C; // throws RangeError: Maximum call stack size exceeded
Consider the following code:
// define a class with a getter
var test1 = Class({
constructor: function(socket)
{
this._should_exist = {yep: true};
},
// this getter will mess things up...
should_exist: {
get: function() {
return this._should_exist.yep;
}
},
});
// then inherit from it.
// this part will evaluate the 'should_exist' getter and will cause exception, since _should_exist is not yet defined.
var test2 = Class(test1, {
});
The code above will invoke exception: TypeError: Cannot read property 'yep' of undefined. The reason for that, I assume, is because probably somewhere inside jsface tries to copy the prototype attribute should_exist, but instead of copying the definition it tries to evaluate it to return the value.
I didn't look too deep into it though.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.