forked from javascript-tutorial/server
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathclass-extend.js
More file actions
executable file
·108 lines (85 loc) · 3.95 KB
/
Copy pathclass-extend.js
File metadata and controls
executable file
·108 lines (85 loc) · 3.95 KB
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
/**
* Синтаксис:
* Class.extend(props)
* Class.extend(props, staticProps)
* Class.extend([mixins], props)
* Class.extend([mixins], props, staticProps)
*/
!function() {
window.Class = function() { /* вся магия - в Class.extend */ };
Class.extend = function(props, staticProps) {
var mixins = [];
// если первый аргумент -- массив, то переназначить аргументы
if ({}.toString.apply(arguments[0]) == "[object Array]") {
mixins = arguments[0];
props = arguments[1];
staticProps = arguments[2];
}
// эта функция будет возвращена как результат работы extend
function Constructor() {
this.init && this.init.apply(this, arguments);
}
// this -- это класс "перед точкой", для которого вызван extend (Animal.extend)
// наследуем от него:
Constructor.prototype = Class.inherit(this.prototype);
// constructor был затёрт вызовом inherit
Constructor.prototype.constructor = Constructor;
// добавим возможность наследовать дальше
Constructor.extend = Class.extend;
// скопировать в Constructor статические свойства
copyWrappedProps(staticProps, Constructor, this);
// скопировать в Constructor.prototype свойства из примесей и props
for (var i = 0; i < mixins.length; i++) {
copyWrappedProps(mixins[i], Constructor.prototype, this.prototype);
}
copyWrappedProps(props, Constructor.prototype, this.prototype);
return Constructor;
};
//---------- вспомогательные методы ----------
// fnTest -- регулярное выражение,
// которое проверяет функцию на то, есть ли в её коде вызов _super
//
// для его объявления мы проверяем, поддерживает ли функция преобразование
// в код вызовом toString: /xyz/.test(function() {xyz})
// в редких мобильных браузерах -- не поддерживает, поэтому регэксп будет /./
var fnTest = /xyz/.test(function() {xyz}) ? /\b_super\b/ : /./;
// копирует свойства из props в targetPropsObj
// третий аргумент -- это свойства родителя
//
// при копировании, если выясняется что свойство есть и в родителе тоже,
// и является функцией -- его вызов оборачивается в обёртку,
// которая ставит this._super на метод родителя,
// затем вызывает его, затем возвращает this._super
function copyWrappedProps(props, targetPropsObj, parentPropsObj) {
if (!props) return;
for (var name in props) {
if (typeof props[name] == "function"
&& typeof parentPropsObj[name] == "function"
&& fnTest.test(props[name])) {
// скопировать, завернув в обёртку
targetPropsObj[name] = wrap(props[name], parentPropsObj[name]);
} else {
targetPropsObj[name] = props[name];
}
}
}
// возвращает обёртку вокруг method, которая ставит this._super на родителя
// и возвращает его потом
function wrap(method, parentMethod) {
return function() {
var backup = this._super;
this._super = parentMethod;
try {
return method.apply(this, arguments);
} finally {
this._super = backup;
}
}
}
// эмуляция Object.create для старых IE
Class.inherit = Object.create || function(proto) {
function F() {}
F.prototype = proto;
return new F;
};
}();