This repository has been archived by the owner on Sep 29, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 262
/
autobind.js
112 lines (90 loc) · 2.87 KB
/
autobind.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
import { decorate, createDefaultSetter,
getOwnPropertyDescriptors, getOwnKeys, bind } from './private/utils';
const { defineProperty, getPrototypeOf } = Object;
let mapStore;
function getBoundSuper(obj, fn) {
if (typeof WeakMap === 'undefined') {
throw new Error(
`Using @autobind on ${fn.name}() requires WeakMap support due to its use of super.${fn.name}()
See https://github.com/jayphelps/core-decorators.js/issues/20`
);
}
if (!mapStore) {
mapStore = new WeakMap();
}
if (mapStore.has(obj) === false) {
mapStore.set(obj, new WeakMap());
}
const superStore = mapStore.get(obj);
if (superStore.has(fn) === false) {
superStore.set(fn, bind(fn, obj));
}
return superStore.get(fn);
}
function autobindClass(klass) {
const descs = getOwnPropertyDescriptors(klass.prototype);
const keys = getOwnKeys(descs);
for (let i = 0, l = keys.length; i < l; i++) {
const key = keys[i];
const desc = descs[key];
if (typeof desc.value !== 'function' || key === 'constructor') {
continue;
}
defineProperty(klass.prototype, key, autobindMethod(klass.prototype, key, desc));
}
}
function autobindMethod(target, key, { value: fn, configurable, enumerable }) {
if (typeof fn !== 'function') {
throw new SyntaxError(`@autobind can only be used on functions, not: ${fn}`);
}
const { constructor } = target;
return {
configurable,
enumerable,
get() {
// Class.prototype.key lookup
// Someone accesses the property directly on the prototype on which it is
// actually defined on, i.e. Class.prototype.hasOwnProperty(key)
if (this === target) {
return fn;
}
// Class.prototype.key lookup
// Someone accesses the property directly on a prototype but it was found
// up the chain, not defined directly on it
// i.e. Class.prototype.hasOwnProperty(key) == false && key in Class.prototype
if (this.constructor !== constructor && getPrototypeOf(this).constructor === constructor) {
return fn;
}
// Autobound method calling super.sameMethod() which is also autobound and so on.
if (this.constructor !== constructor && key in this.constructor.prototype) {
return getBoundSuper(this, fn);
}
const boundFn = bind(fn, this);
defineProperty(this, key, {
configurable: true,
writable: true,
// NOT enumerable when it's a bound method
enumerable: false,
value: boundFn
});
return boundFn;
},
set: createDefaultSetter(key)
};
}
function handle(args) {
if (args.length === 1) {
return autobindClass(...args);
} else {
return autobindMethod(...args);
}
}
export default function autobind(...args) {
if (args.length === 0) {
return function (...argsClass) {
return handle(argsClass);
};
} else {
return handle(args);
}
}