Last active
October 9, 2019 13:58
-
-
Save thamsrang/ceda5f22af8923acb9a2e7d831b6a5be to your computer and use it in GitHub Desktop.
JavaScript code tips and tricks. JavaScript Unknown Facts. JavaScript Deep learning. JavaScript Object Conversion. JavaScript Type Conversion. #js #depth
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /**** | |
| * READ FIRST: https://www.freecodecamp.org/news/js-type-coercion-explained-27ba3d9a2839/ | |
| * READ SECOND: https://github.com/denysdovhan/wtfjs | |
| */ | |
| // Coercion is the term that is used for unexpected type casting in JavaScript. | |
| // https://dmitripavlutin.com/javascriptss-addition-operator-demystified/ | |
| // http://2ality.com/2012/01/object-plus-object.html | |
| // https://dorey.github.io/JavaScript-Equality-Table/ | |
| // https://wtfjs.com/ | |
| // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Equality_comparisons_and_sameness | |
| // http://getify.github.io/coercions-grid/ | |
| // https://delapouite.com/ramblings/javascript-coercion-rules.html | |
| // https://g00glen00b.be/javascript-coercion/ | |
| // https://blog.usejournal.com/little-known-features-of-javascript-901665291387 | |
| var a = Object['length']; | |
| console.log( a ); // Prints 1 | |
| var b = {1}['length']; | |
| console.log( b ); // Prints ['length'] | |
| var c = {}.1; | |
| console.log( c ); //Prints 0.1 | |
| var d = []+{}; | |
| console.log( d ); //Prints "[object Object]" | |
| var e = {}"str"; | |
| console.log( e ); // Prints "str" | |
| var f = {}1; | |
| console.log(f); // Prints 1 | |
| var g = 1+true; | |
| console.log(g); // Prints 2. true is converted to 1. | |
| var h = 1+false; | |
| console.log(f); // Prints 1. false is converted to 0. | |
| console.log("" + 1 + 0); // Prints 10 | |
| console.log("" - 1 + 0); // Prints -1 | |
| console.log(true + false); // Prints 1 | |
| console.log(6 / "3"); // Prints 2 | |
| console.log("2" * "3"); // Prints 6 | |
| console.log(4 + 5 + "px"); // Prints 9px | |
| console.log("$" + 4 + 5); // Prints $45 | |
| console.log("4" - 2); // Prints 2 | |
| console.log("4px" - 2); // Prints NaN | |
| console.log(7 / 0); // Prints Infinity | |
| console.log(" -9\n" + 5); // Prints -9 | |
| 5 && 2 = 2 | |
| 2 && 5 = 5 | |
| 5 || 0 = 5 | |
| 0 || 5 = 5 | |
| null + 1 = 1 // (3) | |
| undefined + 1 = NaN // (4) | |
| null == "\n0\n" = false // (5) | |
| +null == +"\n0\n" = true // (6) | |
| // 5 | |
| console.log(" -9\n" - 5); // Prints -14 | |
| console.log(null + 1); // Prints 1 | |
| console.log(undefined + 1); // Prints NaN | |
| console.log( Boolean("0") ); // Prints true | |
| console.log( Boolean(" ") ); // Prints true (any non-empty string is true) | |
| /** Date **/ | |
| console.log(1*(new Date)); // Prints Timestamp - 1529568542851 | |
| console.log(new Date-0); // Prints Timestamp - 1529568542851 | |
| console.log(+new Date); // Prints Timestamp - 1529568542851 | |
| console.log([] + []); // JavaScript will give you "" (which makes little sense) | |
| console.log({} + []); // JS : 0 | |
| console.log([] + {}); // JS : "[object Object]" | |
| console.log({} + {}); // JS : NaN or [object Object][object Object] depending upon browser | |
| console.log("hello" - 1); // JS : NaN | |
| var str = "constructor"; | |
| str[str]("01"); // Prints "01" | |
| // Source : https://medium.freecodecamp.org/js-type-coercion-explained-27ba3d9a2839 | |
| true + false // 1 | |
| 12 / "6" // 2 | |
| "number" + 15 + 3 // 'number153' | |
| 15 + 3 + "number" // '18number' | |
| [1] > null // true | |
| "foo" + + "bar" // 'fooNaN' | |
| 'true' == true // false | |
| false == 'false' // false | |
| null == '' // false | |
| !!"false" == !!"true" // true | |
| ['x'] == 'x' // true | |
| [] + null + 1 // 'null1' | |
| [1,2,3] == [1,2,3] // false | |
| {}+[]+{}+[1] // '0[object Object]1' | |
| !+[]+[]+![] // 'truefalse' | |
| new Date(0) - 0 // 0 | |
| new Date(0) + 0 // 'Thu Jan 01 1970 02:00:00(EET)0' | |
| Math.max(); // Prints -Infinity | |
| Math.min(); // Prints Infinity | |
| // Credit: https://github.com/brianleroux/wtfjs | |
| console.log(111111111111111111111); // Prints 111111111111111110000 | |
| ("foo" + + "bar") === "fooNaN" // Prints true. Evaluted as “foo” + (+ “bar”), which converts “bar” to not a number. | |
| 0.1 + 0.2 === 0.3; // Prints false. Result of 0.1 + 0.2 is 0.30000000000000004 | |
| Number.MIN_VALUE > 0; // Prints true. Result of Number.MIN_VALUE is 5e-324 | |
| typeof NaN === "number"; // Prints true. Because NaN.__proto__ is Number | |
| NaN === NaN; //Prints false. | |
| typeof null === "object"; // Prints true. | |
| null instanceof Object; //Prints false. Classic null is not an Object. | |
| parseInt('06'); // Parse to 6 | |
| parseInt('08'); // Parse to 0 in old browser. In Chrome66 works fine. This is because parseInt accepts a second argument for radix. If it is not supplied and the string starts with a 0 it will be parsed as an octal number. | |
| "string" instanceof String; //prints false. | |
| String("string") instanceof String; // Prints false. | |
| new String("string") instanceof String; //Prints true. | |
| (function(){ | |
| var x = y = 1; | |
| })(); | |
| console.log(x); // undefined | |
| console.log(y); // 1 -- oops, auto-global! | |
| // It’s treated like: var x = (y = 1); thus, “y=1” creates an auto-global since there’s no binding “var” statement for it. Afterwards, that value gets copied into the properly defined local var “x”. | |
| "222" - -"111" // 333. Evaluted as "222"-(-"111"); | |
| (function(){ | |
| console.log(window); // "undefined" | |
| var window = window; | |
| console.log(window); // "undefined" | |
| })(); | |
| // Because of “hoisting”, all variable declarations will be executed immediately at the top of a function scope. However, the variable initializations are not hoisted. So, local variable “window” is declared but uninitialized/”undefined”. | |
| Object.prototype.foo = 10; | |
| console.log(foo); //Prints 10 | |
| var bignum = 1e300; // 1e300 | |
| var a = 1e400; //Infinity | |
| console.log(a - bignum); //Infinity | |
| setTimeout(function(rand){ console.log(rand); },10); // FF passes a "magic" param we call "rand". I got 99999 | |
| for (var i=0; i<100000; i++) { i; } // take some time | |
| // For Firefox only, any function executed by a setTimeout or setInterval invocation will get passed to it (whether you want it to or not) a mysterious “lateness” variable, which represents the number of milliseconds late the function is in executing. Sucks because it can clobber an intentionally unpassed “default” variable to your function. Sucks even more if this variable is intended to be boolean, because you end up with ”random” true/false’y values. | |
| (true + 1) === 2; // true | |
| (true + true) === 2; // true | |
| true === 2; // false | |
| true === 1; // false | |
| var a = {}; | |
| a.b === undefined; // true because property b is not set | |
| undefined = 42; | |
| a.b === undefined; // false. In JavaScript, undefined is nothing but a global variable name without a default value. Therefore, its primitive value is undefined. You can change the value of undefined. undefined is mutable. | |
| function laugh() | |
| { | |
| return | |
| { | |
| haha: "ha!" | |
| }; | |
| } | |
| laugh(); // returns undefined | |
| // note the number to the left of the 'e', 7 and 8 respectively | |
| console.log( 1.7976931348623157e+308 === 1.7976931348623158e+308 ); // true! | |
| var enum; // Uncaught SyntaxError: Unexpected reserved word | |
| //---------------------------------------------------------------------------------- | |
| // arguments hacking | |
| (function(a,b,c) { | |
| console.log(a,b,c); // one two three | |
| console.log(arguments[0], arguments[1], arguments[2]); // one two three | |
| arguments[0] = "uno"; | |
| console.log(a,b,c); // uno two three | |
| console.log(arguments[0], arguments[1], arguments[2]); // uno two three | |
| // a is magically married to arguments[0], b to arguments[1] etc | |
| // so one could change locally scoped variables from another scope like so: | |
| changeArguments(arguments); | |
| console.log(a,b,c); // I messed up your dataz | |
| })("one", "two", "three"); | |
| // not cool. | |
| function changeArguments(args){ | |
| args[0] = "I messed" | |
| args[1] = "up your"; | |
| args[2] = "dataz"; | |
| } | |
| // A more real world example: | |
| (function(arg1, arg2) { | |
| console.log(arg1, arg2); //-> uno dos | |
| Array.prototype.shift.apply(arguments); | |
| console.log(arg1, arg2); //-> dos dos | |
| console.log(arguments); ["dos"] | |
| })('uno', 'dos'); | |
| //-------------------------------------------------------------------------------------- | |
| "0" && {} // true | |
| 0 && {} // false | |
| 0 == "0" // true | |
| var a = 012; // 10 is assigned to a. Integer Octet conversion. | |
| var a = 8; | |
| var someFunc = function(){ | |
| console.log("'a value is ", a, "'"); | |
| var a = 8; | |
| }; | |
| someFunc(); // Prints 'a value is undefined'. Variable 'a' is Hoisted. | |
| 3 > 2 > 1 // false. Remember how true sometimes has a value so in the above 3 > 2 evaluates to true making the second part of the expression evaluate true > 1 which is false. | |
| (1) === 1; // true | |
| Number.prototype.isOne = function () { return this === 1; } | |
| (1).isOne(); // false! | |
| Number.prototype.reallyIsOne = function () { return this - 1 === 0; } | |
| (1).reallyIsOne(); // true | |
| (function(){return 2*3;}).toString() === (function(){return 6;}).toString(); // false. (function(){return 2*3;}).toString() is "function(){return 2*3;}"; (function(){return 6;}).toString(); is "function(){return 6;}"; | |
| window.window == window // true | |
| window.window.window == window // true | |
| window.window.window.window == window //true | |
| [] == 0 // true | |
| +[] === 0 // true | |
| ++[] === 1 // Uncaught ReferenceError: Invalid left-hand side expression in prefix operation | |
| [[]][0] === []; // false | |
| ++[[]][0] === 1; // true | |
| ++[[]][+[]] === 1 // true | |
| Number.MAX_VALUE*1.0000000000000001 === (1/0) // false | |
| Number.MAX_VALUE*1.0000000000000002 === (1/0) // true | |
| var foo = { | |
| toString: function () { | |
| return 5; | |
| }, | |
| valueOf: function () { | |
| return "foo"; | |
| } | |
| }; | |
| alert(foo.toString() + 1); // 6 (bad!) | |
| alert(foo + 1); // "foo1" (no good!) | |
| alert(+foo); // NaN (the worst!) | |
| alert.call.call.call.call.call.apply(function (a) {return a}, ["1","2"]); // Prints 2 | |
| alert.apply.apply.call.call(function (b) {return b}, {a:1},{b:2},{c:3} ) // Prints {b:2} | |
| Function.prototype.call.apply(function (a) {return a}, [1,2]); // Prints 2 | |
| //Example | |
| function logThisAndArgs() { | |
| console.log(this, arguments); | |
| }; | |
| Function.prototype.call.apply(logThisAndArgs, [{'some':'object'},1,2,3,4]) | |
| // Prints >{ some:"object"} >Arguments(4)[1, 2, 3, 4, callee:f] | |
| // Check it now | |
| Function.prototype.call.call(logThisAndArgs, {'some':'object'},1,2,3,4) | |
| // Prints >{ some:"object"} >Arguments(4)[1, 2, 3, 4, callee:f] | |
| 1 + + 1 // => 2 | |
| 1 + - + 1 // => 0 | |
| 1 + - + - + 1 // => 2 | |
| 1 + - + - + - + 1 // => 0 | |
| 1 + - + + + - + 1 // => 2 | |
| 1 + / + + + / + 1 // => '1/ + + + /1' | |
| isNaN( null ); // false | |
| null === NaN; // false | |
| null == NaN; // false | |
| // Then why | |
| Number( null ); // 0 | |
| // When you create instances of String or Number, they take the default value ("" for strings and 0 for numbers). This is not the same for Object and Array. | |
| var a = new Number; | |
| a == 0 // true | |
| var a = new String; | |
| a == "" // true | |
| var a = new Object; | |
| a == {} // false | |
| var a = new Array; | |
| a == [] // false | |
| // This is even more confusing when using the JSON-style syntax to create objects and arrays. | |
| var a = {}; | |
| a == {} // false | |
| var a = []; | |
| a == [] // false | |
| var a = {}; | |
| var b = {key:2}; | |
| var c = {key:3}; | |
| a[b]=123; | |
| a[c]=456; | |
| console.log(a[b]); // Prints 456. a is {"[object Object]":456}. | |
| console.log((!+[]+[]+![]).length); //Prints 9. | |
| console.log((!+[]+[]+![])); //Prints "truefalse". | |
| var Z = "constructor"; | |
| Z[Z][Z]("alert('wtfjs!')")(); // alerts wtfjs! | |
| /** | |
| Z[Z]; // function String() { [native code] } | |
| Z[Z][Z]; // function Function() { [native code] } | |
| */ | |
| //----------------------------------------------------------------------------------------------------------------------- | |
| // How do you determine if a number is an integer in JavaScript? | |
| x = 1; | |
| x === Math.floor(x); // returns true | |
| // But what happens if we try to add a method for this to the Number prototype? | |
| Number.prototype.isInteger = function() { | |
| return this === Math.floor(this); | |
| } | |
| x = 1; | |
| x.isInteger(); // returns false! | |
| // Why? It turns out that when you add methods to Number, the type of the number inside the method becomes "object" rather than "number", but Math.floor returns a result of type "number". If you use the === operator, the two values are no longer equal because they're different types. So the method can be fixed two ways. | |
| // Solution 1 is to avoid comparing types: | |
| Number.prototype.isInteger = function() { | |
| return this == Math.floor(this); // works but breaks if you care about 0 vs other falsy values | |
| } | |
| // Solution 2 is better; cast "this" to the Number type and then the types are equal. | |
| Number.prototype.isInteger = function() { | |
| return Number(this) === Math.floor(this); | |
| } | |
| //----------------------------------------------------------------------------------------------------------------------- | |
| // https://j11y.io/javascript/another-javascript-quiz/#comment-28938 | |
| // 2010-07-12-fail.md | |
| // Let's take the last post a step further: | |
| console.log( (![]+[])[+[]]+(![]+[])[+!+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]] ) // "fail" | |
| // Breaking that mass of symbols into pieces we notices, that the following patten occurs often: | |
| console.log( (![]+[]) ) // "false" | |
| console.log( ![] ) // false | |
| // So we try adding [] to false. But through a number of internal function calls( binary + Operator -> ToPrimitive -> [[DefaultValue]]) we end up with converting the right operand to a String: | |
| console.log( [].toString() ) // "" | |
| console.log( (![]+[].toString()) ) // false + "" | |
| // Aha, so we are concatenating strings here! Now this is plain obvious! Moving on to the next bit: | |
| console.log( [+[]] ) // [ 0] | |
| console.log( +[] ) // 0 | |
| // Thinking of a String as an Array we can access its first character via [0]. So "false"[0] returns "f". | |
| //----------------------------------------------------------------------------------------------------------------------- | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment