Advertisement
Gerst20051

JavaScript Guide

Oct 18th, 2012
301
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 18.37 KB | None | 0 0
  1. JavaScript Guide
  2. By: Andrew Gerst
  3.  
  4. JavaScript Pitfalls
  5. http://jsfiddle.net/gerst20051/BfNa8/
  6.  
  7. Table of Contents
  8. - What not to do?
  9. - for in problem
  10. - Number methods
  11. - String trim
  12. - typeof
  13. - Array.isArray
  14. - True or False?
  15. - Operators
  16. -- &&
  17. -- ||
  18. -- !
  19. -- Bitwise
  20. - Labels (Break statement)
  21. - Switch statement
  22. - With statement
  23.  
  24. - apply form
  25. - this
  26. - Closure
  27. - Closure Conditional
  28. - later method
  29. - curry method
  30. - try-catch
  31. - Sealer/Unsealer
  32. - Pseudoclassical Inheritance
  33. - Prototypal Inheritance
  34. - New Constructor
  35. - Functional Inheritance
  36. - Functional Inheritance (Privacy)
  37. - Shared Secrets
  38. - Super Methods
  39. - Event handlers in a loop
  40. - The Y Combinator
  41. - Prototype
  42.  
  43. What not to do?
  44.  
  45. - Don't make functions in a loop
  46. -- It can be wasteful because a new function object is created on every iteration.
  47. -- It can be confusing because the new function closes over the loop's variables, not over their current values.
  48. -- Anything that doesn't change in every iteration of the loop should be moved outside of the loop.
  49.  
  50.  
  51. The for in problem
  52. - Functions inherited from a prototype are included in the for in enumeration
  53.  
  54. for (name in object) {
  55. if (Object.prototype.hasOwnProperty.call(object, name) {
  56. ...
  57. }
  58. }
  59.  
  60.  
  61. Number methods
  62.  
  63. if (!Number.prototype.trunc) {
  64. Number.prototype.trunc = function trunc(number) {
  65. return Math[number >= 0 ? 'floor' : 'ceil'](number);
  66. }
  67. }
  68.  
  69.  
  70. String trim
  71. - String trim method was added in ES5.
  72.  
  73. if (typeof String.prototype.trim !== 'function') {
  74. String.prototype.trim = function () {
  75. return this.replace(/^\s*(\S*(\s+\S+)*)\s*$/,"$1");
  76. };
  77. }
  78.  
  79.  
  80. typeof
  81. - Returns a string identifying the type of a value.
  82. - Returns the wrong value for null and not very helpful with arrays.
  83.  
  84. type -> typeof
  85. ----------------------------------
  86. object -> 'object'
  87. function -> 'function'
  88. array -> 'object' // ERROR
  89. number -> 'number'
  90. string -> 'string'
  91. boolean -> 'boolean'
  92. null -> 'object' // ERROR
  93. undefined -> 'undefined'
  94.  
  95. Object.toType = (function toType(global){
  96. return function(obj){
  97. if (obj === global) return "global";
  98. return ({}).toString.call(obj).match(/\s([a-z|A-Z]+)/)[1].toLowerCase();
  99. }
  100. })(this);
  101.  
  102.  
  103. Array.isArray
  104.  
  105. alert(Array.isArray([])); // true
  106.  
  107. if (typeof Array.isArray !== 'function') {
  108. Array.isArray = function (value) {
  109. return Array.prototype.toString.apply(value) === '[object Array]';
  110. }
  111. }
  112.  
  113.  
  114. True or False?
  115.  
  116. Falsy values
  117. - false
  118. - null
  119. - undefined
  120. - "" (empty string)
  121. - 0
  122. - NaN
  123.  
  124. All other values (including all objects) are truthy.
  125. "0" and "false"
  126.  
  127.  
  128. &&
  129. - The guard operator, aka logical and
  130. - If first operand is truthy then return second operand else return first operand.
  131. - It can be used to avoid null references
  132.  
  133. if (a) {
  134. return a.member;
  135. } else {
  136. return a;
  137. }
  138.  
  139. can be written as
  140.  
  141. return a && a.member;
  142.  
  143.  
  144. ||
  145. - The default operator, aka logical or
  146. - If first operand is truthy then result is first operand else result is second operand.
  147. - It can be used to fill in default value.
  148.  
  149. var last = input || nr_items;
  150. // If input is truthy, then last is input, otherwise set last to nr_items.
  151.  
  152.  
  153. !
  154. - Prefix logical not operator
  155. - If the operand is truthy, the result is false. Otherwise, the result is true.
  156. - !! produces booleans.
  157.  
  158.  
  159. Bitwise
  160. & | ^ >> >>> <<
  161.  
  162. - The bitwise operators convert the 64-bit floating point operand to a 32-bit signed integer, and turn the result back into 64-bit floating point.
  163. - Typically used for machine values. JavaScript doesn't have machine values.
  164. - Don't do multiplication by using shift. Not a win and not faster in JavaScript like it is in C.
  165.  
  166.  
  167. Labels (Break statement)
  168. - Statements can have labels.
  169. - Break statements can refer to those labels.
  170. - Label can be any JavaScript identifier that is not a reserved word.
  171.  
  172. loop: for (;;) {
  173. ...
  174. if (...) {
  175. break loop;
  176. }
  177. ...
  178. }
  179.  
  180.  
  181. Switch statement
  182. - The switch value does not need to be a number. It can be a string.
  183. - The case values can be expressions.
  184. - Danger: Cases fall through to the next case unless a disruptive statement like break ends the case.
  185.  
  186. switch (expression) {
  187. case ';':
  188. case ',':
  189. case '.':
  190. punctuation();
  191. break;
  192. default:
  193. noneOfTheAbove();
  194. }
  195.  
  196.  
  197. With statement
  198. - Inteded as a convenient short-hand
  199. - Ambiguous
  200. - Error-prone
  201. - Don't use it
  202.  
  203. with (o) {
  204. foo = koda;
  205. }
  206.  
  207. Can do any of these assignments based on composition of o.
  208.  
  209. o.foo = koda;
  210. o.foo = o.koda;
  211. foo = koda;
  212. foo = o.koda;
  213.  
  214. What it actually does...
  215.  
  216. ​if ('foo' in o) {
  217. o.foo = 'koda' in o ? o.koda : koda;
  218. }​ else {
  219. foo = 'koda' in o ? o.koda : koda;
  220. }​​
  221.  
  222. Using with as a proxy...
  223.  
  224. var docWrapped = {
  225. foo: 5
  226. };
  227.  
  228. with ({document: docWrapped}) {
  229. window.document.write(document.foo); // 5
  230. }​
  231.  
  232.  
  233.  
  234.  
  235.  
  236.  
  237.  
  238. Apply form
  239.  
  240. functionObject.apply(thisObject, arguments);
  241. functionObject.call(thisObject, argument);
  242.  
  243. - A function's apply or call method allows for calling the function, explicitly specifying thisObject.
  244. - It can also take an array of parameters or a sequence of parameters.
  245.  
  246. Function.prototype.call = function (thisObject) {
  247. return this.apply(thisObject, Array.prototype.slice.apply(arguments, [1]));
  248. };
  249.  
  250.  
  251. this
  252. - this is a bonus parameter. It's value depends on the calling form.
  253. - this gives methods access to their objects.
  254. - this is bound at invocation time.
  255.  
  256. Invocation form | this
  257. ---------------------------------------------------------------
  258. function | the global object (ES3) - undefined (ES5 Strict)
  259. method | the object
  260. constructor | the new object
  261. apply | argument
  262.  
  263.  
  264. Closure
  265. - A function that "closes over" some local variables is called a closure.
  266. - Doesn't reallocate memory and array every time function is called.
  267. - Still has the advantage of referencing its local variables.
  268. - The function that returns names[n] is assigned to digit_name.
  269. - This is one of the most important features of JavaScript and makes it one of the most brilliant programming languages in the world.
  270.  
  271. var digit_name = (function(){
  272. var names = ['zero','one','two','three','four','five','six','seven','eight','nine'];
  273.  
  274. return function(n){
  275. return names[n];
  276. };
  277. }());
  278.  
  279. alert(digit_name(3)); // three
  280.  
  281.  
  282. Closure Conditional
  283. - Can't tell you which case is optimal in which scenario but it is another option.
  284.  
  285. var digit_name = (function(){
  286. var names;
  287.  
  288. return function(n){
  289. if (!names) {
  290. names = ['zero','one','two','three','four','five','six','seven','eight','nine'];
  291. }
  292. return names[n];
  293. };
  294. }());
  295.  
  296.  
  297. later method
  298.  
  299. if (typeof Object.prototype.later !== 'function') {
  300. Object.prototype.later = function (msec, method) {
  301. var that = this, args = Array.prototype.slice.apply(arguments, [2]);
  302. if (typeof method === 'string') {
  303. method = that[method];
  304. }
  305. setTimeout(function(){
  306. method.apply(that, args);
  307. }, msec);
  308. return that; // Cascade
  309. };
  310. }
  311.  
  312.  
  313. curry method
  314. - arguments inherits array methods (ES5).
  315. - Since arguments isn't an array calling the slice method returns an array.
  316.  
  317. function curry(func) {
  318. var args = arguments.slice(1);
  319. return function(){
  320. return func.apply(null, args.concat(arguments.slice()));
  321. };
  322. }
  323.  
  324. var inc = curry(function add(a,b) {
  325. return a + b;
  326. }, 1);
  327.  
  328. console.log(inc(6)); // 7
  329.  
  330.  
  331. try-catch
  332. - If you want to ignore an exception don't name the variable e.
  333.  
  334. try {
  335. func(args);
  336. } catch (ignore) {}
  337.  
  338.  
  339. Sealer/Unsealer
  340. - Useful for if you have a package and you don't want the mailman to see the package.
  341.  
  342. function make_sealer() {
  343. var boxes = [], values = [];
  344.  
  345. return {
  346. sealer: function (value) {
  347. var i = boxes.length, box = {};
  348. boxes[i] = box;
  349. values[i] = value;
  350. return box;
  351. },
  352. unsealer: function (box) {
  353. return values[boxes.indexOf(box)];
  354. }
  355. };
  356. }
  357.  
  358.  
  359. Pseudoclassical Inheritance
  360.  
  361. function Gizmo(id) {
  362. this.id = id;
  363. }
  364. Gizmo.prototype.toString = function(){
  365. return "gizmo " + this.id;
  366. };
  367.  
  368. function Hoozit(id) {
  369. this.id = id;
  370. }
  371. Hoozit.prototype = new Gizmo(); // Inherit from Gizmo
  372. Hoozit.prototype.test = function(id){
  373. return this.id === id;
  374. };
  375.  
  376.  
  377. Prototypal Inheritance
  378.  
  379. var gizmo = new_constructor(Object, function(id){
  380. this.id = id;
  381. }, {
  382. toString: function(){
  383. return "gizmo " + this.id;
  384. }
  385. });
  386.  
  387. var hoozit = new_constructor(gizmo, function(id){
  388. this.id = id;
  389. }, {
  390. test: function(id){
  391. return this.id === id;
  392. }
  393. });
  394.  
  395.  
  396. New Constructor
  397.  
  398. function new_constructor(extend, initializer, methods) {
  399. var func, prototype = Object.create(extend && extend.prototype);
  400. if (methods) {
  401. methods.keys().forEach(function(key){
  402. prototype[key] = methods[keys];
  403. });
  404. }
  405. func = function(){
  406. var that = Object.create(prototype);
  407. if (typeof initializer === 'function') {
  408. initializer.apply(that, arguments);
  409. }
  410. return that;
  411. };
  412. func.prototype = prototype;
  413. prototype.constructor = func;
  414. return func;
  415. }
  416.  
  417.  
  418. Functional Inheritance
  419.  
  420. function gizmo(id){
  421. return {
  422. id: id,
  423. toString: function(){
  424. return "gizmo " + this.id;
  425. }
  426. };
  427. }
  428.  
  429. function hoozit(id){
  430. var that = gizmo(id);
  431. that.test = function(testid){
  432. return testid === this.id;
  433. };
  434. return that;
  435. }
  436.  
  437.  
  438. Functional Inheritance (Privacy)
  439. - Referring directly to id parameter
  440.  
  441. function gizmo(id){
  442. return {
  443. toString: function(){
  444. return "gizmo " + id;
  445. }
  446. };
  447. }
  448.  
  449. function hoozit(id){
  450. var that = gizmo(id);
  451. that.test = function(testid){
  452. return testid === id;
  453. };
  454. return that;
  455. }
  456.  
  457.  
  458. Shared Secrets
  459. - Generally unnecessary but nice knowing it's possible
  460.  
  461. function gizmo(id, secret){
  462. secret = secret || {};
  463. secret.id = id;
  464. return {
  465. toString: function(){
  466. return "gizmo " + secret.id;
  467. }
  468. };
  469. }
  470.  
  471. function hoozit(id){
  472. var secret = {};
  473. var that = gizmo(id, secret);
  474. that.test = function(testid){
  475. return testid === secret.id;
  476. };
  477. return that;
  478. }
  479.  
  480.  
  481. Super Methods
  482. - If you find yourself needing super methods you should take a step back there might be a simpler way to think about it.
  483.  
  484. function hoozit(id){
  485. var secret = {};
  486. var that = gizmo(id, secret);
  487. var super_toString = that.toString;
  488. that.test = function(testid){
  489. return testid === secret.id;
  490. };
  491. that.toString = function(){
  492. return super_toString.apply(that);
  493. };
  494. return that;
  495. }
  496.  
  497.  
  498. Event handlers in a loop
  499.  
  500. // WRONG
  501. - They all alert the same value because div_id keeps changing.
  502. - They all alert the id of the div that ended the loop.
  503.  
  504. for (i ...) {
  505. div_id = divs[i].id;
  506. divs[i].onclick = function(){
  507. alert(div_id);
  508. };
  509. }
  510.  
  511. // CORRECT
  512. - Avoids a function within a loop
  513.  
  514. function make_handler(div_id){
  515. return function(){
  516. alert(div_id);
  517. }
  518. }
  519. for (i ...) {
  520. div_id = divs[i].id;
  521. divs[i].onclick = make_handler(div_id);
  522. }
  523.  
  524.  
  525. Tennent's Principle of Correspondence
  526. - Would JavaScript still be a useful language without variables? YES
  527. - Shows that anything you can write with variables you can write with function enclosures instead to do the same thing.
  528.  
  529. function factorial(n){ // result: variable
  530. var result = 1;
  531. while (n > 1) {
  532. result *= n;
  533. n -= 1;
  534. }
  535. return result;
  536. }
  537.  
  538. function factorial(n){ // result: parameter
  539. return (function(result){
  540. while (n > 1) {
  541. result *= n;
  542. n -= 1;
  543. }
  544. return result;
  545. }(1));
  546. }
  547.  
  548.  
  549. The Y Combinator
  550. - Suppose we had a language without variables, assignment, and named functions could we still do recursion? YES
  551. - Incredibly nested functions within functions.
  552.  
  553. function y(le){
  554. return (function(f){
  555. return f(f);
  556. }(function(f){
  557. return le(function(x){
  558. return f(f)(x);
  559. });
  560. }));
  561. }
  562.  
  563. var factorial = y(function(fac){
  564. return function(n){
  565. return n <= 2 ? n : n * fac(n - 1);
  566. };
  567. });
  568.  
  569. var number120 = factorial(f);
  570.  
  571.  
  572. Prototype
  573.  
  574. A prototype is an object from which other objects inherit properties. Any object can be a prototype and every object has a prototype by default. Since prototypes are themselves objects, every prototype has a prototype too. There is only one exception, the default object prototype at the top of every prototype chain.
  575.  
  576. An object in JavaScript is any unordered collection of key-value pairs. If it’s not a primitive (undefined, null, boolean, number or string) it’s an object.
  577.  
  578. The true prototype of an object is held by the internal [[Prototype]] property. ECMA 5 introduces the standard accessor Object.getPrototypeOf(object) which to-date is implemented in Firefox, Safari, Chrome and IE9. In addition all browsers except IE support the non-standard accessor __proto__. Failing that we can ask the object’s constructor for its prototype property.
  579.  
  580. When a primitive is asked for it’s prototype it will be coerced to an object.
  581.  
  582. // (works in IE<=8 too, due to double-negative)
  583. false.__proto__ === Boolean(false).__proto__; // true
  584.  
  585. It rarely makes sense to set a prototype for one instance and only one instance, since it would be equally efficient just to add properties directly to the instance itself.
  586.  
  587. // fails in IE<=8
  588. var a = {};
  589. a.__proto__ = Array.prototype;
  590. a.length; // 0
  591.  
  592. JavaScript makes no distinction between constructors and other functions, so every function gets a prototype property. Conversely, anything that is not a function does not have such a property.
  593.  
  594. // Function will never be a constructor but it has a prototype property anyway
  595. Math.max.prototype; // [object Object]
  596.  
  597. // Function intended to be a constructor has a prototype too
  598. var A = function(name){
  599. this.name = name;
  600. };
  601. A.prototype; // [object Object]
  602.  
  603. // Math is not a function so no prototype property
  604. Math.prototype; // null
  605.  
  606. A function’s prototype property is the object that will be assigned as the prototype to all instances created when this function is used as a constructor. It’s important to understand that a function’s prototype property has nothing to do with it’s actual prototype.
  607.  
  608. // (example fails in IE)
  609. var A = function(name){
  610. this.name = name;
  611. };
  612.  
  613. A.prototype == A.__proto__; // false
  614. A.__proto__ == Function.prototype; // true - A's prototype is set to its constructor's prototype property
  615.  
  616. Constructor. this is returned as new object and its internal [[prototype]] property will be set to the constructor's default prototype property.
  617.  
  618. var Circle = function(radius){
  619. this.radius = radius;
  620. // next line is implicit, added for illustration only
  621. // this.__proto__ = Circle.prototype;
  622. };
  623.  
  624. Augment Circle's default prototype property thereby augmenting the prototype of each generated instance.
  625.  
  626. Circle.prototype.area = function(){
  627. return Math.PI*this.radius*this.radius;
  628. }
  629.  
  630. // Create two instances of a circle and make each leverage the common prototype.
  631. var a = new Circle(3), b = new Circle(4);
  632. a.area().toFixed(2); // 28.27
  633. b.area().toFixed(2); // 50.27
  634.  
  635. If I modify the existing prototype’s property then this is true, because a.__proto__ is a reference to the object defined by A.prototype at the time it was created.
  636.  
  637. var A = function(name) {
  638. this.name = name;
  639. };
  640.  
  641. var a = new A('alpha');
  642. a.name; // alpha
  643. A.prototype.x = 23;
  644. a.x; // 23
  645.  
  646. But if I replace the prototype property with a new object, a.__proto__ still references the original object.
  647.  
  648. var A = function(name) {
  649. this.name = name;
  650. };
  651.  
  652. var a = new A('alpha');
  653. a.name; // alpha
  654. A.prototype = {x:23};
  655. a.x; // null
  656.  
  657. The default prototype looks like an object with one property, the constructor.
  658.  
  659. var A = function(){};
  660. A.prototype.constructor == A; // true
  661. var a = new A();
  662. a.constructor == A; // true (a's constructor property inherited from it's prototype)
  663. A.prototype.constructor == a.constructor; // true
  664.  
  665. The expression a instanceof A will answer true if a’s prototype falls within the same prototype chain as A’s prototype property. This means we can trick instanceof into failing.
  666.  
  667. var A = function(){};
  668.  
  669. var a = new A();
  670. a.__proto__ == A.prototype; // true - so instanceof A will return true
  671. a instanceof A; // true
  672.  
  673. // Mess around with a's prototype
  674. a.__proto__ = Function.prototype;
  675.  
  676. // a's prototype no longer in same prototype chain as A's prototype property
  677. a instanceof A; // false
  678.  
  679. Every constructor has a prototype property which it uses to assign prototypes to all instances it generates. This also applies to native constructors too such as Function and String. By extending (not replacing!) this property we get to update the prototype of every instance of the given type.
  680.  
  681. String.prototype.times = function(count){
  682. return count < 1 ? '' : new Array(count + 1).join(this);
  683. };
  684.  
  685. "hello!".times(3); // "hello!hello!hello!";
  686. "please...".times(6); // "please...please...please...please...please...please..."
  687.  
  688. Since every object and every prototype (bar one) has a prototype, we can think of a succession of objects linked together to form a prototype chain. The end of the chain is always the default object’s prototype.
  689.  
  690. a.__proto__ = b;
  691. b.__proto__ = c;
  692. c.__proto__ = {}; // default object
  693. {}.__proto__.__proto__; // null
  694.  
  695. The prototypical inheritance mechanism is internal and non-explicit. When object a is asked to evaluate property foo, JavaScript walks the prototype chain (starting with object a itself), checking each link in the chain for the presence of property foo. If and when foo is found it is returned, otherwise undefined is returned.
  696.  
  697. Prototypical inheritance is not a player when property values are set. a.foo = 'bar' will always be assigned directly to the foo property of a. To assign a property to a prototype you need to address the prototype directly. You can add a property to a previously defined object type by using the prototype property. This defines a property that is shared by all objects of the specified type, rather than by just one instance of the object.
  698.  
  699. prototype is a property of a Function object. It is the prototype of objects constructed by that function.
  700.  
  701. __proto__ is internal property of an object, pointing to its prototype. By the standard, this property should not be accessible for the programmer (although it in fact is in Firefox).
  702.  
  703. So, suppose the following code creating a "point" object.
  704.  
  705. function Point(x, y) {
  706. this.x = x;
  707. this.y = y;
  708. }
  709.  
  710. var myPoint = new Point();
  711.  
  712. Now, Point is a function (a constructor), so it is an object of the type Function. myPoint is an object constructed by the constructor Point(). Therefore, myPoint.__proto__ and Point.prototype are the same object.
  713.  
  714. __proto__ is the actual object that is used in the lookup chain to resolve methods, etc. prototype is the object that is used to build __proto__ when you create an object with new:
  715.  
  716. ( new Foo ).__proto__ === Foo.prototype
  717. ( new Foo ).prototype === undefined
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement