Advertisement
Gerst20051

JavaScript: The Good Parts

Aug 11th, 2012
308
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 29.12 KB | None | 0 0
  1. This is my condensed version of an already extremely dense book.
  2.  
  3. JavaScript: The Good Parts
  4. By: Douglas Crockford
  5.  
  6. PROTOTYPE
  7.  
  8. Prototype is basically inheritance. All objects are linked to Object.prototype therefore all objects can inherit properties from it and you can specify what object should be it's prototype when you create the object. The prototype link has no effect on updating so when you make changes to an object, the object's prototype is not touched which means that the prototype link is used only in retrieval. When you try to retrieve a property from an object and the object lacks the property name it will try to retrieve it from the prototype object and continues until it reaches Object.prototype if it doesn't exist elsewhere. Another important thing to remember about prototype is that it's a dynamic relationship so when you add a new property to a prototype it's immediately visible to all of the objects based on that prototype. One thing that really clarified this for me is using the delete. The delete operator can be used to remove a property from an object if it has one but it will not touch any of the objects in the prototype linkage. This is cool because you can remove a property from the object and then the prototype value will be revealed.
  9.  
  10. if (typeof Object.create !== 'function') {
  11. Object.create = function (o) {
  12. var F = function () {};
  13. F.prototype = o;
  14. return new F();
  15. };
  16. }
  17.  
  18. var stooge = {
  19. "first-name": "Jerome",
  20. "last-name": "Howard"
  21. };
  22. var another_stooge = Object.create(stooge);
  23.  
  24. var flight = {
  25. airline: "Oceanic",
  26. number: 815,
  27. departure: {
  28. IATA: "SYD",
  29. time: "2004-09-22 14:55",
  30. city: "Sydney"
  31. },
  32. arrival: {
  33. IATA: "LAX",
  34. time: "2004-09-23 10:42",
  35. city: "Los Angeles"
  36. }
  37. };
  38.  
  39.  
  40.  
  41. REFLECTION
  42.  
  43. It is easy to inspect an object to determine what properties it has by attempting to retrieve the properties and examining the values obtained. Some care must be taken because any property on the prototype chain can produce a value:
  44.  
  45. typeof flight.toString // 'function'
  46. typeof flight.constructor // 'function'
  47.  
  48. There are two approaches to dealing with these undesired properties. The first is to reject function values and the second is to use the hasOwnProperty method, which returns true if the object has a particular property. The hasOwnProperty method does no look at the prototype chain:
  49.  
  50. flight.hasOwnProperty('number') // true
  51. flight.hasOwnProperty('constructor') // false
  52.  
  53.  
  54.  
  55. INVOCATION
  56.  
  57. Invoking a function suspends the execution of the current function, passing control and parameters to the new function. In addition to the declared parameters, every function receives two additional parameters: this and arguments. The this parameter is important because its value is determined by its invocation pattern. There are four patterns of invocation in JavaScript: the method invocation pattern, the function invocation pattern, the constructor invocation pattern, and the apply invocation pattern. The patterns differ in how the bonus parameter this is initialized. The invocation operator is a pair of parentheses that follow any expression that produces a function value.
  58.  
  59.  
  60. The Method Invocation Pattern
  61.  
  62. When a function is stored as a property of an object, we call it a method. When a method is invoked, this is bound to that object. A method can use this to access the object so that it can retrieve values from the object or modify the object. If an invocation expression contains a refinement (that is, a . dot expression or [subscript] expression), it is invoked as a method:
  63.  
  64. // Create myObject. It has a value and an increment
  65. // method. The increment method takes an optional
  66. // parameter. If the argument is not a number, then 1
  67. // is used as the default.
  68.  
  69. var myObject = {
  70. value: 0,
  71. increment: function (inc) {
  72. this.value += typeof inc === 'number' ? inc : 1;
  73. }
  74. };
  75.  
  76. myObject.increment();
  77. document.writeln(myObject.value); // 1
  78.  
  79. myObject.increment(2);
  80. document.writeln(myObject.value); // 3
  81.  
  82.  
  83. The Function Invocation Pattern
  84.  
  85. When a function is not the property of an object, then it is invoked as a function:
  86.  
  87. // Create a variable called add and store a function
  88. // in it that adds the two numbers.
  89.  
  90. var add = function (a, b) {
  91. return a + b;
  92. };
  93.  
  94. var sum = add(3,4); // sum is 7
  95.  
  96. When a function is invoked with this pattern, this is bound to the global object. This was a mistake in the design of the language. Had the language been designed correctly, when the inner function is invoked, this would still be bound the this variable of the outer function. A consequence of this error is that a method cannot employ an inner function to help it do its work because the inner function does not share the method's access to the object as its this is bound to the wrong value. Fortunately, this is an easy workaround. If the method defines a variable and assigns it the value of this, the inner function will have access to this through that variable. By convention, the name of that variable is that:
  97.  
  98. // Augment myObject with a double method.
  99.  
  100. myObject.double = function () {
  101. var that = this; // Workaround.
  102.  
  103. var helper = function () {
  104. that.value = add(that.value, that.value);
  105. };
  106.  
  107. helper(); // Invoke helper as a function.
  108. };
  109.  
  110. // Invoke double as a method.
  111.  
  112. myObject.double();
  113. document.writeln(myObject.value);
  114.  
  115.  
  116. The Constructor Invocation Pattern
  117.  
  118. JavaScript is a prototypal inheritance language. That means that objects can inherit properties directly from other objects. The language is class-free. If a function is invoked with the new prefix, then a new object will be created with a hidden link to the value of the function's prototype member, and this will be bound to that new object. The new prefix also changes the behavior of the return statement.
  119.  
  120. // Create a constructor function called Quo.
  121. // It makes an object with a status property.
  122.  
  123. var Quo = function (string) {
  124. this.status = string;
  125. };
  126.  
  127. // Give all instances of Quo a public method
  128. // called get_status.
  129.  
  130. Quo.prototype.get_status = function () {
  131. return this.status;
  132. };
  133.  
  134. // Make an instance of Quo.
  135.  
  136. var myQuo = new Quo("confused");
  137.  
  138. document.writeln(myQuo.get_status()); // confused
  139.  
  140. Functions that are intended to be used with the new prefix are called constructors. By convention, they are kept in variables with a capitalized name. If a constructor is called without the new prefix, very bad this can happen without a compile-time or a runtime warning, so the capitalization convention is really important. Use of this style of constructor functions is not recommended.
  141.  
  142.  
  143. The Apply Invocation Pattern
  144.  
  145. Because JavaScript is a functional object-oriented language, functions can have methods.
  146.  
  147. The apply method lets us construct an array of arguments to use to invoke a function. It also lets us choose the value of this. They apply method takes two parameters. The first is the value that should be bound to this. The second is an array of parameters.
  148.  
  149. // Make an array of 2 numbers and add them.
  150.  
  151. var array = [3,4];
  152. var sum = add.apply(null, array); // sum is 7
  153.  
  154. // Make an object with a status member.
  155.  
  156. var statusObject = {
  157. status: 'A-OK'
  158. };
  159.  
  160. // statusObject does no inherit from Quo.prototype,
  161. // but we can invoke the get_status method on
  162. // statusObject even though statusObject does not have
  163. // a get_status method.
  164.  
  165. var status = Quo.prototype.get_status.apply(statusObject);
  166. // status is 'A-OK'
  167.  
  168.  
  169.  
  170. ARGUMENTS
  171.  
  172. A bonus parameter that makes it possible to write functions that take an unspecified number of parameters. Because of a design error, arguments is not really an array. It is an array-like object. arguments has a length property, but it lacks all of the array methods.
  173.  
  174. // Make a function that adds a lot of stuff.
  175.  
  176. // Note that defining the variable sum inside of
  177. // the function does not interfere with the sum
  178. // defined outside of the function. The function
  179. // only sees the inner one.
  180.  
  181. var sum = function () {
  182. var i, sum = 0;
  183. for (i = 0; i < arguments.length; i += 1) {
  184. sum += arguments[i];
  185. }
  186. return sum;
  187. };
  188.  
  189. document.writeln(sum(4, 8, 15, 16, 23, 42)); // 108
  190.  
  191.  
  192.  
  193. RETURN
  194.  
  195. When a function is invoked, it begins with the first statement, and ends when it hits the } that closes the function body. This causes the function to return control to the part of the program that invoked the function. The return statement can be used to cause the function to return early. When return is executed, the function returns immediately without executing the remaining statements. A function always returns a value. If the return value is not specified, then undefined is returned. If the function was invoked with the new prefix and the return value is not an object then this (the new object) is returned instead.
  196.  
  197.  
  198.  
  199. EXCEPTIONS
  200.  
  201. JavaScript provides an exception handling mechanism. Exceptions are unusual (but not completely unexpected) mishaps that interfere with the normal flow of a program. When such a mishap is detected, your program should throw an exception:
  202.  
  203. var add = function (a, b) {
  204. if (typeof a !== 'number' || typeof b !== 'number') {
  205. throw {
  206. name: 'TypeError',
  207. message: 'add needs numbers'
  208. };
  209. }
  210. return a + b;
  211. }
  212.  
  213. The throw statements interrupts execution of the function. It should be given an exception objected containing a name property that identifies the type of the exception, and a descriptive message property. You can also add other properties. The exception object will be delivered to the catch clause of a try statement:
  214.  
  215. // Make a try_it function that calls the new add
  216. // function incorrectly
  217.  
  218. var try_it = function () {
  219. try {
  220. add("seven");
  221. } catch (e) {
  222. document.writeln(e.name + ': ' + e.message);
  223. }
  224. }
  225.  
  226. try_it();
  227.  
  228. If an exception is thrown within a try block, control will go to its catch clause. A try statement has a single catch block that will catch all exceptions. If your handling depends on the type of the exception, then the exception handler will have to inspect the name to determine the type of the exception.
  229.  
  230.  
  231.  
  232. AUGMENTING TYPES
  233.  
  234. JavaScript allows the basic types of the language to be augmented. We know that adding a method to Object.prototype makes that method available to all objects. This also works for functions, arrays, strings, numbers, regular expressions, and booleans.
  235.  
  236. For example, by augmenting Function.prototype, we can make a method available to all functions:
  237.  
  238. Function.prototype.method = function (name, func) {
  239. if (!this.prototype[name]) {
  240. this.prototype[name] = func;
  241. return this;
  242. }
  243. }
  244.  
  245. By augmenting Function.prototype with a method method, we no longer have to type the name of the prototype property. JavaScript does not have a separate integer type, so it is sometimes necessary to extract just the integer part of a number. The method JavaScript provides to do that is ugly. We can fix it by adding an integer method to Number.prototype. It uses either Math.ceil or Math.floor, depending on the sign of the number:
  246.  
  247. Number.method('integer', function () {
  248. return Math[this < 0 ? 'ceil' : 'floor'](this);
  249. });
  250.  
  251. document.writeln((-10 / 3).integer()); // -3
  252.  
  253. JavaScript lacks a method that removes spaces from the ends of a string. Our trim method uses a regular expression. That is an easy oversight to fix:
  254.  
  255. String.method('trim', function () {
  256. return this.replace(/^\s+|\s+$/g, '');
  257. });
  258.  
  259. document.writeln('"' + " neat ".trim() + '"');
  260.  
  261. Because of the dynamic nature of JavaScript's prototypal inheritance, all values are immediately endowed with the new methods, even values that were created before the methods were created.
  262.  
  263.  
  264.  
  265. RECURSION
  266.  
  267. A recursive function is a function that calls itself, either directly or indirectly. Generally, a recursive function calls itself to solve its subproblems. Recursive functions can be very effective at manipulating tree structures such as the browser's Document Object Model (DOM). Each recursive call is given a smaller piece of the tree to work on:
  268.  
  269. // Defins a walk_the_DOM function that visits every
  270. // node of the tree in HTML source order, starting
  271. // from some given node. It invokes a function,
  272. // passing it each node in turn. walk_the_DOM calls
  273. // itself to process each of the child nodes.
  274.  
  275. var walk_the_DOM = function walk(node, func) {
  276. func(node);
  277. node = node.firstChild;
  278. while (node) {
  279. walk(node, func);
  280. node = node.nextSibling;
  281. }
  282. };
  283.  
  284. // Define a getElementsByAttribute function. It
  285. // takes an attribute name string and optional
  286. // matching value. It calls walk_the_DOM, passing it a
  287. // function that looks for an attribute name in the
  288. // node. The matching nodes are accumulated in a
  289. // results array.
  290.  
  291. var getElementsByAttribute = function (att, value) {
  292. var results = [];
  293.  
  294. walk_the_DOM(document.body, function (node) {
  295. var actual = node.nodeType === 1 && node.getAtrribute(att);
  296. if (typeof actual === 'string' && (actual === value || typeof value !== 'string')) {
  297. results.push(node);
  298. }
  299. });
  300. };
  301.  
  302. Some languages offer the tail recursion optimization. This means that if a function returns the result of invoking itself recursively, then the invocations is replaced with a loop, which can significantly speed things up. Unfortunately, JavaScript does not currently provide tail recursion optimization. Functions that recurse very deeply can fail by exhausting the return stack:
  303.  
  304. // Make a factorial function with tail recursion. It is
  305. // tail because it returns the result of calling itself.
  306.  
  307. var factorial = function factorial(i, a) {
  308. a = a || 1;
  309. if (i < 2) {
  310. return a;
  311. }
  312. return factorial(i - 1, a * i);
  313. };
  314.  
  315. document.writeln(factorial(4)); // 24
  316.  
  317.  
  318.  
  319. CLOSURE
  320.  
  321. Earlier, we made a myObject that had a value and an increment method. Suppose we wanted to protect the value from unauthorized changes. Instead of initializing myObject with an object literal, we will initialize myObject by calling a function that returns an object literal. That function defines a value variable. That variable is always available to the increment and getValue methods, but the functions scope keeps it hidden from the rest of the program:
  322.  
  323. var myObject = (function () {
  324. var value = 0;
  325.  
  326. return {
  327. increment: function (inc) {
  328. value += typeof inc === 'number' ? inc : 1;
  329. },
  330. getValue: function () {
  331. return value;
  332. }
  333. };
  334. }());
  335.  
  336. We are not assigning a function to myObject. We are assigning the result of invoking that function. Notice the () on the last line. The function returns an object containing two methods, and these methods continue to enjoy the privilege of access to the value variable.
  337.  
  338. // Create a maker function called quo. It makes an object
  339. // with a get_status method and a private status property.
  340.  
  341. var quo = function (status) {
  342. return {
  343. get_status: function () {
  344. return status;
  345. }
  346. };
  347. };
  348.  
  349. // Make an instance of quo.
  350.  
  351. var myQuo = quo("amazed");
  352.  
  353. document.writeln(myQuo.get_status()); // amazed
  354.  
  355. The quo function is designed to be used without the new prefix, so the name is not capitalized. When we call quo, it returns a new object containing a get_status method. A reference to that object is stored in myQuo. The get_status method still has privileged access to quo's status property even though quo has already returned. get_status does not have access to a copy of the parameter; it has access to the parameter itself. This is possible because the function has access to the context in which it was created. This is called closure.
  356.  
  357. // Define a function that sets a DOM node's color
  358. // to yellow and then fades it to white.
  359.  
  360. var fade = function (node) {
  361. var level = 1;
  362. var step = function () {
  363. var hex = level.toString(16);
  364. node.style.backgroundColor = '#FFFF' + hex + hex;
  365. if (level < 15) {
  366. level += 1;
  367. setTimeout(step, 100);
  368. }
  369. };
  370. setTimeout(step, 100);
  371. };
  372.  
  373. fade(document.body);
  374.  
  375. // Make a function that assigns event handler functions to an array of nodes.
  376. // When you click on a node, an alert box will display the ordinal of the node.
  377.  
  378. var add_the_handlers = function (nodes) {
  379. var helper = function (i) {
  380. return function (e) {
  381. alert(i);
  382. };
  383. };
  384. var i;
  385. for (i = 0; i < nodes.length; i += 1) {
  386. nodes[i].onclick = helper(i);
  387. }
  388. };
  389.  
  390. Avoid creating functions within a loop. It can be wasteful computationally, and it can cause confusion. We avoid the confusion by creating a helper function outside of the loop that will deliver a function that binds to the current value of i.
  391.  
  392.  
  393.  
  394. CURRY
  395.  
  396. Functions are values, and we can manipulate function values in interesting ways. Currying allows us to produce a new function by combining a function and an argument:
  397.  
  398. var add1 = add.curry(1);
  399. document.writeln(add1(6)); // 7
  400.  
  401. add1 is a function that was created by passing 1 to add's curry method. The add1 function adds 1 to its argument. JavaScript does not have a curry method, but we can fix that by augmenting Function.prototype:
  402.  
  403. Function.method('curry', function () {
  404. var slice = Array.prototype.slice,
  405. args = slice.apply(arguments),
  406. that = this;
  407. return function () {
  408. return that.apply(null, args.concat(slice.apply(arguments)));
  409. };
  410. });
  411.  
  412. The curry method works by creating a closure that holds the original function and the arguments to curry. It returns a function that, when invoked, returns the result of calling that original function, passing it all of the arguments from the invocation of curry and the current invocation. It uses the Array concat method to concatenate the two arrays of arguments together.
  413.  
  414.  
  415.  
  416. MEMOIZATION
  417.  
  418. Functions can use objects to remember the results of previous operations, making it possible to avoid unnecessary work. This optimization is called memoization. JavaScript's objects and arrays are very convenient for this. We will keep our memoized results in a memo array that we can hide in a closure. When our function is called, it first looks to see if it already knows the result. If it does, it can immediately return it:
  419.  
  420. var fibonacci = (function () {
  421. var memo = [0, 1];
  422. var fib = function (n) {
  423. var result = memo[n];
  424. if (typeof result !== 'number') {
  425. result = fib(n - 1) + fib(n - 2);
  426. memo[n] = result;
  427. }
  428. return result;
  429. };
  430. return fib;
  431. }());
  432.  
  433. We can generalize this by making a function that helps us make memoized functions. The memoizer function will take an initial memo array and the formula function. It returns a recur function that manages the memo store and that calls the formula function as needed. We pass the recur function and the function's parameters to the formula function:
  434.  
  435. var memoizer = function (memo, formula) {
  436. var recur = function (n) {
  437. var result = memo[n];
  438. if (typeof result !== 'number') {
  439. result = formula(recur, n);
  440. memo[n] = result;
  441. }
  442. return result;
  443. };
  444. return recur;
  445. };
  446.  
  447. We can now define fibonacci with the memoizer, providing the initial memo array and formula function:
  448.  
  449. var fibonacci = memoizer([0, 1], function (recur, n) {
  450. return recur(n - 1) + recur(n - 2);
  451. });
  452.  
  453. By devising functions that produce other functions, we can significantly reduce the amount of work we have to do. For example, to produce a memoizing factorial function, we only need to supply the basic factorial formula:
  454.  
  455. var factorial = memoizer([1, 1], function (recur, n) {
  456. return n * recur(n - 1);
  457. });
  458.  
  459.  
  460.  
  461. INHERITANCE
  462.  
  463. When a function object is created, the Function constructor that produces the function object runs some code like this:
  464.  
  465. this.prototype = {constructor: this};
  466.  
  467. The new function object is given a prototype property whose value is an object containing a constructor property whose value is the new function object. The prototype object is the place where inherited traits are to be deposited. Every function gets a prototype object because the language does not provide a way of determining which functions are intended to be used as constructors. The constructor property is not useful. It is the prototype object that is important.
  468.  
  469. When a function is invoked with the constructor invocation pattern using the new prefix, this modifies the way in which the function is executed. If the new operator were a method instead of an operator, it could have been implemented like this:
  470.  
  471. Function.method('new', function () {
  472. // Create a new object that inherits from the
  473. // constructor's prototype.
  474.  
  475. var that = Object.create(this.prototype);
  476.  
  477. // Invoke the constructor, binding -this- to
  478. // the new object.
  479.  
  480. var other = this.apply(that, arguments);
  481.  
  482. // If it's return value isn't an object,
  483. // substitute the new object.
  484.  
  485. return (typeof other === 'object' && object) || that;
  486. });
  487.  
  488. Function.method('inherits', function (Parent) {
  489. this.prototype = new Parent();
  490. return this;
  491. });
  492.  
  493. Our inherits and method methods return this, allowing us to program in a cascade style. We can now make our Cat with one statement.
  494.  
  495. var Cat = function (name) {
  496. this.name = name;
  497. this.saying = 'meow';
  498. }.
  499. inherits(Mammal).
  500. method('purr', function (n) {
  501. var i, s = '';
  502. for (i = 0; i < n; i += 1) {
  503. if (s) {
  504. s+= '-';
  505. }
  506. s += 'r';
  507. }
  508. return s;
  509. }).
  510. method('get_name', function () {
  511. return this.says() + ' ' + this.name + ' ' + this.says();
  512. });
  513.  
  514. Here is a pseudocode template of a functional constructor:
  515.  
  516. var constructor = function (spec, my) {
  517. var that, other private instance variables;
  518. my = my || {};
  519.  
  520. Add shared variables and functions to my
  521.  
  522. that = a new object;
  523.  
  524. Add privileged methods to that
  525.  
  526. return that;
  527. };
  528.  
  529. Next, add the shared secrets to the my object. This is done by assignment.
  530.  
  531. my.member = value;
  532.  
  533. Next, we augment that, adding the privileged methods that make up the object's interface. We can assign new functions to members of that. Or, more securely, we can define the functions first as private methods, and then assign them to that.
  534.  
  535. var methodical = function () {
  536. ...
  537. };
  538. that.methodical = methodical;
  539.  
  540. The advantage to defining methodical in two steps is that if other methods want to call methodical, they can call methodical() instead of that.methodical(). If the instance is damaged or tampered with so that that.methodical is replaced, the methods that call methodical will continue to work the same because their private methodical is not affected by modification of the instance.
  541.  
  542. Finally, we return that.
  543.  
  544. The name and saying properties are now completely private. They are accessible only via the privileged get_name and says methods:
  545.  
  546. var mammel = function (spec) {
  547. var that = {};
  548.  
  549. that.get_name = function () {
  550. return spec.name;
  551. };
  552.  
  553. that.says = function () {
  554. return spec.saying || '';
  555. };
  556.  
  557. return that;
  558. };
  559.  
  560. var myMammel = mammel({name: 'Herb'});
  561.  
  562. The functional pattern also gives us a way to deal with super methods. We will make a superior method that takes a method name and returns a function that invokes that method. The function will invoke the original method even if the property is changed:
  563.  
  564. Object.method('superior', function (name) {
  565. var that = this,
  566. method = that[name];
  567. return function () {
  568. return method.apply(that, arguments);
  569. };
  570. });
  571.  
  572. Let's try it out on a coolcat that is just like cat except it has a cooler get_name method that calls the super method. It requires just a little bit of preparation. We will declare a super_get_name variable and assign it the result of invoking the superior method:
  573.  
  574. var coolcat = function (spec) {
  575. var that = cat(spec),
  576. super_get_name = that.superior('get_name');
  577. that.get_name = function (n) {
  578. return 'like ' + super_get_name() + ' baby';
  579. };
  580. return that;
  581. };
  582.  
  583. var myCoolCat = coolcat({name: 'Bix'});
  584. var name = myCoolCat.get_name(); // 'like meow Bix meow baby'
  585.  
  586.  
  587.  
  588. PARTS
  589.  
  590. We can compose objects out of sets of parts. For example, we can make a function that can add simple event processing features to any object. It adds an on method, a fire method, and a private event registry:
  591.  
  592. var eventuality = function (that) {
  593. var registry = {};
  594.  
  595. that.fire = function (event) {
  596. // Fire an event on an object. The event can be either
  597. // a string containing the name of the event or an
  598. // object containing a type property containing the
  599. // name of the event. Handlers registered by the 'on'
  600. // method that match the event name will be invoked.
  601.  
  602. var array,
  603. func,
  604. handler,
  605. i,
  606. type = typeof event === 'string' ? event : event.type;
  607.  
  608. // If an array of handlers exist for this event, then
  609. // loop through it and execute the handlers in order.
  610.  
  611. if (registry.hasOwnProperty(type)) {
  612. array = registry[type];
  613. for (i = 0; i < array.length; i += 1) {
  614. handler = array[i];
  615.  
  616. // A handler record contains a method and an optional
  617. // array of parameters. If the method is a name, look
  618. // up the function.
  619.  
  620. func = handler.method;
  621. if (typeof func === 'string') {
  622. func = this[func];
  623. }
  624.  
  625. // Invoke a handler. If the record contained
  626. // parameters, then pass them. Otherwise, pass the
  627. // event object.
  628.  
  629. func.apply(this, handler.parameters || [event]);
  630. }
  631. }
  632. return this;
  633. };
  634.  
  635. that.on = function (type, method, parameters) {
  636. // Register an event. Make a handler record. Put it
  637. // in a handler array, making one if it doesn't yet
  638. // exist for this type.
  639.  
  640. var handler = {
  641. method: method,
  642. parameters: parameters
  643. };
  644. if (registry.hasOwnProperty(type)) {
  645. registry[type].push(handler);
  646. } else {
  647. registry[type] = [handler];
  648. }
  649. return this;
  650. };
  651. return that;
  652. };
  653.  
  654. We could call eventuality on any individual object, bestowing it with event handling methods. We could also call it in a constructor function before that is returned:
  655.  
  656. eventuality(that);
  657.  
  658. In this way, a constructor could assemble objects from a set of parts. JavaScript's loose typing is a big benefit here because we are not burdened with a type system that is concerned about the lineage of classes. Instead, we can focus on the character of their contents.
  659.  
  660. If we wanted eventuality to have access to the object's private state, we could pass it the my bundle.
  661.  
  662.  
  663.  
  664. ARRAYS
  665.  
  666. var is_array = function (value) {
  667. return Object.prototype.toString.apply(value) === '[object Array]';
  668. };
  669.  
  670.  
  671. Methods
  672.  
  673. JavaScript provides a set of methods for acting on arrays. The methods are functions stored in Array.prototype.
  674.  
  675. For example, suppose we want to add an array method that will allow us to do computation on an array:
  676.  
  677. Array.method('reduce', function (f, value) {
  678. var i;
  679. for (i = 0; i < this.length; i += 1) {
  680. value = f(this[i], value);
  681. }
  682. return value;
  683. });
  684.  
  685. By adding a function to Array.prototype, every array inherits the method. In this case, we defined a reduce method that takes a function and a starting value. For each element of the array, it calls the function with an element and the value, and computes a new value. When it is finished, it returns the value. If we pass in a function that adds two numbers, it computes the sum. If we pass in a function that multiplies two numbers, it computes the product:
  686.  
  687. // Create an array of numbers.
  688.  
  689. var data = [4, 8, 15, 16, 23, 42];
  690.  
  691. // Define two simple functions. One will add two
  692. // numbers. The other will multiple two numbers.
  693.  
  694. var add = function (a, b) {
  695. return a + b;
  696. };
  697.  
  698. var mult = function (a, b) {
  699. return a * b;
  700. };
  701.  
  702. // Invoke the data's reduce method, passing in the add function.
  703.  
  704. var sum = data.reduce(add, 0); // sum is 108
  705.  
  706. // Invoke the reduce method again, this time passing in the multiply function.
  707.  
  708. var product = data.reduce(mult, 1); // product is 7418880
  709.  
  710. Because an array is really an object, we can add methods directly to an individual array:
  711.  
  712. // Give the data array a total function.
  713.  
  714. data.total = function () {
  715. return this.reduce(add, 0);
  716. };
  717.  
  718. total = data.total(); // total is 108
  719.  
  720. Since the string 'total' is not an integer, adding a total property to an array does not change its length. Arrays are most useful when the property names are integers, but they are still objects, and objects can accept any string as a property name.
  721.  
  722. It is not useful to use the Object.create method on arrays because it produces an object, not an array. The object produced will inherit the array's values and methods, but it will not have the special length property.
  723.  
  724. Dimensions
  725.  
  726. Array.dim = function (dimension, initial) {
  727. var a = [], i;
  728. for (i = 0; i < dimension; i += 1) {
  729. a[i] = initial;
  730. }
  731. return a;
  732. };
  733.  
  734. // Make an array containing 10 zeros.
  735.  
  736. var myArray = Array.dim(10, 0);
  737.  
  738. Array.matrix = function (m, n, initial) {
  739. var a, i, j, mat = [];
  740. for (i = 0; i < m; i += 1) {
  741. a = [];
  742. for (j = 0; j < n; j += 1) {
  743. a[j] = initial;
  744. }
  745. mat[i] = a;
  746. }
  747. return mat;
  748. };
  749.  
  750. // Make a 4 * 4 matrix filled with zeros.
  751.  
  752. var myMatrix = Array.matrix(4, 4, 0);
  753.  
  754. document.writeln(myMatrix[3][3]); // 0
  755.  
  756. // Mathod to make an identity matrix.
  757.  
  758. Array.identity = function (n) {
  759. var i, mat = Array.matrix(n, n, 0);
  760. for (i = 0; i < n; i += 1) {
  761. mat[i][i] = 1;
  762. }
  763. return mat;
  764. };
  765.  
  766. myMatrix = Array.identity(4);
  767.  
  768. document.writeln(myMatrix[3][3]); // 1
  769.  
  770.  
  771.  
  772. REGULAR EXPRESSIONS
  773.  
  774. var parse_url = /^(?:([A-Za-z]+):)?(\/{0,3})([0-9.\-A-Za-z]+)(?::(\d+))?(?:\/([^?#]*))?(?:\?([^#]*))?(?:#(.*))?$/;
  775.  
  776. Let's call parse_url's exec method. If it successfully matches the string that we pass it, it will return an array containing pieces extracted from the url:
  777.  
  778.  
  779.  
  780. TO BE CONTINUED... TOMORROW
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement