-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy path3_1_js-objects.clj
165 lines (157 loc) · 5.41 KB
/
3_1_js-objects.clj
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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
(chapter "JavaScript-like objects")
(example "Maps as objects"
(def point {:x 10 :y 20})
point
(point :x))
(section "Prototypes")
(example "Object with prototype"
(def shifted-point {:prototype point :dx 1 :dy 2 :y 100}))
(example "Get with prototype"
(defn proto-get
"Returns object property respecting the prototype chain"
([obj key] (proto-get obj key nil))
([obj key default]
(cond
(contains? obj key) (obj key)
(contains? obj :prototype) (proto-get (obj :prototype) key default)
:else default))))
(example "Own property"
(proto-get shifted-point :dx))
(example "Inherited property"
(proto-get shifted-point :x))
(example "Overridden property"
(proto-get shifted-point :y))
(example "Missing property"
(proto-get shifted-point :z))
(example "Missing property with default"
(proto-get shifted-point :z 1000))
(section "Methods")
(example "Points with methods"
(def point
{:x 10
:y 20
:getX (fn [this] (proto-get this :x))
:getY (fn [this] (proto-get this :y))
:setX (fn [this x] (assoc this :x x))
:setY (fn [this y] (assoc this :y y))
:add (fn [this a b] (+ a b))
})
(def shifted-point
{:prototype point
:dx 1
:dy 2
:getX (fn [this] (+ (proto-get this :x) (proto-get this :dx)))
:getY (fn [this] (+ (proto-get this :y) (proto-get this :dy)))
}))
(example "Call method"
(defn proto-call
"Calls object method respecting the prototype chain"
[this key & args]
(apply (proto-get this key) this args)))
(example "Own method"
(proto-call point :getX))
(example "Overridden method"
(proto-call shifted-point :getX))
(example "Mutator method"
(proto-call point :setX -10)
(proto-call (proto-call point :setX -10) :getX)
(proto-call shifted-point :setX -10)
(proto-call (proto-call shifted-point :setX -10) :getX))
(example "Multi-argument method"
(proto-call point :add 2 3)
(proto-call shifted-point :add 2 3))
(section "Syntactic sugar")
(example "Field declaration"
(defn field
"Creates field"
[key] (fn
([this] (proto-get this key))
([this def] (proto-get this key def)))))
(example "Method declaration"
(defn method
"Creates method"
[key] (fn [this & args] (apply proto-call this key args))))
(example "Fields"
(def __x (field :x))
(def __y (field :y))
(def __dx (field :dx))
(def __dy (field :dy)))
(example "Methods"
(def _getX (method :getX))
(def _getY (method :getY))
(def _setX (method :setX))
(def _setY (method :setY))
(def _add (method :add)))
(example "Points"
(def point
{:x 10
:y 20
:getX __x
:getY __y
:setX (fn [this x] (assoc this :x x))
:setY (fn [this y] (assoc this :y y))
:add (fn [this a b] (+ a b))
})
(def shifted-point
{:prototype point
:dx 1
:dy 2
:getX (fn [this] (+ (__x this) (__dx this)))
:getY (fn [this] (+ (__y this) (__dy this)))
}))
(example "Fields usage"
(__x point)
(__x shifted-point)
(__dx shifted-point)
(__dx point 100))
(example "Methods usage"
(_getX point)
(_getX shifted-point)
(_getX (_setX shifted-point 1000))
(_add shifted-point 2 3))
(section "Constructors")
(example "Constructor declaration"
(defn constructor
"Defines constructor"
[ctor prototype]
(fn [& args] (apply ctor {:prototype prototype} args))))
(example "Supertype"
(declare _Point)
(def _distance (method :distance))
(def _length (method :length))
(def _sub (method :sub))
(def PointPrototype
{:getX __x
:getY __y
:sub (fn [this that] (_Point (- (_getX this) (_getX that))
(- (_getY this) (_getY that))))
:length (fn [this] (let [square #(* % %)] (Math/sqrt (+ (square (_getX this)) (square (_getY this))))))
:distance (fn [this that] (_length (_sub this that)))
})
(defn Point [this x y]
(assoc this
:x x
:y y))
(def _Point (constructor Point PointPrototype)))
(example "Subtype"
(def ShiftedPointPrototype
(assoc PointPrototype
:getX (fn [this] (+ (__x this) (__dx this)))
:getY (fn [this] (+ (__y this) (__dy this)))))
(defn ShiftedPoint [this x y dx dy]
(assoc (Point this x y)
:dx dx
:dy dy
))
(def _ShiftedPoint (constructor ShiftedPoint ShiftedPointPrototype)))
(example "Instances"
(def point (_Point 10 20))
(def shifted-point (_ShiftedPoint 10 20 1 2))
(_getX point)
(_getX shifted-point)
(__x point)
(__x shifted-point)
(__dx shifted-point)
(_length (_Point 4 3))
(_sub (_Point -1 -2) (_Point 2 2))
(_distance (_Point -1 -2) (_Point 2 2)))