-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathindex.html
379 lines (314 loc) · 10.6 KB
/
index.html
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
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
background: #f5f5f5;
color: #4d4d4d;
font-weight: 300;
}
#login {
margin: 20px auto;
width: 300px;
height: 200px;
background-color: white;
border-radius: 10px;
box-shadow: 0 5px 10px rgba(0,0,0,.05),0 5px 5px rgba(0,0,0,.08),0 7px 10px rgba(0,0,0,.09),0 22px 22px rgba(0,0,0,.05);
text-align: center;
vertical-align: middle;
line-height: 200px;
}
input[type="button"] {
display: block;
margin: auto;
}
</style>
<body>
<input id="loginBtn" type="button" value="Log in"></input>
<input id="bindBtn" type="button" value="Binding"></input>
<script type="text/javascript">
//Lazy-loaded Singleton in JS
/*
Singleton initialized on Demand
*/
//Bad Practice, create it when page loaded, and then hide it.
// var loginWindow = (function() {
// var div = document.createElement("div");
// div.innerHTML = "Login Window";
// div.style.display = "none";
// div.setAttribute("id", "login");
// document.body.appendChild(div);
// return div;
// })();
// document.getElementById("loginBtn").onclick = function() {
// loginWindow.style.display = "block";
// }
//This approach is not good since sometimes user does not needs to login, and the dom node, which always exits, is a waste.
//Let's change the code so that the window will only be created when the user click on the "log in" button.
//We use the idea of closure here again
// var createLoginWindow = (function() {
// var div;
// return function() {
// if ( !div ) {
// div = document.createElement("div");
// div.innerHTML = "Login Window";
// div.style.display = "none";
// div.setAttribute("id", "login");
// document.body.appendChild(div);
// }
// return div;
// }
// })();
// document.getElementById("loginBtn").onclick = function() {
// var loginWindow = createLoginWindow();
// loginWindow.style.display = "block";
// }
// This looks better, but again we are facing the same problems: the createLoginWindow is in charge of two functions, checking singleton and create new object. As mentioned, we want to separate them.
/* logic for checking singleton
var obj
if ( !obj ) {
obj = xxx
}
*/
// //Below is the general code for getSinglton
// var getSinglton = function(fn) {
// var result;
// return function() {
// return result || (result = fn.apply(this, arguments));
// }
// }
// // fn is the function we pass in as a parameter, its duty is to create the object, such as createLoginWindow, createXhr...
// var createLoginWindow = function() {
// var div = document.createElement("div");
// div.innerHTML = "Login Window";
// div.style.display = "none";
// div.setAttribute("id", "login");
// document.body.appendChild(div);
// return div;
// }
// var createSingletonLoginWindow = getSinglton(createLoginWindow);
// document.getElementById("loginBtn").onclick = function() {
// var loginWindow = createSingletonLoginWindow();
// loginWindow.style.display = "block";
// }
// //What's more, getSinglton can also be used to bind the event listener only once.
// var bindEvent = getSinglton(function() {
// document.getElementById("bindBtn").addEventListener("click", function() {
// console.log("click");
// });
// return true;
// });
// var render = function() {
// console.log("rendering...");
// bindEvent();
// }
// render();
// render();
// render();
// // you can see even though we call rendering 3 times, only one event listener has been bined to the Binding button
</script>
<script type="text/javascript">
// We can further improve it by proxy
// We first make the init function separate from the checking function
// //I only in charge of init now
// var CreateLoginDiv = function(html) {
// this.html = html;
// this.init();
// }
// CreateLoginDiv.prototype.init = function() {
// var div = document.createElement('div');
// div.innerHTML = this.html;
// document.body.appendChild(div);
// }
// //Then we can introduce the proxy to do the checking function
// var ProxySingletonCreateLoginDiv = (function() {
// var instance;
// return function(html) {
// if (!instance) {
// instance = new CreateLoginDiv(html);
// }
// return instance;
// }
// })();
// you can use the proxy as constructor, though what it returns is technically not created ("new") by itself, but another constructor (CreateLoginDiv).
// var a = new ProxySingletonCreateLoginDiv("Twitter Login");
// var b = new ProxySingletonCreateLoginDiv("Another Twitter Login");
// if (a === b) {console.log(true)};
// For more information about proxy, visit the [Proxy Pattern].
// The above code you see is more like the tradition OO language such as Java, where object always comes from class. However, javascript is class-free. You can simply make an object without class, Thus we don't need to follow the traditional OO style.
// Remember, the key of Singleton is to ensure there is only one instantiation, and provide global access to it.
// As a result, a singleton in js can be as simple as declaring a global variable
// var a = {};
// //However, we all know that global variable is not good. It might pollute our namespace, and can be easily overwritten by local variables. To solve it:
// //1. Use proper namespace
// var namespace1 = {
// a: function() {
// console.log("hello world by ns1");
// },
// b: function() {
// console.log("goodbye world by ns1");
// }
// }
// //or do it dynamically (quoted from "Object-Oriented Javascript")
// var myApp = {};
// myApp.namespace = function(name) {
// //e.g name = "store.food.meat"
// var parts = name.split("."); //parts = ["store", "food", "meat"]
// var current = myApp;
// // parts.forEach(function(attr) {
// // if ( !current[attr]) {
// // current[attr] = {}
// // }
// // current = current[attr];
// // });
// for (var i in parts) {
// if (!current[ parts[i] ]) {
// current[ parts[i] ] = {};
// }
// current = current[ parts[i] ];
// }
// }
// myApp.namespace("event");
// myApp.namespace("dom.style");
// myApp.namespace("dom.style.width");
// myApp.namespace("dom.style.height");
// console.log(myApp);
// /*
// you will get
// var Myapp = {
// event: {},
// dom: {
// style: {
// width: {},
// height: {}
// }
// }
// }
// */
//Oh, here an interesting quetsion will be how to get all names from the object. This is a typical depth-first Search(DFS) question, below is the solution, for more information, go to my link on [Algorithim].
// var names = [];
// function dfs(obj, result, name) {
// var keys = Object.keys(obj);
// if (keys.length == 0) {
// //reach the leaf node, add the name to the result collection
// result.push(name.join("."));
// } else {
// for (var i = 0, key; key = keys[i++];) {
// name.push(key);
// dfs(obj[key], result, name);
// name.pop();
// }
// }
// }
// dfs(myApp, names, []);
// console.log(names);
// //Use closure to make private variable
// var user = (function() {
// var _name = "Tom",
// _food = "Jerry";
// return {
// getUserInfo: function() {
// return _name + " eats " + _food;
// }
// }
// })();
// console.log(user.getUserInfo());
</script>
<script type="text/javascript">
// Improved version
// make the singleton class transparent
// this is a class to create the unique Login div in the page
// var CreateLoginDiv = (function() {
// //closure
// var instance;
// //the return function is a constructor
// var CreateLoginDiv = function(html) {
// //if the variable in the closure has been defined, the constructor return it
// if (instance) {
// return instance;
// }
// //else, construct a new object and assign it to the variable in the closure
// this.html = html;
// console.log(this);
// this.init();
// return instance = this;
// };
// CreateLoginDiv.prototype.init = function() {
// var div = document.createElement('div');
// div.innerHTML = this.html;
// document.body.appendChild(div);
// }
// return CreateLoginDiv;
// })();
// //we can now do sth like this:
// var a = new CreateLoginDiv("facebook Login");
// var b = new CreateLoginDiv("another facebook Login");
// if (a === b) {console.log(true)};
// This method also has some problems. The IIFE makes the real constructor method unclear to read. Besides, the contructor here is in charge of doing two things:
// var CreateLoginDiv = function(html) {
// if (instance) {
// return instance;
// }
// this.html = html;
// this.init();
// return instance = this;
// };
// first it runs the init function to setup the object
// second it also needs to check if the instance already exit
// In charge of doing two functions is not good for a function, you know 1 + 1 != 1.
</script>
<script type="text/javascript">
// // Follow the tradition OO stype (class -> object)
// // Two simple examples to make singleton
// // first exampe: instance as a property (like the static variable in java) of the class
// var Singleton = function(name) {
// this.name = name;
// this.instance = null;
// }
// Singleton.prototype.getName = function() {
// console.log(this.name);
// }
// // notice this is an attribute of the class, not the prototype
// // similar to static method in Java
// // this attribute is a function that return the only instance of this class
// Singleton.getInstance = function(name) {
// if ( !this.instance) {
// this.instance = new Singleton(name);
// }
// return this.instance;
// }
// var a = Singleton.getInstance("instance1");
// var b = Singleton.getInstance("instance2");
// if (a === b) {
// console.log(true, a.getName(), b.getName());
// }
// //another exampe: instance as a variable in the closure
// var Singleton = function(name) {
// this.name = name;
// };
// Singleton.prototype.getName = function() {
// console.log(this.name);
// }
// Singleton.getInstance = (function(name) {
// //create a closure
// var instance = null;
// return function(name) {
// //this function has reference to the variabe inside the closure
// //it will be return to the getInstance attribute
// if (!instance) {
// instance = new Singleton(name);
// }
// return instance;
// }
// })();//self-invoked only once, create the only reference to instance
// //What is closure, see below
// function closure(input) {
// var valueInClosure = 100;
// return var funcInClosure = function() {
// //remember, a function can also access variables defined outside the function
// //in fact, all functions have access to the scope "above" them.
// console.log(valueInClosure);
// }
// }
// //Through ```Singleton.getInstance``` method, we can have a singleton pattern. However, this class is not transparent, and the way to get instance is different from the general "new" function.
</script>