I'm starting using facebook javscript SDK, and I found a interesting thing when I read the source code.
The code example is as belowed:
/**
* Annotates a function with a meta object
*/
function annotate(fn, meta) {
meta.superClass = fn.__superConstructor__;
fn.__TCmeta = meta;
return fn;
}
// export to global
__w = annotate;
/**
* when using the annotate function
*/
function sprintf(format) {
// function body
}
__w(sprintf, {"signature":"function(string)"}); // <-- what is the purpose of doing this?
So, my question is what is that used for? What is the benefit doing this?
FYI, the whole source code is here, where you can see lots of annotate() is being used
http://connect.facebook.net/en_US/all/debug.js
It appears to be a home-grown strong typing setup:
/**
* A recursive descent analyzer which takes a value and a typehint, validating
* whether or not the value matches the typehint.
* The function will call it self as long as both the value and the typehint
* yields a nested component. This means that we will never recurse deeper
* than needed, and also that we automatically get support for
* > equals([], 'array<string>') // true
* > equals(['string'], 'array') // true
*/
function equals(value, node) {
var type = typeof value;
var subType;
var nextNode;
var nextValue;
//: Nullable types are delimited with a leading ?
//: ?string, ?boolean, etc.
var nullable = /^\?/.test(node);
if (nullable) {
node = node.substring(1);
}
//: snip ...
switch (type) {
// start by testing the most common types
case 'boolean':
case 'number':
case 'string':
case 'undefined':
break;
default:
//: snip ...
// let functions with signatures also match 'function'
type = value.__TCmeta && node !== 'function'
? value.__TCmeta.signature
: 'function';
} else if (type === 'object' || type === 'function') {
// HTMLObjectElements has a typeof function in FF
var constructor = value.constructor;
if (constructor && constructor.__TCmeta) {
// The value is a custom type
//: snip ...
while (constructor && constructor.__TCmeta) {
if (constructor.__TCmeta.type == node) {
type = node;
break;
}
constructor = constructor.__TCmeta.superClass;
}
//: snip ...
}
}
}
}
if (nullable && /undefined|null/.test(type)) {
return true;
}
if (type in typeInterfaces) {
var interfaces = typeInterfaces[type], i = interfaces.length;
while (i--) {
if (interfaces[i] === node) {
type = node;
break;
}
}
}
currentType.push(type);
return nextValue && nextNode
? node === type && equals(nextValue, nextNode)
: subType && nextNode
? node === type && subType === nextNode
: node === type;
}
/**
* Given a value and a typehint (can be a union type), this will return
* whether or not the passed in value matches the typehint.
*/
function matches(value, node) {
var nodes = node.split('|'), i = nodes.length;
while (i--) {
currentType = [];
if (equals(value, nodes[i])) {
return true;
}
}
return false;
}
The reason they use the annotate
function is to allow type hinting for custom types and function signatures. Without annotate
you could only do matches(someVar, "function")
. With annotate
you can do matches(someVar, "function(string, ?int)|function(string)")
and only accept functions that take a string and a nullable integer
or functions that only accept a string.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With