File: README.md
What the f\*ck JavaScript?
A list of funny and tricky JavaScript examples
JavaScript is a great language. It has a simple syntax, large ecosystem and, what is most important, a great community.
At the same time, we all know that JavaScript is quite a funny language with tricky parts. Some of them can quickly turn our everyday job into hell, and some of them can make us laugh out loud.
The original idea for WTFJS belongs to Brian Leroux. This list is highly inspired by his talk “WTFJS” at dotJS 2012:
Node Packaged Manuscript
You can install this handbook using npm. Just run:
$ npm install wtfjsYou should be able to run wtfjs at the command line now. This will open the manual in your selected $PAGER. Otherwise, you may continue reading on here.
The source is available here: https://github.com/denysdovhan/wtfjs
Translations
Currently, there are these translations of wtfjs:
Help translating to your language
Note: Translations are maintained by their translators. They may not contain every example, and existing examples may be outdated.
Table of Contents
- 💪🏻 Motivation
- ✍🏻 Notation
- 👀 Examples
- [
[]is equal ` - [
trueis not equal ` - true is false
- baNaNa
NaNis not aNaNObject.is()and===weird cases- It's a fail
[]is truthy, but nottruenullis falsy, but notfalsedocument.allis an object, but it is undefined- Minimal value is greater than zero
- function is not a function
- Adding arrays
- Trailing commas in array
- Array equality is a monster
undefinedandNumberparseIntis a bad guy- Math with
trueandfalse - HTML comments are valid in JavaScript
NaNisnota number[]andnullare objects- Magically increasing numbers
- Precision of
0.1 + 0.2 - Patching numbers
- Comparison of three numbers
- Funny math
- Addition of RegExps
- Strings aren't instances of
String - Calling functions with backticks
- Call call call
- A
constructorproperty - Object as a key of object's property
- Accessing prototypes with
__proto__ `${{Object}}`- Destructuring with default values
- Dots and spreading
- Labels
- Nested labels
- Insidious
try..catch - Is this multiple inheritance?
- A generator which yields itself
- A class of class
- Non-coercible objects
- Tricky arrow functions
- Arrow functions can not be a constructor
argumentsand arrow functions- Tricky return
- Chaining assignments on object
- Accessing object properties with arrays
Number.toFixed()display different numbersMath.max()less thanMath.min()- Comparing
nullto0 - Same variable redeclaration
- Default behavior Array.prototype.sort()
- resolve() won't return Promise instance
{}{}is undefinedargumentsbinding- An
alertfrom hell - An infinite timeout
- A
setTimeoutobject - Double dot
- Extra Newness
- Why you should use semicolons
- Split a string by a space
- A stringified string
- Non-strict comparison of a number to
true
- [
- 📚 Other resources
- 🤝 Supporting
- 🎓 License
💪🏻 Motivation
Just for fun
— “Just for Fun: The Story of an Accidental Revolutionary”, Linus Torvalds
The primary goal of this list is to collect some crazy examples and explain how they work, if possible. Just because it's fun to learn something that we didn't know before.
If you are a beginner, you can use these notes to get a deeper dive into JavaScript. I hope these notes will motivate you to spend more time reading the specification.
If you are a professional developer, you can consider these examples as a great reference for all of the quirks and unexpected edges of our beloved JavaScript.
In any case, just read this. You're probably going to find something new.
⚠️ Note: If you enjoy reading this document, please, consider supporting the author of this collection.
✍🏻 Notation
// -> is used to show the result of an expression. For example:
1 + 1; // -> 2// > means the result of console.log or another output. For example:
console.log("hello, world!"); // > hello, world!// is just a comment used for explanations. Example:
// Assigning a function to foo constant
const foo = function() {};👀 Examples
`[]` is equal `![]`
Array is equal not array:
[] == ![]; // -> true💡 Explanation:
The abstract equality operator converts both sides to numbers to compare them, and both sides become the number 0 for different reasons. Arrays are truthy, so on the right, the opposite of a truthy value is false, which is then coerced to 0. On the left, however, an empty array is coerced to a number without becoming a boolean first, and empty arrays are coerced to 0, despite being truthy.
Here is how this expression simplifies:
+[] == +![];
0 == +false;
0 == 0;
true;See also [] is truthy, but not true.
`true` is not equal `![]`, but not equal `[]` too
Array is not equal true, but not Array is not equal true too;
Array is equal false, not Array is equal false too:
true == []; // -> false
true == ![]; // -> false
false == []; // -> true
false == ![]; // -> true💡 Explanation:
true == []; // -> false
true == ![]; // -> false
// According to the specification
true == []; // -> false
toNumber(true); // -> 1
toNumber([]); // -> 0
1 == 0; // -> false
true == ![]; // -> false
![]; // -> false
true == false; // -> falsefalse == []; // -> true
false == ![]; // -> true
// According to the specification
false == []; // -> true
toNumber(false); // -> 0
toNumber([]); // -> 0
0 == 0; // -> true
false == ![]; // -> true
![]; // -> false
false == false; // -> truetrue is false
!!"false" == !!"true"; // -> true
!!"false" === !!"true"; // -> true💡 Explanation:
Consider this step-by-step:
// true is 'truthy' and represented by value 1 (number), 'true' in string form is NaN.
true == "true"; // -> false
false == "false"; // -> false
// 'false' is not the empty string, so it's a truthy value
!!"false"; // -> true
!!"true"; // -> truebaNaNa
"b" + "a" + +"a" + "a"; // -> 'baNaNa'This is an old-school joke in JavaScript, but remastered. Here's the original one:
"foo" + +"bar"; // -> 'fooNaN'💡 Explanation:
The expression is evaluated as 'foo' + (+'bar'), which converts 'bar' to not a number.
`NaN` is not a `NaN`
NaN === NaN; // -> false💡 Explanation:
The specification strictly defines the logic behind this behavior:
- If
Type(x)is different fromType(y), return false.- If
Type(x)is Number, then
- If
xis NaN, return false.- If
yis NaN, return false.- … … …
Following the definition of NaN from the IEEE:
Four mutually exclusive relations are possible: less than, equal, greater than, and unordered. The last case arises when at least one operand is NaN. Every NaN shall compare unordered with everything, including itself.
— “What is the rationale for all comparisons returning false for IEEE754 NaN values?” at StackOverflow
`Object.is()` and `===` weird cases
Object.is() determines if two values have the same value or not. It works similar to the === operator but there are a few weird cases:
Object.is(NaN, NaN); // -> true
NaN === NaN; // -> false
Object.is(-0, 0); // -> false
-0 === 0; // -> true
Object.is(NaN, 0 / 0); // -> true
NaN === 0 / 0; // -> false💡 Explanation:
In JavaScript lingo, NaN and NaN are the same value but they're not strictly equal. NaN === NaN being false is apparently due to historical reasons so it would probably be better to accept it as it is.
Similarly, -0 and 0 are strictly equal, but they're not the same value.
For more details about NaN === NaN, see the above case.
It's a fail
You would not believe, but …
(![] + [])[+[]] +
(![] + [])[+!+[]] +
([![]] + [][[]])[+!+[] + [+[]]] +
(![] + [])[!+[] + !+[]];
// -> 'fail'💡 Explanation:
By breaking that mass of symbols into pieces, we notice that the following pattern occurs often:
![] + []; // -> 'false'
![]; // -> falseSo we try adding [] to false. But due to a number of internal function calls (binary + Operator -> ToPrimitive -> [[DefaultValue]]) we end up converting the right operand to a string:
![] + [].toString(); // 'false'Thinking of a string as an array we can access its first character via [0]:
"false"[0]; // -> 'f'The rest is obvious, but the i is tricky. The i in fail is grabbed by generating the string 'falseundefined' and grabbing the element on index ['10'].
More examples:
+![] // -> 0
+!![] // -> 1
!![] // -> true
![] // -> false
[][[]] // -> undefined
+!![] / +![] // -> Infinity
[] + {} // -> "[object Object]"
+{} // -> NaN- Brainfuck beware: JavaScript is after you!
- Writing a sentence without using the Alphabet — generate any phrase using JavaScript
`[]` is truthy, but not `true`
An array is a truthy value, however, it's not equal to true.
!![] // -> true
[] == true // -> false💡 Explanation:
Here are links to the corresponding sections in the ECMA-262 specification:
`null` is falsy, but not `false`
Despite the fact that null is a falsy value, it's not equal to false.
!!null; // -> false
null == false; // -> falseAt the same time, other falsy values, like 0 or '' are equal to false.
0 == false; // -> true
"" == false; // -> true💡 Explanation:
The explanation is the same as for previous example. Here's the corresponding link:
`document.all` is an object, but it is undefined
⚠️ This is part of the Browser API and won't work in a Node.js environment ⚠️
Despite the fact that document.all is an array-like object and it gives access to the DOM nodes in the page, it responds to the typeof function as undefined.
document.all instanceof Object; // -> true
typeof document.all; // -> 'undefined'At the same time, document.all is not equal to undefined.
document.all === undefined; // -> false
document.all === null; // -> falseBut at the same time:
document.all == null; // -> true💡 Explanation:
document.allused to be a way to access DOM elements, in particular with old versions of IE. While it has never been a standard it was broadly used in the old age JS code. When the standard progressed with new APIs (such asdocument.getElementById) this API call became obsolete and the standard committee had to decide what to do with it. Because of its broad use they decided to keep the API but introduce a willful violation of the JavaScript specification.
The reason why it responds tofalsewhen using the Strict Equality Comparison withundefinedwhiletruewhen using the Abstract Equality Comparison is due to the willful violation of the specification that explicitly allows that.— “Obsolete features - document.all” at WhatWG - HTML spec
— “Chapter 4 - ToBoolean - Falsy values” at YDKJS - Types & Grammar
Minimal value is greater than zero
Number.MIN_VALUE is the smallest number, which is greater than zero:
Number.MIN_VALUE > 0; // -> true💡 Explanation:
Number.MIN_VALUEis5e-324, i.e. the smallest positive number that can be represented within float precision, i.e. that's as close as you can get to zero. It defines the best resolution that floats can give you.Now the overall smallest value is
Number.NEGATIVE_INFINITYalthough it's not really numeric in a strict sense.— “Why is
0less thanNumber.MIN_VALUEin JavaScript?” at StackOverflow
function is not a function
⚠️ A bug present in V8 v5.5 or lower (Node.js <=7) ⚠️
All of you know about the annoying undefined is not a function, but what about this?
// Declare a class which extends null
class Foo extends null {}
// -> [Function: Foo]
new Foo() instanceof null;
// > TypeError: function is not a function
// > at … … …💡 Explanation:
This is not a part of the specification. It's just a bug that has now been fixed, so there shouldn't be a problem with it in the future.
Super constructor null of Foo is not a constructor
It's continuation of story with previous bug in modern environment (tested with Chrome 71 and Node.js v11.8.0).
class Foo extends null {}
new Foo() instanceof null;
// > TypeError: Super constructor null of Foo is not a constructor💡 Explanation:
This is not a bug because:
Object.getPrototypeOf(Foo.prototype); // -> nullIf the class has no constructor the call from prototype chain. But in the parent has no constructor. Just in case, I’ll clarify that null is an object:
typeof null === "object";Therefore, you can inherit from it (although in the world of the OOP for such terms would have beaten me). So you can't call the null constructor. If you change this code:
class Foo extends null {
constructor() {
console.log("something");
}
}You see the error:
ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructorAnd if you add super:
class Foo extends null {
constructor() {
console.log(111);
super();
}
}JS throws an error:
TypeError: Super constructor null of Foo is not a constructorAdding arrays
What if you try to add two arrays?
[1, 2, 3] + [4, 5, 6]; // -> '1,2,34,5,6'💡 Explanation:
The concatenation happens. Step-by-step, it looks like this:
[1, 2, 3] +
[4, 5, 6][
// call toString()
(1, 2, 3)
].toString() +
[4, 5, 6].toString();
// concatenation
"1,2,3" + "4,5,6";
// ->
("1,2,34,5,6");Trailing commas in array
You've created an array with 4 empty elements. Despite all, you'll get an array with three elements, because of trailing commas:
let a = [, , ,];
a.length; // -> 3
a.toString(); // -> ',,'💡 Explanation:
Trailing commas (sometimes called "final commas") can be useful when adding new elements, parameters, or properties to JavaScript code. If you want to add a new property, you can simply add a new line without modifying the previously last line if that line already uses a trailing comma. This makes version-control diffs cleaner and editing code might be less troublesome.
— Trailing commas at MDN
Array equality is a monster
Array equality is a monster in JS, as you can see below:
[] == '' // -> true
[] == 0 // -> true
[''] == '' // -> true
[0] == 0 // -> true
[0] == '' // -> false
[''] == 0 // -> true
[null] == '' // true
[null] == 0 // true
[undefined] == '' // true
[undefined] == 0 // true
[[]] == 0 // true
[[]] == '' // true
[[[[[[]]]]]] == '' // true
[[[[[[]]]]]] == 0 // true
[[[[[[ null ]]]]]] == 0 // true
[[[[[[ null ]]]]]] == '' // true
[[[[[[ undefined ]]]]]] == 0 // true
[[[[[[ undefined ]]]]]] == '' // true💡 Explanation:
You should watch very carefully for the above examples! The behaviour is described in section 7.2.15 Abstract Equality Comparison of the specification.
`undefined` and `Number`
If we don't pass any arguments into the Number constructor, we'll get 0. The value undefined is assigned to formal arguments when there are no actual arguments, so you might expect that Number without arguments takes undefined as a value of its parameter. However, when we pass undefined, we will get NaN.
Number(); // -> 0
Number(undefined); // -> NaN💡 Explanation:
According to the specification:
- If no arguments were passed to this function's invocation, let
nbe+0. - Else, let
nbe ?ToNumber(value). - In case of
undefined,ToNumber(undefined)should returnNaN.
Here's the corresponding section:
`parseInt` is a bad guy
parseInt is famous by its quirks:
parseInt("f*ck"); // -> NaN
parseInt("f*ck", 16); // -> 15💡 Explanation: This happens because parseInt will continue parsing character-by-character until it hits a character it doesn't know. The f in 'f*ck' is the hexadecimal digit 15.
Parsing Infinity to integer is something…
//
parseInt("Infinity", 10); // -> NaN
// ...
parseInt("Infinity", 18); // -> NaN...
parseInt("Infinity", 19); // -> 18
// ...
parseInt("Infinity", 23); // -> 18...
parseInt("Infinity", 24); // -> 151176378
// ...
parseInt("Infinity", 29); // -> 385849803
parseInt("Infinity", 30); // -> 13693557269
// ...
parseInt("Infinity", 34); // -> 28872273981
parseInt("Infinity", 35); // -> 1201203301724
parseInt("Infinity", 36); // -> 1461559270678...
parseInt("Infinity", 37); // -> NaNBe careful with parsing null too:
parseInt(null, 24); // -> 23💡 Explanation:
It's converting
nullto the string"null"and trying to convert it. For radixes 0 through 23, there are no numerals it can convert, so it returns NaN. At 24,"n", the 14th letter, is added to the numeral system. At 31,"u", the 21st letter, is added and the entire string can be decoded. At 37 on there is no longer any valid numeral set that can be generated andNaNis returned.— “parseInt(null, 24) === 23… wait, what?” at StackOverflow
Don't forget about octals:
parseInt("06"); // 6
parseInt("08"); // 8 if support ECMAScript 5
parseInt("08"); // 0 if not support ECMAScript 5💡 Explanation: If the input string begins with "0", radix is eight (octal) or 10 (decimal). Exactly which radix is chosen is implementation-dependent. ECMAScript 5 specifies that 10 (decimal) is used, but not all browsers support this yet. For this reason always specify a radix when using parseInt.
parseInt always convert input to string:
parseInt({ toString: () => 2, valueOf: () => 1 }); // -> 2
Number({ toString: () => 2, valueOf: () => 1 }); // -> 1Be careful while parsing floating point values
parseInt(0.000001); // -> 0
parseInt(0.0000001); // -> 1
parseInt(1 / 1999999); // -> 5💡 Explanation: ParseInt takes a string argument and returns an integer of the specified radix. ParseInt also strips anything after and including the first non-digit in the string parameter. 0.000001 is converted to a string "0.000001" and the parseInt returns 0. When 0.0000001 is converted to a string it is treated as "1e-7" and hence parseInt returns 1. 1/1999999 is interpreted as 5.00000250000125e-7 and parseInt returns 5.
Math with `true` and `false`
Let's do some math:
true + true; // -> 2
(true + true) * (true + true) - true; // -> 3Hmmm… 🤔
💡 Explanation:
We can coerce values to numbers with the Number constructor. It's quite obvious that true will be coerced to 1:
Number(true); // -> 1The unary plus operator attempts to convert its value into a number. It can convert string representations of integers and floats, as well as the non-string values true, false, and null. If it cannot parse a particular value, it will evaluate to NaN. That means we can coerce true to 1 easier:
+true; // -> 1When you're performing addition or multiplication, the ToNumber method is invoked. According to the specification, this method returns:
If
argumentis true, return 1. Ifargumentis false, return +0.
That's why we can add boolean values as regular numbers and get correct results.
Corresponding sections:
HTML comments are valid in JavaScript
You will be impressed, but `
(function() {
return
{
b: 10;
}
})(); // -> undefined💡 Explanation:
return and the returned expression must be in the same line:
(function() {
return {
b: 10
};
})(); // -> { b: 10 }This is because of a concept called Automatic Semicolon Insertion, which automagically inserts semicolons after most newlines. In the first example, there is a semicolon inserted between the return statement and the object literal, so the function returns undefined and the object literal is never evaluated.
Chaining assignments on object
var foo = { n: 1 };
var bar = foo;
foo.x = foo = { n: 2 };
foo.x; // -> undefined
foo; // -> {n: 2}
bar; // -> {n: 1, x: {n: 2}}From right to left, {n: 2} is assigned to foo, and the result of this assignment {n: 2} is assigned to foo.x, that's why bar is {n: 1, x: {n: 2}} as bar is a reference to foo. But why foo.x is undefined while bar.x is not ?
💡 Explanation:
Foo and bar references the same object {n: 1}, and lvalues are resolved before assignations. foo = {n: 2} is creating a new object, and so foo is updated to reference that new object. The trick here is foo in foo.x = ... as a lvalue was resolved beforehand and still reference the old foo = {n: 1} object and update it by adding the x value. After that chain assignments, bar still reference the old foo object, but foo reference the new {n: 2} object, where x is not existing.
It's equivalent to:
var foo = { n: 1 };
var bar = foo;
foo = { n: 2 }; // -> {n: 2}
bar.x = foo; // -> {n: 1, x: {n: 2}}
// bar.x point to the address of the new foo object
// it's not equivalent to: bar.x = {n: 2}Accessing object properties with arrays
var obj = { property: 1 };
var array = ["property"];
obj[array]; // -> 1
// this also works with nested arrays
var nestedArray = [[[[[[[[[["property"]]]]]]]]]];
obj[nestedArray]; // -> 1What about pseudo-multidimensional arrays?
var map = {};
var x = 1;
var y = 2;
var z = 3;
map[[x, y, z]] = true;
map[[x + 10, y, z]] = true;
map["1,2,3"]; // -> true
map["11,2,3"]; // -> true💡 Explanation:
The brackets [] operator converts the passed expression using toString. Converting a one-element array to a string is akin to converting the contained element to the string:
["property"].toString(); // -> 'property'`Number.toFixed()` display different numbers
Number.toFixed() can behave a bit strange in different browsers. Check out this example:
(0.7875).toFixed(3);
// Firefox: -> 0.787
// Chrome: -> 0.787
// IE11: -> 0.788
(0.7876).toFixed(3);
// Firefox: -> 0.788
// Chrome: -> 0.788
// IE11: -> 0.788💡 Explanation:
While your first instinct may be that IE11 is correct and Firefox/Chrome are wrong, the reality is that Firefox/Chrome are more directly obeying standards for numbers (IEEE-754 Floating Point), while IE11 is minutely disobeying them in (what is probably) an effort to give clearer results.
You can see why this occurs with a few quick tests:
// Confirm the odd result of rounding a 5 down
(0.7875).toFixed(3); // -> 0.787
// It looks like it's just a 5 when you expand to the
// limits of 64-bit (double-precision) float accuracy
(0.7875).toFixed(14); // -> 0.78750000000000
// But what if you go beyond the limit?
(0.7875).toFixed(20); // -> 0.78749999999999997780Floating point numbers are not stored as a list of decimal digits internally, but through a more complicated methodology that produces tiny inaccuracies that are usually rounded away by toString and similar calls, but are actually present internally.
In this case, that "5" on the end was actually an extremely tiny fraction below a true 5. Rounding it at any reasonable length will render it as a 5... but it is actually not a 5 internally.
IE11, however, will report the value input with only zeros appended to the end even in the toFixed(20) case, as it seems to be forcibly rounding the value to reduce the troubles from hardware limits.
See for reference NOTE 2 on the ECMA-262 definition for toFixed.
`Math.max()` less than `Math.min()`
I find this example hilarious:
Math.min() > Math.max(); // -> true
Math.min() < Math.max(); // -> false💡 Explanation:
This is a simple one. Let's consider each part of this expression separately:
Math.min(); // -> Infinity
Math.max(); // -> -Infinity
Infinity > -Infinity; // -> trueWhy so? Well, Math.max() is not the same thing as Number.MAX_VALUE. It does not return the largest possible number.
Math.max takes arguments, tries to convert the to numbers, compares each one and then returns the largest remaining. If no arguments are given, the result is −∞. If any value is NaN, the result is NaN.
The opposite is happening for Math.min. Math.min returns ∞, if no arguments are given.
- 15.8.2.11 Math.max
- 15.8.2.11 Math.min
- Why is
Math.max()less thanMath.min()? by Charlie Harvey
Comparing `null` to `0`
The following expressions seem to introduce a contradiction:
null == 0; // -> false
null > 0; // -> false
null >= 0; // -> trueHow can null be neither equal to nor greater than 0, if null >= 0 is actually true? (This also works with less than in the same way.)
💡 Explanation:
The way these three expressions are evaluated are all different and are responsible for producing this unexpected behavior.
First, the abstract equality comparison null == 0. Normally, if this operator can't compare the values on either side properly, it converts both to numbers and compares the numbers. Then, you might expect the following behavior:
// This is not what happens
(null == 0 + null) == +0;
0 == 0;
true;However, according to a close reading of the spec, the number conversion doesn't actually happen on a side that is null or undefined. Therefore, if you have null on one side of the equal sign, the other side must be null or undefined for the expression to return true. Since this is not the case, false is returned.
Next, the relational comparison null > 0. The algorithm here, unlike that of the abstract equality operator, will convert null to a number. Therefore, we get this behavior:
null > 0
+null = +0
0 > 0
falseFinally, the relational comparison null >= 0. You could argue that this expression should be the result of null > 0 || null == 0; if this were the case, then the above results would mean that this would also be false. However, the >= operator in fact works in a very different way, which is basically to take the opposite of the < operator. Because our example with the greater than operator above also holds for the less than operator, that means this expression is actually evaluated like so:
null >= 0;
!(null < 0);
!(+null < +0);
!(0 < 0);
!false;
true;Same variable redeclaration
JS allows to redeclare variables:
a;
a;
// This is also valid
a, a;Works also in strict mode:
var a, a, a;
var a;
var a;💡 Explanation:
All definitions are merged into one definition.
Default behavior Array.prototype.sort()
Imagine that you need to sort an array of numbers.
[10, 1, 3].sort(); // -> [ 1, 10, 3 ]💡 Explanation:
The default sort order is built upon converting the elements into strings, then comparing their sequences of UTF-16 code units values.
Hint
Pass compareFn if you try to sort anything but string.
[10, 1, 3].sort((a, b) => a - b); // -> [ 1, 3, 10 ]resolve() won't return Promise instance
const theObject = {
a: 7
};
const thePromise = new Promise((resolve, reject) => {
resolve(theObject);
}); // Promise instance object
thePromise.then(value => {
console.log(value === theObject); // > true
console.log(value); // > { a: 7 }
});The value which is resolved from thePromise is exactly theObject.
How about input another Promise into the resolve function?
const theObject = new Promise((resolve, reject) => {
resolve(7);
}); // Promise instance object
const thePromise = new Promise((resolve, reject) => {
resolve(theObject);
}); // Promise instance object
thePromise.then(value => {
console.log(value === theObject); // > false
console.log(value); // > 7
});💡 Explanation:
This function flattens nested layers of promise-like objects (e.g. a promise that resolves to a promise that resolves to something) into a single layer.
The specification is ECMAScript 25.6.1.3.2 Promise Resolve Functions. But it is not quite human-friendly.
`{}{}` is undefined
Write them in the console. They will return the value defined in the last object.
{}{}; // -> undefined
{}{}{}; // -> undefined
{}{}{}{}; // -> undefined
{foo: 'bar'}{}; // -> 'bar'
{}{foo: 'bar'}; // -> 'bar'
{}{foo: 'bar'}{}; // -> 'bar'
{a: 'b'}{c:' d'}{}; // -> 'd'
{a: 'b', c: 'd'}{}; // > SyntaxError: Unexpected token ':'
({}{}); // > SyntaxError: Unexpected token '{'💡 Explanation:
When inspecting each {}, they returns undefined. If you inspect {foo: 'bar'}{}, you will find {foo: 'bar'} is 'bar'.
There are two meanings for {}: an object or a block. For example, the {} in () => {} means block. So we need to use () => ({}) to return an object.
Let's use {foo: 'bar'} as a block. Write this snippet in your console:
if (true) {
foo: "bar";
} // -> 'bar'Surprisingly, it behaviors the same! You can guess here that {foo: 'bar'}{} is a block.
`arguments` binding
Consider this function:
function a(x) {
arguments[0] = "hello";
console.log(x);
}
a(); // > undefined
a(1); // > "hello"💡 Explanation:
arguments is an Array-like object that contains the values of the arguments passed to that function. When no arguments are passed, then there's no x to override.
- The arguments object on MDN
An `alert` from hell
This on is literally from hell:
[666]["\155\141\160"]["\143\157\156\163\164\162\165\143\164\157\162"](
"\141\154\145\162\164(666)"
)(666); // alert(666)💡 Explanation:
This one is based on octal escape sequences and multiple strings.
Any character with a character code lower than 256 (i.e. any character in the extended ASCII range) can be escaped using its octal-encoded character code, prefixed with \. An example above is basically an alert encoded by octal escape sequences.
An infinite timeout
Guess what would happen if we set an infinite timeout?
setTimeout(() => console.log("called"), Infinity); // -> <timeoutId>
// > 'called'It will executed immediately instead of infinity delay.
💡 Explanation:
Usually, runtime stores the delay as a 32-bit signed integer internally. This causes an integer overflow, resulting in the timeout being executed immediately.
For example, in Node.js we will get this warning:
(node:1731) TimeoutOverflowWarning: Infinity does not fit into a 32-bit signed integer.
Timeout duration was set to 1.
(Use `node --trace-warnings ...` to show where the warning was created)A `setTimeout` object
Guess what would happen if we set a callback that's not a function to setTimeout?
setTimeout(123, 100); // -> <timeoutId>
// > 'called'This is fine.
setTimeout('{a: 1}', 100); // -> <timeoutId>
// > 'called'This is also fine.
setTimeout({a: 1}, 100); // -> <timeoutId>
// > 'Uncaught SyntaxError: Unexpected identifier setTimeout (async) (anonymous) @ VM__:1'This throws an SyntaxError.
Note that this can easily happen if your function returns an object and you call it here instead of passing it! What if the content - policy is set to self?
setTimeout(123, 100); // -> <timeoutId>
// > console.error("[Report Only] Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of script in the following Content Security Policy directive: "script-src 'report-sample' 'self' ")The console refuses to run it at all!
💡 Explanation:
WindowOrWorkerGlobalScope.setTimeout() can be called with code as first argument, which will be passed on to eval, which is bad. Eval will coerce her input to String, and evaluate what is produced, so Objects becomes '[object Object]' which has hmmm ... an 'Unexpected identifier'!
- eval() on MDN (don't use this)
- WindowOrWorkerGlobalScope.setTimeout() on MDN
- Content Security Policy
- Timers on W3C
Double dot
Let's try to coerce a number to a string:
27.toString() // > Uncaught SyntaxError: Invalid or unexpected tokenMaybe we should try with two dots?
27..toString(); // -> '27'But why doesn't first example work?
💡 Explanation:
It's just a language grammar limitation.
The . character presents an ambiguity. It can be understood to be the member operator, or a decimal, depending on its placement.
The specification's interpretation of the . character in that particular position is that it will be a decimal. This is defined by the numeric literal syntax of ECMAScript.
You must always use parenthesis or an addition dot to make such expression valid.
(27).toString(); // -> '27'
// or
27..toString(); // -> '27'- Usage of toString in JavaScript on StackOverflow
- Why does 10..toString() work, but 10.toString() does not?
Extra Newness
I present this as an oddity for your amusement.
class Foo extends Function {
constructor(val) {
super();
this.prototype.val = val;
}
}
new new Foo(":D")().val; // -> ':D'💡 Explanation:
Constructors in JavaScript are just functions with some special treatment. By extending Function using the class syntax you create a class that, when instantiated, is now a function, which you can then additionally instantiate.
While not exhaustively tested, I believe the last statement can be analyzed thus:
new new Foo(":D")().val(new newFooInstance()).val;
veryNewFooInstance.val;
// -> ':D'As a tiny addendum, doing new Function('return "bar";') of course creates a function with the body return "bar";. Since super() in the constructor of our Foo class is calling Function's constructor, it should come as no surprise now to see that we can additionally manipulate things in there.
class Foo extends Function {
constructor(val) {
super(`
this.val = arguments[0];
`);
this.prototype.val = val;
}
}
var foo = new new Foo(":D")("D:");
foo.val; // -> 'D:'
delete foo.val; // remove the instance prop 'val', deferring back to the prototype's 'val'.
foo.val; // -> ':D'Why you should use semicolons
Writing some standard JavaScript… and then BOOM!
class SomeClass {
["array"] = []
["string"] = "str"
}
new SomeClass().array; // -> 'str'What the …?
💡 Explanation:
Once again, this is all thanks to the Automatic Semicolon Insertion.
An example above is basically the same as:
class SomeClass {
["array"] = ([]["string"] = "str");
}You basically assign a string str into an array property.
- An original tweet with an example by Ryan Cavanaugh
- TC39 meeting when they debated about it
Split a string by a space
Have you ever tried to split a string by a space?
"".split(""); // -> []
// but…
"".split(" "); // -> [""]💡 Explanation:
This is expected behaviour. Its responsibility is to divide the input string every time a separator occurs in that input string. When you pass in an empty string it'll never find a separator and thus return that string.
Let's quote the specification:
The substrings are determined by searching from left to right for occurrences of
separator; these occurrences are not part of any String in the returned array, but serve to divide up the String value.
- 22.1.3.21 String.prototype.split
- An original tweet with an example by Ryan Cavanaugh
- A tweet with an explanation by Nabil Tharwat
A stringified string
This caused a bug that I've been solving for a few days:
JSON.stringify("production") === "production"; // -> false💡 Explanation:
Let's see what JSON.stringify is returning:
JSON.stringify("production"); // -> '"production"'It is actually a stringified string, so it's true:
'"production"' === "production"; // -> falseNon-strict comparison of a number to `true`
1 == true; // -> true
// but…
Boolean(1.1); // -> true
1.1 == true; // -> false💡 Explanation:
According to the specification:
The comparison x == y, where x and y are values, produces true or false. Such a comparison is performed as follows:
- If
Type(x)is Number andType(y)is String, return the result of the comparisonx == ! ToNumber(y).
So this comparison is performed like this:
1 == true;
1 == Number(true);
1 == 1; // -> true
// but…
1.1 == true;
1.1 == Number(true);
1.1 == 1; // -> false📚 Other resources
- wtfjs.com — a collection of those very special irregularities, inconsistencies and just plain painfully unintuitive moments for the language of the web.
- Wat — A lightning talk by Gary Bernhardt from CodeMash 2012
- What the... JavaScript? — Kyle Simpsons talk for Forward 2 attempts to “pull out the crazy” from JavaScript. He wants to help you produce cleaner, more elegant, more readable code, then inspire people to contribute to the open source community.
- Zeros in JavaScript — a comparison table of
==,===,+and*in JavaScript.
🤝 Supporting
Hi! I work on this project in my spare time, in addition to my primary job. I hope you enjoy reading it. If you do, please, consider supporting me 🙏.
Every single donation is important. Your donation is gonna make a clear statement: My work is valued.
🙏 Thank you for your support! 🙏
| Service | Link | Action |
|---|---|---|
| Patreon | Become a patron | |
| BuyMeACoffee | Buy me a cup of ☕️ or 🥤 | |
| Bitcoin | 1EJsKs6rPsqa7QLoVLpe3wgcdL9Q8WmDxE |
|
| Ethereum | 0x6aF39C917359897ae6969Ad682C14110afe1a0a1 |
⚠️ Note: I live in Ukraine and services like PayPal and Stripe don't work with Ukrainian bank accounts. This means there's no way for me to set up GitHub Sponsors, OpenCollective, or services relied on them. Sorry, those are the only ways you can support me for now.
🎓 License
File: README-fr-fr.md
What the f\*ck JavaScript?
Une liste d'exemples JavaScript drôles et délicats
Le JavaScript est un langage formidable! Il possède une syntaxe simple, un grand écosystème et, le plus important de tout, une immense communauté.
En même temps, nous savons tous que le JavaScript est un langage assez amusant comprenant des aspects plus complexes que d'autres. Certains d'entre eux peuvent rapidement faire de notre travail quotidien un enfer, tout comme d'autres peuvent nous faire rire aux éclats.
L'idée originale de WTFJS appartient à Brian Leroux. Cette liste est fortement inspirée par son discours “WTFJS” at dotJS 2012:
Le Manuscript sous forme de paquet Node
Vous pouvez installer ce manuel en utilisant npm. Pour cela, il suffit d'exécuter :
$ npm install wtfjsVous devriez pouvoir ensuite utiliser wtfjs en ligne de commande. Cela ouvrira le manuel dans votre terminal. Sinon, vous pouvez continuer à lire ici tout simplement.
La source du package est disponible ici: https://github.com/denysdovhan/wtfjs
Traductions
Actuellement, il existe des traductions de ** wtfjs ** pour les langues suivantes :
Table of Contents
- 💪🏻 Motivation
- ✍🏻 Notation
- 👀 Exemples
- [
[]est égal à ` - [
truen'est pas égal à ` - true est faux
- baNaNa
NaNn'est pas unNaN- C'est un échec
[]est truthy, mais pastruenullest falsy, mais pasfauxdocument.allest un objet, mais il estundefined- La valeur minimale est supérieure à zéro
- Fonction n'est pas une fonction
- Ajout de tableaux
- Les virgules finales dans un tableau
- L'égalité des tableaux est un monstre
undefinedetNumberparseIntest un méchant- Math avec
trueetfalse - Les commentaires HTML sont valides en JavaScript
NaNn'estpasun nombre[]etnullsont des objets- Nombres magiquement croissant
- Précision de
0.1 + 0.2 - Patching de numéros
- Comparaison de trois nombres
- Math drôle
- Addition de RegExps
- Les chaînes ne sont pas des instances de
String - Appeler des fonctions avec des caractères accent grave
- Call call call
- Une propriété
constructor - Object en tant que clé de la propriété d'un objet
- Accéder aux prototypes avec
__proto__ `${{Object}}`- Déstructuration avec des valeurs par défaut
- Points et propagation
- Étiquettes
- Étiquettes imbriquées
Try...catchinsidieux- Est-ce un héritage multiple ?
- Un générateur qui se
yieldlui-même - Une classe de classe
- Objets incoercibles
- Fonctions fléchées complexes
- Les fonctions fléchées ne peuvent pas être un constructeur
argumentset fonctions fléchées- Retour difficile
- Chaînage d'affectations sur un objet
- Accéder aux propriétés d'un objet avec des tableaux
- Opérateurs
nullet relationnels Number.toFixed()affiche différents nombresMath.max()est moins queMath.min()- Comparer
nullà0 - Même redéclaration d'une variable
- Comportement par défaut d'
Array.prototype.sort()
- [
- 📚 Autres ressources
- 🎓 Licence
💪🏻 Motivation
Juste pour le fun
— “Just for Fun: The Story of an Accidental Revolutionary”, Linus Torvalds
L'objectif principal de cette liste est de rassembler quelques exemples loufoques et d'expliquer leur fonctionnement, quand c'est possible. 😉 Tout simplement parce qu'il est amusant d'apprendre quelque chose qu'on ne connaissait pas auparavant.
Si vous êtes débutant, vous pouvez aussi utiliser ces notes pour approfondir vos connaissances en JavaScript. J'espère qu'elles vous inciteront à passer plus de temps à lire la spécification.
Si vous êtes un développeur professionnel, vous pouvez considérer ces exemples comme une excellente référence pour toutes les bizarreries et comportements inattendus de notre langage bien-aimé, le JavaScript.
Dans tous les cas, lisez ce qui suit. Vous y trouverez probablement quelque chose de nouveau !
✍🏻 Notation
// -> est utilisé pour afficher le résultat d'une expression. Par exemple :
1 + 1; // -> 2// > définit le résultat de console.log ou tout autre sortie. Par exemple :
console.log("hello, world!"); // > hello, world!// indique un commentaire utilisé pour donner des explications. Par exemple :
// Assigner une fonction la constant foo
const foo = function() {};👀 Exemples
`[]` est égal à `![]`
Tableau est égal à pas tableau
[] == ![]; // -> true💡 Explication :
L'opérateur de comparaison d'égalité faible convertit les deux côtés en nombres pour les comparer. Pour différentes raisons, les deux côtés deviennent le nombre 0.
Les tableaux sont truthy, donc à droite, on trouve l'opposé d'une valeur truthy, soit false, qui est ensuite forcé à 0. A gauche, puisqu'un tableau vide est forcé à 0 automatiquement, sans devoir être précédemment transformé en booléen, on trouve aussi 0, malgré le fait qu'un tableau soit truthy.
Voici comment cette expression se simplifie:
+[] == +![];
0 == +false;
0 == 0;
true;Voir aussi [] est truthy, mais pas true.
`true` n'est pas égal à `![]`, mais pas égal à `[]` aussi
Un tableau n'est pas égal à true, tout comme pas tableau. Un tableau est égal à false, pas tableau est égal à false aussi :
true == []; // -> false
true == ![]; // -> false
false == []; // -> true
false == ![]; // -> true💡 Explication :
true == []; // -> false
true == ![]; // -> false
// Selon la spécification
true == []; // -> false
toNumber(true); // -> 1
toNumber([]); // -> 0
1 == 0; // -> false
true == ![]; // -> false
![]; // -> false
true == false; // -> falsefalse == []; // -> true
false == ![]; // -> true
// Selon la spécification
false == []; // -> true
toNumber(false); // -> 0
toNumber([]); // -> 0
0 == 0; // -> true
false == ![]; // -> false
![]; // -> false
false == false; // -> truetrue est faux
!!"false" == !!"true"; // -> true
!!"false" === !!"true"; // -> true💡 Explication :
Considérez ceci étape par étape :
// `true` est 'truthy' et est représenté par la valeur 1 (nombre), 'true' sous forme de chaîne est NaN.
true == "true"; // -> false
false == "false"; // -> false
// 'false' n'est pas une chaîne vide, donc c'est une valeur `truthy`
!!"false"; // -> true
!!"true"; // -> truebaNaNa
"b" + "a" + +"a" + "a"; // -> 'baNaNa'Ceci est une blague "old school" en JavaScript, mais remasterisée. Voici l'originale :
"foo" + +"bar"; // -> 'fooNaN'💡 Explication :
L'expression est évaluée comme 'foo' + (+'bar'), ce qui convertit 'bar' à NaN.
`NaN` n'est pas un `NaN`
NaN === NaN; // -> false💡 Explication :
La spécification définit strictement la logique derrière ce comportement :
- Si
Type(x)est différent deType(y), retourne false.- Si
Type(x)estNumber, alors
- Si
xest NaN, retourne false.- Si
yest NaN, retourne false.- … … …
Sur la base de la définition de NaN de l'IEEE :
Quatre relations mutuellement exclusives sont possibles : inférieur à, égal, supérieur à, et non ordonné. Le dernier cas survient quand au moins un opérande est
NaN. Tous lesNaNdoivent se comparer de manière non ordonnée avec tout, y compris avec lui-même.— “What is the rationale for all comparisons returning false for IEEE754 NaN values?” sur StackOverflow.
C'est un échec
Vous ne le croiriez pas, mais …
(![] + [])[+[]] +
(![] + [])[+!+[]] +
([![]] + [][[]])[+!+[] + [+[]]] +
(![] + [])[!+[] + !+[]];
// -> 'fail'💡 Explication :
En brisant cette masse de symboles en morceaux, nous remarquons que le schéma suivant se produit souvent :
![] + []; // -> 'false'
![]; // -> falseDonc, nous essayons d'ajouter [] à false, mais en raison d'un certain nombre d'appels de fonctions internes (binary + Operator -> ToPrimitive -> [[DefaultValue]]), nous finissons par convertir l'opérande de droite en chaîne :
![] + [].toString(); // 'false'En considérant une chaîne comme un tableau, nous pouvons accéder à son premier caractère via [0] :
"false"[0]; // -> 'f'Le reste est évident, sauf pour le i. Le i dans fail est saisi en générant la chaîne "falseundefined" et en saisissant l'éléments sur l'index [10].
`[]` est truthy, mais pas `true`
Un tableau est une valeur truthy, mais n'est pas égal à true.
!![] // -> true
[] == true // -> false💡 Explication :
Voici des liens vers les sections correspondantes de la spécification ECMA-262 :
`null` est falsy, mais pas `faux`
Malgré le fait que null soit une valeur falsy, elle n'est pas égale à false.
0 == false; // -> true
"" == false; // -> true💡 Explication :
L'explication est la même que pour l'exemple précédent. Voici le lien correspondant :
`document.all` est un objet, mais il est `undefined`
⚠️ Ceci fait partie de la Browser API et ne fonctionnera pas dans un environnement Node.js ⚠️
Malgré le fait que document.all soit un objet de type tableau et qu'il donne accès aux nœuds DOM de la page, il répond à la fonction typeof comme étant undefined.
document.all instanceof Object; // -> true
typeof document.all; // -> 'undefined'En même temps, document.all n'est pas égal à undefined.
document.all === undefined; // -> false
document.all === null; // -> falseMais, parallèlement :
document.all == null; // -> true💡 Explication :
document.allétait anciennement un moyen d'accéder aux éléments DOM, principalement avec les anciennes versions d'IE. Bien que cela n'ait jamais été une norme,document.allétait largement utilisé dans "l'ancien code JS". Quand la norme a progressé avec la venue de nouvelles API (par exemple,document.getElementById), l'APIdocument.allest devenue obsolète et le comité de normes a dû décider ce qu'ils allaient en faire. En raison de sa large utilisation, ils ont décidé de la conserver, mais d'introduire une violation volontaire de la spécification JavaScript.
La raison pour laquelledocument.allretournefalselors de l'utilisation de l'opérateur d'égalité stricte (Strict Equality Comparison) avecundefinedettruelors de l'utilisation de l'opérateur d'égalité abstraite (Abstract Equality Comparison) est due à la violation volontaire de la spécification qui le permet explicitement.— “Obsolete features - document.all” sur WhatWG - HTML spec.
— “Chapter 4 - ToBoolean - Falsy values” sur YDKJS - Types & Grammar.
La valeur minimale est supérieure à zéro
Number.MIN_VALUE est le plus petit nombre, qui est supérieur à zéro :
Number.MIN_VALUE > 0; // -> true💡 Explication :
Number.MIN_VALUEest5e-324, c'est-à-dire le plus petit nombre positif pouvant être représenté dans la précision flottante et donc, c'est aussi le plus près que possible de zéro. Il définit la meilleure résolution que vous pouvez atteindre avec des valeurs flottantes.Maintenant, la plus petite valeur globale est
Number.NEGATIVE_INFINITY, bien que cette dernière ne soit pas vraiment numérique au sens strict.— “Why is
0less thanNumber.MIN_VALUEin JavaScript?” at StackOverflow
Fonction n'est pas une fonction
⚠️ Une erreur présente dans la v5.5 (V8) ou inférieure de Node.js (Node.js <=7) ⚠️
Vous êtes tous au courant de l'irritant undefined is not a function, mais qu'en est-il de ceci :
// Déclare une classe qui _extends_ `null`
class Foo extends null {}
// -> [Function: Foo]
new Foo() instanceof null;
// > TypeError: function is not a function
// > at … … …💡 Explication :
Ceci ne fait pas partie de la spécification. C'est seulement une erreur qui a depuis été corrigé, il ne devrait donc plus y avoir de problème à l'avenir.
Ajout de tableaux
Et si vous essayiez d'additionner deux tableaux ?
[1, 2, 3] + [4, 5, 6]; // -> '1,2,34,5,6'💡 Explication :
C'est la concaténation ! Etape par étape, ça ressemble à ceci :
[1, 2, 3] +
[4, 5, 6][
// appelle toString()
(1, 2, 3)
].toString() +
[4, 5, 6].toString();
// concaténation
"1,2,3" + "4,5,6";
// ->
("1,2,34,5,6");Les virgules finales dans un tableau
Vous avez créé un tableau avec 4 éléments vides. Malgré tout, vous obtiendrez un tableau avec seulement trois éléments, à cause des virgules finales.
let a = [, , ,];
a.length; // -> 3
a.toString(); // -> ',,'💡 Explication :
Les virgules finales (trailing commas en anglais) peuvent être utiles lors de l'ajout de nouveaux éléments, de paramètres ou de propriétés à du code JavaScript. Si vous voulez ajouter une nouvelle propriété, vous pouvez tout simplement ajouter une nouvelle ligne sans modifier la ligne précédente si cette ligne utilise déjà une virgule finale. Cela rend plus clair les différences dans un système de contrôle de version et l'édition de code pourrait être moins difficile.
— Virgules finales (trailing commas) sur MDN.
L'égalité des tableaux est un monstre
En JavaScript, l'égalité des tableaux est un monstre, comme vous pouvez le voir ci-dessous :
[] == '' // -> true
[] == 0 // -> true
[''] == '' // -> true
[0] == 0 // -> true
[0] == '' // -> false
[''] == 0 // -> true
[null] == '' // true
[null] == 0 // true
[undefined] == '' // true
[undefined] == 0 // true
[[]] == 0 // true
[[]] == '' // true
[[[[[[]]]]]] == '' // true
[[[[[[]]]]]] == 0 // true
[[[[[[ null ]]]]]] == 0 // true
[[[[[[ null ]]]]]] == '' // true
[[[[[[ undefined ]]]]]] == 0 // true
[[[[[[ undefined ]]]]]] == '' // true💡 Explication :
Vous devriez regarder très attentivement les exemples ci-dessus ! Ce comportement est décrit dans la section 7.2.13 Abstract Equality Comparison de la spécification.
`undefined` et `Number`
Si nous ne transmettons aucun argument dans le constructeur Number, nous obtiendrons 0. La valeur undefined est attribuée aux arguments formels en l'absence d'arguments, alors vous pouvez vous attendre à ce que Number sans argument utilise undefined comme valeur de paramètre. Toutefois, quand nous lui passons undefined directement, nous obtiendrons NaN en retour.
Number(); // -> 0
Number(undefined); // -> NaN💡 Explication :
Selon la spécification :
- Si aucun argument n'a été passé lors de l'appel de la fonction,
nest+0. - Sinon,
nest ?ToNumber(value). - Dans le cas d'
undefined,ToNumber(undefined)doit retournerNaN.
Voici les sections correspondantes :
`parseInt` est un méchant
parseInt est célèbre pour ses bizarreries:
parseInt("f*ck"); // -> NaN
parseInt("f*ck", 16); // -> 15💡 Explication : Ce résultat est dû au fait que parseInt continue d'analyser caractère par caractère la chaîne jusqu'à ce qu'il rencontre un caractère qu'il ne connaît pas. Le f dans 'f*ck' correspond au chiffre hexadécimal 15.
Analyser Infinity comme entier est quelque chose…
//
parseInt("Infinity", 10); // -> NaN
// ...
parseInt("Infinity", 18); // -> NaN...
parseInt("Infinity", 19); // -> 18
// ...
parseInt("Infinity", 23); // -> 18...
parseInt("Infinity", 24); // -> 151176378
// ...
parseInt("Infinity", 29); // -> 385849803
parseInt("Infinity", 30); // -> 13693557269
// ...
parseInt("Infinity", 34); // -> 28872273981
parseInt("Infinity", 35); // -> 1201203301724
parseInt("Infinity", 36); // -> 1461559270678...
parseInt("Infinity", 37); // -> NaNSoyez prudent avec l'analyse de null aussi :
parseInt(null, 24); // -> 23💡 Explication :
parsetIntconvertitnullsous forme de chaîne"null"et essait de la convertir. Pour les bases comprises entre 0 et 23,parseIntne peut convertir aucun chiffre, doncparseIntrenvoieNaN. Sur 24,"n", la 14ème lettre, est ajoutée au système de numération. Sur 31,"u", la 21ème lettre, est ajoutée et la chaîne entière peut être décodée. Sur 37, il n'y a plus de jeu de valeur numérique valide pouvant être générée, donc,NaNest renvoyé.— “parseInt(null, 24) === 23… wait, what?” sur StackOverflow.
N'oubliez pas les octaux:
parseInt("06"); // 6
parseInt("08"); // 8 si support ECMAScript 5
parseInt("08"); // 0 si pas support ECMAScript 5💡 Explication : Si la chaîne d'entrée commence par 0, la base est égale à 8 (octal) ou à 10 (décimal). La base choisie dépend de l'implémentation. ECMAScript 5 indique que la valeur 10 (décimal) est utilisée, mais tous les navigateurs ne le prennent pas encore en charge. Pour cette raison, spécifiez toujours une base lorsque vous utilisez parseInt.
parseInt convertit toujours une entrée en chaîne :
parseInt({ toString: () => 2, valueOf: () => 1 }); // -> 2
Number({ toString: () => 2, valueOf: () => 1 }); // -> 1Soyez prudent lors d'analyse de valeurs en virgule flottante :
parseInt(0.000001); // -> 0
parseInt(0.0000001); // -> 1
parseInt(1 / 1999999); // -> 5💡 Explication : parseInt prend une chaîne de caractère comme argument et retourne un entier sur la base spécifiée. parseInt supprime aussi tout ce que suit, incluant le premier non-chiffre de la chaîne transmise en paramètre. 0.000001 est converti en chaîne "0.000001", et parseInt retourne 0. Quand 0.0000001 est converti en chaîne, il est traité comme "1e-7" et retourne donc 1. 1/1999999 est interprété comme étant 5.00000250000125e-7 et retourne 5.
Math avec `true` et `false`
Faisons des maths :
true +
true(
// -> 2
true + true
) *
(true + true) -
true; // -> 3Hmmm… 🤔
💡 Explication :
Avec le constructeur Number, nous pouvons forcer les valeurs aux nombres. Il est assez évident que true sera forcé à 1.
Number(true); // -> 1L'opérateur unaire + tente de convertir sa valeur en nombre. Il peut convertir des représentations d'entiers et de nombres flottants sous forme de chaîne, de même que les valeurs true, false et null. S'il ne peut pas analyser une valeur particulière, il sera évalué à NaN. Cela signifie que nous pouvons contraindre true à 1 plus facilement :
+true; // -> 1Lorsque vous effectuez une addition ou une multiplication, la méthode ToNumber est appelée. Selon la spécification, cette méthode retourne :
Si
argumentest true, retourne 1. Siargumentest false, retourne +0.
C'est pourquoi nous pouvons ajouter des valeurs booléennes en tant que nombres réguliers et obtenir des résultats corrects.
Les sections correspondantes :
Les commentaires HTML sont valides en JavaScript
Vous serez impressionné, mais <!-- (connu sous le nom de commentaire HTML) est aussi valide comme commentaire en JavaScript.
// commentaire valide
<!-- commentaire valide aussi💡 Explication :
Impressionné ? Les commentaires de type HTML étaient à la base destinés à permettre aux navigateurs ne comprenant pas la balise <script> de se dégrader avec élégance. Ces navigateurs, par exemple Netscape 1.x, ne sont plus populaires aujourd'hui. Il est donc inutile de mettre des commenaitres HTML dans vos balises <script>.
`NaN` n'est ~~pas~~ un nombre
typeof NaN est un 'number' :
typeof NaN; // -> 'number'💡 Explication :
Explications sur le fonctionnement des opérateurs typeof et instanceof :
`[]` et `null` sont des objets
typeof []; // -> "object"
typeof null; // -> "object"
// however
null instanceof Object; // false💡 Explication :
Le comportement de l'opérateur typeof est défini dans la section suivante de la spécification :
Selon la spécification, l'opérateur typeof renvoie une chaîne : Table 35: typeof Operator Results. Pour les objets null, ordinaires, standard exotic et non-standard exotic, qui n'implémentent pas [[Call]], il retourne la chaîne "object".
En revanche, vous pouvez vérifier le type d'un objet en utilisant la méthode toString.
Object.prototype.toString.call([]);
// -> '[object Array]'
Object.prototype.toString.call(new Date());
// -> '[object Date]'
Object.prototype.toString.call(null);
// -> '[object Null]'Nombres magiquement croissant
999999999999999; // -> 999999999999999
9999999999999999; // -> 10000000000000000
10000000000000000; // -> 10000000000000000
10000000000000000 + 1; // -> 10000000000000000
10000000000000000 + 1.1; // -> 10000000000000002💡 Explication :
Ceci est dû à la norme IEEE 754-2008 concernant l'arithmétique binaire en virgule flottante. À cette échelle, un nombre s'arrondit au nombre pair le plus près. Plus d'infos :
- 6.1.6 The Number Type
- IEEE 754 sur Wikipedia
Précision de `0.1 + 0.2`
Une blague bien connue. L'ajout de 0.1 et de 0.2 est mortellement précis :
0.1 +
0.2(
// -> 0.30000000000000004
0.1 + 0.2
) ===
0.3; // -> false💡 Explication :
La réponse à la question ”Is floating point math broken?” sur StackOverflow:
Les constantes
0.2et0.3seront également des approximations à leurs vraies valeurs. Il arrive que ledoublele plus proche de0.2soit supérieur au nombre rationnel0.2, mais que ledoublele plus proche de0.3soit inférieur au nombre rationnel0.3. La somme de0.1et0.2finit donc par être supérieure au nombre rationnel0.3et donc, en désaccord avec la constante de votre code.
Le problème est tellement connu qu'il y a même un site web appelé 0.30000000000000004.com. Il est récurrent pour tous les langages utilisant des mathématiques avec des virgules flottantes, pas seulement JavaScript.
Patching de numéros
Vous pouvez ajouter vos propres méthodes pour encapsuler des objets comme Number ou String.
Number.prototype.isOne = function() {
return Number(this) === 1;
};
(1.0).isOne(); // -> true
(1).isOne(); // -> true
(2.0)
.isOne()(
// -> false
7
)
.isOne(); // -> false💡 Explication :
De toute évidence, vous pouvez extend l'objet Number comme n'importe quel autre objet en JavaScript, mais ce n'est toutefois pas recommandé si le comportement de la méthode définie ne fait partie de la spécification. Voici donc la liste des propriétés de Number :
Comparaison de trois nombres
1 < 2 < 3; // -> true
3 > 2 > 1; // -> false💡 Explication :
Pourquoi est-ce que cela fonctionne ainsi ? Et bien le problème se trouve dans la première partie de l'expression. Voici comment cela fonctionne :
1 < 2 < 3; // 1 < 2 -> true
true < 3; // true -> 1
1 < 3; // -> true
3 > 2 > 1; // 3 > 2 -> true
true > 1; // true -> 1
1 > 1; // -> falseNous pouvons résoudre ce problème avec l'opérateur Supérieur ou égal à (>=) :
3 > 2 >= 1; // trueEn savoir plus sur les opérateurs relationnels dans la spécification :
Math drôle
Souvent, les résultats d'opérations arithmétiques en JavaScript peuvent être assez inattendus. Considérez ces exemples :
3 - 1 // -> 2
3 + 1 // -> 4
'3' - 1 // -> 2
'3' + 1 // -> '31'
'' + '' // -> ''
[] + [] // -> ''
{} + [] // -> 0
[] + {} // -> '[object Object]'
{} + {} // -> '[object Object][object Object]'
'222' - -'111' // -> 333
[4] * [4] // -> 16
[] * [] // -> 0
[4, 4] * [4, 4] // NaN💡 Explication :
Que se passe-t-il dans les quatre premiers exemples ? Voici un petit tableau pour comprendre les additions en JavaScript :
Nombre + Nombre -> addition
Booléen + Nombre -> addition
Booléen + Booléen -> addition
Nombre + Chaîne -> concaténation
Chaîne + Booléen -> concaténation
Chaîne + Chaîne -> concaténationQu'en est-il des autres exemples ? Les méthodes ToPrimitive et ToString sont implicitement appelées pour [] et {} avant une addition. En lire plus sur le processus d'évalution dans la spécification :
- 12.8.3 The Addition Operator (
+) - 7.1.1 ToPrimitive(
input[,PreferredType]) - 7.1.12 ToString(
argument)
Addition de RegExps
Saviez-vous que vous pouviez ajouter des nombres comme dans l'exemple ci-dessous ?
// Remplacement de la méthode toString
RegExp.prototype.toString =
function() {
return this.source;
} /
7 /
-/5/; // -> 2💡 Explication :
Les chaînes ne sont pas des instances de `String`
"str"; // -> 'str'
typeof "str"; // -> 'string'
"str" instanceof String; // -> false💡 Explication :
Le constructeur String retourne une chaîne :
typeof String("str"); // -> 'string'
String("str"); // -> 'str'
String("str") == "str"; // -> trueEssayons avec un new :
new String("str") == "str"; // -> true
typeof new String("str"); // -> "object"Un objet ? Qu'est-ce que c'est ?
new String("str"); // -> [String: 'str']Plus d'infos sur le constructeur String dans la spécification :
Appeler des fonctions avec des caractères accent grave
Déclarons une fonction qui enregistre tous les paramètres dans la console :
function f(...args) {
return args;
}Aucun doute, vous savez qu'il est possible d'appeler cette fonction comme ceci :
f(1, 2, 3); // -> [ 1, 2, 3 ]Mais saviez-vous que vous pouvez appeler n'importe quelle fonction avec des caractères accent grave (backticks en anglais) ?
f`true is ${true}, false is ${false}, array is ${[1, 2, 3]}`;
// -> [ [ 'true is ', ', false is ', ', array is ', '' ],
// -> true,
// -> false,
// -> [ 1, 2, 3 ] ]💡 Explication :
Bon, ce n'est pas du tout magique si vous êtes familier des littéraux de gabarits étiquetés. Dans l'exemple ci-dessus, la fonction f est une étiquette pour littéral de gabarit. Les étiquettes avant un littéral de gabarit vous permettent d'analyser les littéraux de gabarits avec une fonction. Le premier argument d'une fonction étiquetée contient un tableau avec comme valeurs des chaînes. Les arguments restants sont liés aux expressions. Exemple :
function template(strings, ...keys) {
// fait quelque chose avec `strings` et `keys`…
}Voici la magic behind la célébre bibliothèque appelée 💅 styled-components, qui est populaire dans la communauté React.
Lien vers la spécification :
Call call call
Trouvé par @cramforce
console.log.call.call.call.call.call.apply(a => a, [1, 2]);💡 Explication :
Attention, ceci pourrait vous casser la tête ! Essayez de reproduire ce code dans votre tête : nous appliquons la méthode call en utilisant la méthode apply. Plus d'infos :
- 19.2.3.3 Function.prototype.call(
thisArg, ...args) - **19.2.3.1 ** Function.prototype.apply(
thisArg,argArray)
Une propriété `constructor`
const c = "constructor";
c[c][c]('console.log("WTF?")')(); // > WTF?💡 Explication :
Considérez cet exemple étape par étape :
// Déclare une nouvelle constante qui est une chaîne : "constructor"
const c = "constructor";
// `c` est une chaîne
c; // -> 'constructor'
// Pour obtenir le constructeur de la chaîne
c[c]; // -> [Function: String]
// Pour obtenir le constructeur du constructeur
c[c][c]; // -> [Function: Function]
// Appelle la Fonction constructeur et passe
// le corps d'une nouvelle fonction comme argument
c[c][c]('console.log("WTF?")'); // -> [Function: anonymous]
// Et ensuite appelle cette fonction anonyme
// Le résultat est un retour de console avec la chaîne "WTF?"
c[c][c]('console.log("WTF?")')(); // > "WTF?"Un Object.prototype.constructor renvoie une référence à la fonction constructeur Object qui a créé l'instance de l'objet. Dans le cas des chaînes, il s'agit de String, dans le cas des nombres, il s'agit de Number, et ainsi de suite.
Object en tant que clé de la propriété d'un objet
{ [{}]: {} } // -> { '[object Object]': {} }💡 Explication :
Pourquoi est-ce que ça marche ? Ici, nous utilisons un Computed property name. Quand vous passez un objet entre ces crochets, l'objet est forcé de devenir une chaîne, alors nous obtenons la clé [objet Object] et la valeur {}.
Nous pouvons créer des enfers de crochets et de parenthèses comme dans l'exemple ci-dessous :
({ [{}]: { [{}]: {} } }[{}][{}]); // -> {}
// structure:
// {
// '[object Object]': {
// '[object Object]': {}
// }
// }En savoir plus sur les objets littéraux ici:
Accéder aux prototypes avec `__proto__`
Comme nous le savons, les primitives n’ont pas de prototypes. Cependant, si nous essayons d'obtenir une valeur de __proto__ pour les primitives, nous obtiendrions ceci :
(1).__proto__.__proto__.__proto__; // -> null💡 Explication :
Cela se produit car lorsque quelque chose n'a pas de prototype, il sera encapsulé dans un objet à l'aide de la méthode ToObject. Donc, étape par étape :
(1)
.__proto__(
// -> [Number: 0]
1
)
.__proto__.__proto__(
// -> {}
1
).__proto__.__proto__.__proto__; // -> nullVoici plus d'infos sur __proto__ :
`` `${{Object}}` ``
Quel est le résultat de l'expression ci-dessous ?
`${{ Object }}`;La réponse est :
// -> '[object Object]'💡 Explication :
Nous avons défini un objet avec une propriété Object en utilisant une propriété de notation raccourcie :
{
Object: Object;
}Ensuite, nous avons passé cet objet au litéral de gabarit, ce qui fait que la méthode toString appelle donc cet objet. Voilà pourquoi nous obtenons la chaîne '[object Object]'.
Déstructuration avec des valeurs par défaut
Considérez cet exemple :
let x,
{ x: y = 1 } = { x };
y;L'exemple ci-dessus est une bonne question pour un entretien. Quelle est la valeur de y ? La réponse est :
// -> 1💡 Explication :
let x,
{ x: y = 1 } = { x };
y;
// ↑ ↑ ↑ ↑
// 1 3 2 4Avec l'exemple ci-dessus :
- On déclare
xsans valeur, donc sa valeur estundefined. - Ensuite, nous intégrons la valeur de
xdans la propriété de l'objetx. - Ensuite, nous extrayons la valeur de
xen utilisant la déstructuration pour l'assigner ày. Si la valeur n'est pas déinie, nous utiliserons1comme valeur par défaut. - Retourne la valeur de
y.
- Initialisateur d'objet sur MDN.
Points et propagation
Des exemples intéressants pourraient être composés avec la propagation de tableaux. Considérez cela :
[...[..."..."]].length; // -> 3💡 Explication :
Pourquoi 3 ? Lorsque nous utilisons le spread operator, la méthode @@iterator est appelée et l'itérateur renvoyé est utilisé pour obtenir les valeurs à itérer. L'itérateur par défaut pour une chaîne étend une chaîne en caractères. Après sa propagation, ces caractères sont empaquetés dans un tableau. Ensuite, ce tableau est à nouveau propagé et empaqueté encore une fois dans un tableau.
Une chaîne '...' est composée de trois caractères ., donc, la longueur du tableau résultant est de 3.
Maintenant, étape par étape :
[...'...'] // -> [ '.', '.', '.' ]
[...[...'...']] // -> [ '.', '.', '.' ]
[...[...'...']].length // -> 3Évidemment, nous pouvons étendre et encapsuler les éléments d’un tableau autant de fois que nous le souhaitons :
[...'...'] // -> [ '.', '.', '.' ]
[...[...'...']] // -> [ '.', '.', '.' ]
[...[...[...'...']]] // -> [ '.', '.', '.' ]
[...[...[...[...'...']]]] // -> [ '.', '.', '.' ]
// etc.Étiquettes
Peu de programmeurs connaissent les étiquettes en JavaScript. Elles sont plutôt intéressantes :
foo: {
console.log("first");
break foo;
console.log("second");
}
// -> first
// -> undefined💡 Explication :
La déclaration étiquetée est utilisée avec les déclarations break ou continue. Vous pouvez utiliser une étiquette pour identifier une boucle, puis ensuite, utiliser la déclaration break ou continue pour indiquer si un programme doit interrompre la boucle ou poursuivre son exécution.
Dans l'exemple ci-dessus, nous identifions une étiquette foo. Ensuite, console.log('first'); est exécuté et l'exécution est interrompue.
En savoir plus sur les étiquettes en JavaScript :
- 13.13 Labelled Statements
- Label sur MDN.
Étiquettes imbriquées
a: b: c: d: e: f: g: 1, 2, 3, 4, 5; // -> 5💡 Explication :
Similaire aux exemples précédents, suivez ces liens pour plus d'infos :
`Try...catch` insidieux
Que retournera cette expression ? 2 ou 3 ?
(() => {
try {
return 2;
} finally {
return 3;
}
})();La réponse est 3. Surprenant ?
💡 Explication :
Est-ce un héritage multiple ?
Regardez l'exemple ci-dessous :
new class F extends (String, Array) {}(); // -> F []Est-ce un héritage multiple ? Non.
💡 Explication :
L'élément intéressant est la valeur de la clause extends ((String, Array)). L'opérateur de groupement retourne toujours son dernier argument, donc (String, Array) est en réalité simplement Array. Cela signifie que nous venons de créer une classe qui extends Array.
Un générateur qui se `yield` lui-même
Considérez cet exemple de générateur qui se yield lui-même :
(function* f() {
yield f;
})().next();
// -> { value: [GeneratorFunction: f], done: false }Comme vous pouvez le constater, la valeur renvoyée est un objet dont la valeur est égale à f. Dans ce cas, nous pouvons faire quelque chose semblable à ça :
(function* f() {
yield f;
})()
.next()
.value()
.next()(
// -> { value: [GeneratorFunction: f], done: false }
// and again
function* f() {
yield f;
}
)()
.next()
.value()
.next()
.value()
.next()(
// -> { value: [GeneratorFunction: f], done: false }
// and again
function* f() {
yield f;
}
)()
.next()
.value()
.next()
.value()
.next()
.value()
.next();
// -> { value: [GeneratorFunction: f], done: false }
// and so on
// …💡 Explication :
Pour comprendre pourquoi cela fonctionne ainsi, lisez ces sections de la spécification :
Une classe de classe
Considérez cette syntaxe obfusquée :
typeof new class {
class() {}
}(); // -> "object"Il semblerait que nous déclarions une classe à l'intérieur d'une classe. Cela devrait être une erreur, cependant, nous obtenons la chaîne "object".
💡 Explication :
Depuis ECMAScript 5, les mots clés sont autorisés en tant que noms de propriétés. Réfléchissez comment vous le feriez pour cet exemple d'objet simple :
const foo = {
class: function() {}
};Et les définitions de méthode abrégée standard d'ES6. Aussi, les classes peuvent être anonymes. Donc, si nous supprimons la partie : function, nous obtiendrons :
class {
class() {}
}Le résultat d'une classe par défaut est toujours un objet simple. Et son typeof devrait retourner "object".
Plus d'infos ici :
Objets incoercibles
Avec des symboles bien connus, il existe un moyen de se débarrasser de la coercition de type. Examinez l'exemple ci-dessous :
function nonCoercible(val) {
if (val == null) {
throw TypeError(
"inCoercible ne doit pas être appelé avec null ou undefined"
);
}
const res = Object(val);
res[Symbol.toPrimitive] = () => {
throw TypeError("Tente de transformer un objet incoercible");
};
return res;
}Maintenant, nous pouvons l'utiliser de cette manière :
// objets
const foo = nonCoercible({ foo: "foo" });
foo * 10; // -> TypeError: Tente de transformer un objet incoercible
foo + "evil"; // -> TypeError: Tente de transformer un objet incoercible
// chaînes
const bar = nonCoercible("bar");
bar + "1"; // -> TypeError: Tente de transformer un objet incoercible
bar.toString() + 1; // -> bar1
bar === "bar"; // -> false
bar.toString() === "bar"; // -> true
bar == "bar"; // -> TypeError: Tente de transformer un objet incoercible
// nombres
const baz = nonCoercible(1);
baz == 1; // -> Tente de transformer un objet incoercible
baz === 1; // -> false
baz.valueOf() === 1; // -> true💡 Explication :
Fonctions fléchées complexes
Considérez l'exemple ci-dessous :
let f = () => 10;
f(); // -> 10D'accord, d'accord, mais qu'en est-il de celui-ci :
let f = () => {};
f(); // -> undefined💡 Explication :
Vous pourriez vous attendre à obtenir {} au lieu d'undefined. C'est dû au fait que les accolades font partie de la syntaxe des fonctions fléchées; f renverra donc undefined. Il est cependant possible de renvoyer l'objet {} directement à partir d'une fonction fléchée en mettant la valeur de retour entre parenthèses :
let f = () => ({});
f(); // -> {}Les fonctions fléchées ne peuvent pas être un constructeur
Considérez l'exemple ci-dessous :
let f = function() {
this.a = 1;
};
new f(); // -> { 'a': 1 }Maintenant, essayez de faire la même chose avec une fonction fléchée :
let f = () => {
this.a = 1;
};
new f(); // -> TypeError: f is not a constructor💡 Explication :
Les fonctions fléchées ne peuvent pas être utilisées en tant que constructeurs et produiront une erreur si elles sont utilisées avec new parce qu'elles ont un lexical this et pas de propriété prototype, cela n'aurait donc pas beaucoup de sens.
`arguments` et fonctions fléchées
Considérez l'exemple ci-dessous :
let f = function() {
return arguments;
};
f("a"); // -> { '0': 'a' }Maintenant, essayez de faire la même chose avec une fonction fléchée :
let f = () => arguments;
f("a"); // -> Uncaught ReferenceError: arguments is not defined💡 Explication :
Les fonctions fléchées sont une version allégée des fonctions standards dans laquelle l'accent est mis sur la taille et le lexical this. En même temps, les fonctions fléchées ne fournissent pas de liaison pour l'objet arguments. Comme alternative valable, utilisez les paramètres rest pour obtenir le même résultat :
let f = (...args) => args;
f("a");- Fonctions fléchées sur MDN.
Retour difficile
La déclaration return est compliquée aussi. Considérez ceci :
(function() {
return;
{
b: 10;
}
})(); // -> undefined💡 Explication :
return et l'expression renvoyée doivent être sur la même ligne :
(function() {
return {
b: 10
};
})(); // -> { b: 10 }Cela est dû au concept appelé "Insertion Automatique du Point-Virgule", qui insère automatiquement des points-virgules à la fin de la plupart des nouvelles lignes. Dans le premier exemple, un point-virgule est inséré entre return et l'objet littéral. La fonction renvoie donc undefined et l'objet littéral n'est jamais évalué.
Chaînage d'affectations sur un objet
var foo = { n: 1 };
var bar = foo;
foo.x = foo = { n: 2 };
foo.x; // -> undefined
foo; // -> {n: 2}
bar; // -> {n: 1, x: {n: 2}}De droite à gauche, {n: 2} est affecté à foo, et le résultat de cette affectation {n: 2} est affecté à foo.x. C'est pourquoi bar retourne {n: 1, x: {n: 2}}, puisque bar est une référence à foo. Mais pourquoi foo.x retourne undefined, alors que ce n'est pas le cas pour bar.x ?
💡 Explication :
foo et bar font référence au même objet {n: 1}, et les lvalues sont résolues avant les assignations. foo = {n: 2} crée un nouvel objet, et donc foo est mis à jour pour référencer ce nouvel objet. L'astuce ici est foo dans foo.x = …, car une lvalue a été résolue au préalable et fait toujours référence à l'objet précédent foo = {n: 1} et donc le met à jour en ajoutant la valeur x.
Après cette chaîne d'assignation, bar, quant à lui, fait toujours référence à l'ancien objet foo, alors que foo fait référence au nouvel objet {n: 2}, où x n'existe pas.
C'est équivalent à :
var foo = { n: 1 };
var bar = foo;
foo = { n: 2 }; // -> {n: 2}
bar.x = foo; // -> {n: 1, x: {n: 2}}
// `bar.x` pointe sur l'adresse du nouvel objet `foo`
// ce n'est pas équivalent à : `bar.x = {n: 2}`Accéder aux propriétés d'un objet avec des tableaux
var obj = { property: 1 };
var array = ["property"];
obj[array]; // -> 1Qu'en est-il des tableaux pseudo-multidimensionnels ?
var map = {};
var x = 1;
var y = 2;
var z = 3;
map[[x, y, z]] = true;
map[[x + 10, y, z]] = true;
map["1,2,3"]; // -> true
map["11,2,3"]; // -> true💡 Explication :
L'opérateur [] convertit l'expression passée en utilisant toString. Transformer un tableau composé d'un seul élément vers une chaîne est similaire à transformer l'élément du tableau en chaîne :
["property"].toString(); // -> 'property'Opérateurs `null` et relationnels
null > 0; // false
null == 0; // false
null >= 0; // true💡 Explication :
En bref, si null < 0 est false, alors null >= 0 est true. Lisez une explication détaillée ici.
`Number.toFixed()` affiche différents nombres
Number.toFixed() peut se comporter étrangement selon le navigateur utilisé. Voir cet exemple :
(0.7875).toFixed(3);
// Firefox: -> 0.787
// Chrome: -> 0.787
// IE11: -> 0.788
(0.7876).toFixed(3);
// Firefox: -> 0.788
// Chrome: -> 0.788
// IE11: -> 0.788💡 Explication :
Alors que votre premier instinct peut être que IE11 a correct et que Firefox et Chrome ont faux, la réalité est que Firefox et Chrome obéissent plus directement aux normes relatives aux nombres (IEEE-754 Floating Point), alors qu'IE11 les désobéit minutieusement dans (ce qui est probablement) un effort de donner des résultats plus clairs.
Vous pouvez voir pourquoi cela se produit avec quelques tests rapides :
// Confirme le résultat curieux d'arrondir `5` vers le bas
(0.7875).toFixed(3); // -> 0.787
// Il semble que ce soit juste `5` lorsque vous développez
// les limites de la précision de flottement de 64 bits (double précision)
(0.7875).toFixed(14); // -> 0.78750000000000
// Mais que se passe-t-il si vous allez au-delà de la limite ?
(0.7875).toFixed(20); // -> 0.78749999999999997780Les nombres à virgule flottante ne sont pas stockés sous forme de liste de chiffres décimaux en interne, mais par le biais d'une méthodologie plus complexe qui produit de minuscules inexactitudes qui sont généralement arrondies par des appels à toString ou des appels similaires, mais qui sont réellement présentes en interne.
Dans ce cas, le 5 sur la fin était en réalité une fraction extrêmement petite, en dessous d'un vrai 5. Si vous arrondissez à une longueur raisonnable, vous obtenez un 5… mais ce n'est en réalité pas un 5 en interne.
Ceci étant dit, IE11 rapportera la valeur entrée uniquement avec des zéros ajoutés à la fin, même dans le cas de toFixed(20), car cela semble forcer la valeur arrondie pour réduire les problèmes liées aux limites matérielles.
Voir pour référence NOTE 2 sur la définition de ECMA-262 pour toFixed.
`Math.max()` est moins que `Math.min()`
Math.min(1, 4, 7, 2); // -> 1
Math.max(1, 4, 7, 2); // -> 7
Math.min(); // -> Infinity
Math.max(); // -> -Infinity
Math.min() > Math.max(); // -> true💡 Explication :
- Why is Math.max() less than Math.min()? par Charlie Harvey.
Comparer `null` à `0`
Les expressions suivantes semblent introduire une contradiction :
null == 0; // -> false
null > 0; // -> false
null >= 0; // -> trueComment null peut-il être ni égal ni supérieur à 0, si null >= 0 est en fait true ? (Ceci fonctionne de la même manière avec inférieur ou égal à (<=).)
💡 Explication :
Les méthodes d'évaluation de ces trois expressions sont toutes différentes et sont responsables de la production de ce comportement inattendu.
Premièrement, la comparaison d'égalité abstraite null == 0. Normalement, si cet opérateur ne peut pas comparer correctement les valeurs d'un côté comme de l'autre, il convertit les deux en nombres et compare ensuite les nombres. Alors, vous pouvez vous attendre au comportement suivant :
// Ce n'est pas ce qui se passe
(null == 0 + null) == +0;
0 == 0;
true;Cependant, suite à une lecture attentive de la spécification, la transformation du nombre n'a pas lieu sur un côté qui est null ou undefined. Par conséquent, si vous avez null sur un des deux côté du signe égal, l'autre côté doit être null ou undefined pour que l'expression retourne true. Comme ce n'est pas le cas, false est renvoyé.
Ensuite, la comparaison relationnelle null > 0. L'algorithme ici, contrairement à celui de l'opérateur d'égalité abstraite, va convertir null à un nombre. Donc, nous obtenons ce comportement :
null > 0
+null = +0
0 > 0
falseFinalement, la comparaison relationnelle null >= 0. Vous pourriez soutenir que cette expression devrait être le résultat de null > 0 || null == 0; si tel était le cas, les résultats ci-dessus signifieraient que ceci serait aussi false. Toutefois, l'opérateur >= fonctionne d'une manière très différente, qui est simplement d'utiliser le contraire de l'opérateur <. Parce que notre exemple ci-dessus, utilisant l'opérateur supérieur à, s'applique également à l'opérateur inférieur à, cela signifie que cette expression est réellement évaluée de la manière suivante :
null >= 0;
!(null < 0);
!(+null < +0);
!(0 < 0);
!false;
true;Même redéclaration d'une variable
JavaScript autorise la redéclaration de variables :
a;
a;
// Ceci est aussi valide
a, a;Et ça fonctionne aussi en mode strict :
var a, a, a;
var a;
var a;💡 Explication :
Toutes les définitions fusionnent en une seule définition.
Comportement par défaut d'`Array.prototype.sort()`
Imaginez que vous ayez besoin de trier un tableau composé de nombres.
[ 10, 1, 3 ].sort() // -> [ 1, 10, 3 ]💡 Explication :
L'ordre de tri par défaut est construit lors de la transformation des éléments en chaînes, puis en comparant leurs séquences de valeurs unitaires sur le jeu de caractères UTF-16.
Indice
Passez comparefn si vous essayez de trier n'importe quoi d'autre qu'une chaîne.
[ 10, 1, 3 ].sort((a, b) => a - b) // -> [ 1, 3, 10 ]📚 Autres ressources
- wtfjs.com — une collection d'irrégularités spéciales très particulières, d'incohérences et de moments terriblement non intuitifs pour le langage du Web.
- Wat — Un discours éclair de Gary Bernhardt de CodeMash 2012.
- What the... JavaScript? — Kyle Simpsons parle des tentatives de Forward 2 pour "sortir de l'absurdité" du JavaScript. Il veut vous aider à produire un code plus propre, plus élégant et plus lisible, puis inspirer les gens à contribuer à la communauté open-source.
🎓 Licence
File: README-hi.md
f\*ck जावास्क्रिप्ट क्या है
मजाकिया और मुश्किल जावास्क्रिप्ट उदाहरणों की एक सूची
जावास्क्रिप्ट एक महान भाषा है। इसका एक सरल वाक्यविन्यास है, एक बड़ा पारिस्थितिकी तंत्र, और, जो सबसे महत्वपूर्ण है, एक महान समुदाय।
उसी समय, हम सभी जानते हैं कि जावास्क्रिप्ट मुश्किल भागों के साथ काफी मज़ेदार भाषा है। उनमें से कुछ हमारी रोज़मर्रा की नौकरी को जल्दी से नरक में बदल सकते हैं, और उनमें से कुछ हमें ज़ोर से हँसा सकते हैं।
WTFJS के लिए मूल विचार है Brian Leroux. यह सूची उनकी बातों से प्रेरित है “WTFJS” at dotJS 2012:
Node पैकेज्ड पांडुलिपि
आप इस हैंडबुक को npm का उपयोग करके स्थापित कर सकते हैं। यहां जाओ:
$ npm install wtfjsअब आपको कमांड लाइन पर wtfjs चलाने में सक्षम होना चाहिए। यह आपके चयनित $ PAGER में मैनुअल को खोलेगा। अन्यथा, आप यहां पढ़ना जारी रख सकते हैं।
स्रोत यहां उपलब्ध है: https://github.com/denysdovhan/wtfjs
Translations
Currently, there are these translations of wtfjs:
Table of Contents
- 💪🏻 प्रेरणा
- ✍🏻 नोटेशन
- 👀 उदाहरण
- [
[]के बराबर` - [
trueis not equal ` - true is false
- baNaNa
NaNis not aNaN- यह विफल है
[]is truthy, but nottruenullis falsy, but notfalsedocument.allis an object, but it is undefined- न्यूनतम मान शून्य से अधिक है
- फंक्शन कोई फंक्शन नहीं है
- Adding arrays
- सरणी में अल्पविराम
- ऐरे समानता एक राक्षस है
undefinedandNumberparseIntएक बुरा आदमी हैसत्यऔरअसत्यके साथ गणित- HTML टिप्पणियाँ जावास्क्रिप्ट में मान्य हैं
NaNisnota number[]andnullare objects- Magically increasing numbers
- Precision of
0.1 + 0.2 - पैचिंग नंबर
- Comparison of three numbers
- मजेदार गणित
- RegExps का जोड़
- स्ट्रिंग्स
स्ट्रिंगके उदाहरण नहीं हैं - बैकटिक्स के साथ कॉलिंग फ़ंक्शन
- पुकार पुकार पुकार
- A
constructorproperty - वस्तु की संपत्ति की कुंजी के रूप में वस्तु
- प्रोटोटाइपिंग को
__proto__के साथ एक्सेस करना `$ {{वस्तु}}`- डिफ़ॉल्ट मानों के साथ विनाशकारी
- डॉट्स और फैल रहा है
- लेबल
- Nested labels
- कपटी
कोशिश..चेक - क्या यह कई विरासत है?
- एक जनरेटर जो खुद उपजता है
- कक्षा का एक वर्ग
- गैर-सहकर्मी वस्तुएं
- मुश्किल तीर कार्य
- एरो फ़ंक्शंस एक निर्माता नहीं हो सकता है
तर्कऔर तीर कार्य- मुश्किल वापसी
- ऑब्जेक्ट पर कार्य असाइन करना
- सरणियों के साथ ऑब्जेक्ट गुण तक पहुँचना
- Null and Relational Operators
Number.toFixed ()विभिन्न संख्याएँ प्रदर्शित करता हैMath.max()less thanMath.min()- तुलना
nullसे0तक - समान परिवर्तनशील पुनर्वितरण
- डिफ़ॉल्ट व्यवहार Array.prototyp.sort ()
- [
- 📚 अन्य संसाधन
- 🎓 License
💪🏻 प्रेरणा
सिर्फ मनोरंजन के लिए
— “सिर्फ मनोरंजन के लिए: एक्सीडेंटल रिवोल्यूशनरी की कहानी”, Linus Torvalds
इस सूची का प्राथमिक लक्ष्य कुछ पागल उदाहरणों को इकट्ठा करना और यह बताना है कि यदि संभव हो तो वे कैसे काम करते हैं। सिर्फ इसलिए कि कुछ ऐसा सीखना मजेदार है जिसे हम पहले नहीं जानते थे।
यदि आप एक शुरुआती हैं, तो आप इन नोटों का उपयोग जावास्क्रिप्ट में गहरा गोता लगाने के लिए कर सकते हैं। मुझे उम्मीद है कि ये नोट्स आपको विनिर्देश पढ़ने में अधिक समय बिताने के लिए प्रेरित करेंगे।
यदि आप एक पेशेवर डेवलपर हैं, तो आप इन उदाहरणों पर सभी उद्धरणों और हमारे प्रिय जावास्क्रिप्ट के अप्रत्याशित किनारों के लिए एक महान संदर्भ के रूप में विचार कर सकते हैं।
किसी भी मामले में, बस इसे पढ़ें। आप शायद कुछ नया खोजने जा रहे हैं।
✍🏻 नोटेशन
// -> एक अभिव्यक्ति का परिणाम दिखाने के लिए प्रयोग किया जाता है. उदाहरण के लिए:
1 + 1; // -> 2// > का परिणाम है console.log या कोई अन्य आउटपुट। उदाहरण के लिए:
console.log("hello, world!"); // > hello, world!// स्पष्टीकरण के लिए इस्तेमाल की जाने वाली टिप्पणी मात्र है। उदाहरण:
// Assigning a function to foo constant
const foo = function() {};👀 उदाहरण
`[]` के बराबर`![]`
array समान नहीं है:
[] == ![]; // -> true💡 व्याख्या:
अमूर्त समानता ऑपरेटर उनकी तुलना करने के लिए दोनों पक्षों को संख्याओं में परिवर्तित करता है और दोनों पक्ष अलग-अलग कारणों से संख्या 0 बन जाते हैं। एरे सत्य हैं, इसलिए दाईं ओर, एक सत्य मूल्य के विपरीत गलत है, जो तब 0 के लिए मजबूर है। बाईं ओर, हालांकि, एक खाली सरणी पहले एक बूलियन बनने के बिना एक संख्या के लिए मजबूर की जाती है, और खाली सरणियों को सत्य होने के बावजूद 0 के लिए मजबूर किया जाता है।
इस प्रकार यह अभिव्यक्ति सरल है:
+[] == +![];
0 == +false;
0 == 0;
true;See also [] is truthy, but not true.
`true` is not equal `![]`, but not equal `[]` too
Array बराबर नहीं है true, लेकिन Array बराबर नहीं है true भी नहीं;
Array बराबर है 'false', Array बराबर है 'false' भी:
true == []; // -> false
true == ![]; // -> false
false == []; // -> true
false == ![]; // -> true💡 व्याख्या:
true == []; // -> false
true == ![]; // -> false
// विनिर्देश के अनुसार
true == []; // -> false
toNumber(true); // -> 1
toNumber([]); // -> 0
1 == 0; // -> false
true == ![]; // -> false
![]; // -> false
true == false; // -> falsefalse == []; // -> true
false == ![]; // -> true
// विनिर्देश के अनुसार
false == []; // -> true
toNumber(false); // -> 0
toNumber([]); // -> 0
0 == 0; // -> true
false == ![]; // -> false
![]; // -> false
false == false; // -> truetrue is false
!!"false" == !!"true"; // -> true
!!"false" === !!"true"; // -> true💡 व्याख्या:
Consider this step-by-step:
// true is 'truthy' and represented by value 1 (number), 'true' in string form is NaN.
true == "true"; // -> false
false == "false"; // -> false
// 'false' is not the empty string, so it's a truthy value
!!"false"; // -> true
!!"true"; // -> truebaNaNa
"b" + "a" + +"a" + "a"; // -> 'baNaNa'यह जावास्क्रिप्ट में एक पुराने स्कूल का मजाक है, लेकिन फिर से बनाया गया है। यहाँ मूल एक है:
"foo" + +"bar"; // -> 'fooNaN'💡 व्याख्या:
अभिव्यक्ति का मूल्यांकन 'फू' + (+ 'बार') के रूप में किया जाता है, जो संख्या को नहीं करने के लिए 'बार'` को परिवर्तित करता है।
`NaN` is not a `NaN`
NaN === NaN; // -> false💡 व्याख्या:
विनिर्देश इस व्यवहार के पीछे तर्क को सख्ती से परिभाषित करता है:
- If
Type(x)is different fromType(y), return false.- If
Type(x)is Number, then
- If
xis NaN, return false.- If
yis NaN, return false.- … … …
Following the definition of NaN from the IEEE:
चार परस्पर अनन्य संबंध संभव हैं: से कम, बराबर, अधिक से अधिक, और अव्यवस्थित। आखिरी मामला तब उठता है जब कम से कम एक ऑपरेंड NaN होता है। प्रत्येक NaN अपने आप सहित हर चीज के साथ अनियंत्रित की तुलना करेगा।
— “IEEE754 NaN मानों के लिए झूठी वापसी करने वाली सभी तुलनाओं के लिए तर्क क्या है?” at StackOverflow
यह विफल है
आपको विश्वास नहीं होगा, लेकिन …
(![] + [])[+[]] +
(![] + [])[+!+[]] +
([![]] + [][[]])[+!+[] + [+[]]] +
(![] + [])[!+[] + !+[]];
// -> 'fail'💡 व्याख्या:
प्रतीकों के उस द्रव्यमान को टुकड़ों में तोड़कर, हम देखते हैं कि निम्न पैटर्न अक्सर होता है:
![] + []; // -> 'false'
![]; // -> falseइसलिए हम [] को गलत में जोड़ने का प्रयास करते हैं। लेकिन कई आंतरिक फ़ंक्शन कॉल के कारण (बाइनरी + ऑपरेटर -> ToPrimitive -> [[DefaultValue]]) हम एक स्ट्रिंग के लिए सही ऑपरेंड को परिवर्तित करते हैं:
![] + [].toString(); // 'false'एक स्ट्रिंग के रूप में एक सरणी के रूप में सोचकर हम इसके पहले चरित्र तक पहुंच सकते हैं [0]:
"false"[0]; // -> 'f'बाकी स्पष्ट है, लेकिन i मुश्किल है। विफल 'में i स्ट्रिंग को उत्पन्न करके' 'falseundefined' को पकड़ा जाता है और सूचकांक पर तत्व को पकड़ा जाता है [' 10 ']'
`[]` is truthy, but not `true`
एक सरणी एक सत्य मूल्य है, हालांकि, यह सत्य के बराबर नहीं है।
!![] // -> true
[] == true // -> false💡 व्याख्या:
यहाँ ECMA-262 विनिर्देश में संबंधित वर्गों के लिंक दिए गए हैं:
`null` is falsy, but not `false`
इस तथ्य के बावजूद कि null एक मिथ्या मूल्य है, यह false के बराबर नहीं है।
!!null; // -> false
null == false; // -> falseइसी समय, अन्य झूठे मूल्य, जैसे 0 या `` `` झूठ के बराबर हैं।
0 == false; // -> true
"" == false; // -> true💡 व्याख्या:
विवरण पिछले उदाहरण के समान है। यहाँ इसी लिंक है:
`document.all` is an object, but it is undefined
⚠️ यह ब्राउज़र API का हिस्सा है और यह Node.js वातावरण में काम नहीं करेगा⚠️
इस तथ्य के बावजूद कि document.all एक सरणी जैसी वस्तु है और यह पृष्ठ में DOM नोड्स तक पहुँच प्रदान करता है, यह टाइपोफ फ़ंक्शन को अपरिभाषित के रूप में प्रतिक्रिया देता है।
document.all instanceof Object; // -> true
typeof document.all; // -> 'undefined'इसी समय, document.all`` अपरिभाषित के बराबर नहीं है।
document.all === undefined; // -> false
document.all === null; // -> falseBut at the same time:
document.all == null; // -> true💡 व्याख्या:
document.allविशेष रूप से IE के पुराने संस्करणों के साथ DOM तत्वों को एक्सेस करने का एक तरीका हुआ करता था। हालांकि यह कभी भी एक मानक नहीं रहा है, लेकिन इसका इस्तेमाल वृद्धावस्था के जेएस कोड में किया जाता था। जब मानक नए एपीआई (जैसेdocument.getElementById) के साथ आगे बढ़ा, तो यह एपीआई कॉल अप्रचलित हो गई और मानक समिति को तय करना था कि इसके साथ क्या करना है। इसके व्यापक उपयोग के कारण उन्होंने एपीआई रखने का फैसला किया, लेकिन जावास्क्रिप्ट विनिर्देश के विलफुल उल्लंघन का परिचय दिया।
इसका उपयोग करने पर कारणझूठका जवाब देता है Strict Equality Comparison withundefinedwhiletruewhen using the Abstract Equality Comparison स्पष्ट रूप से अनुमति देता है कि विनिर्देश के विलफुल उल्लंघन के कारण है।— “Obsolete features - document.all” at WhatWG - HTML spec
— “Chapter 4 - ToBoolean - Falsy values” at YDKJS - Types & Grammar
न्यूनतम मान शून्य से अधिक है
Number.MIN_VALUE सबसे छोटी संख्या है, जो शून्य से अधिक है:
Number.MIN_VALUE > 0; // -> true💡 व्याख्या:
नंबर .IN_VALUE`` 5e-324है, यानी सबसे छोटी धनात्मक संख्या जिसे फ्लोट प्रिसिजन के भीतर दर्शाया जा सकता है, यानी कि आप शून्य के जितना करीब हो सकते हैं। यह सबसे अच्छे रिज़ॉल्यूशन को परिभाषित करता है जो तैरता है जो आपको दे सकता हैअब कुल मिलाकर सबसे छोटा मूल्य है
नंबर.नैगेटिव_इनफिनिटीहालांकि यह एक सख्त अर्थ में वास्तव में संख्यात्मक नहीं है।— “Why is
0less thanNumber.MIN_VALUEin JavaScript?” at StackOverflow
फंक्शन कोई फंक्शन नहीं है
⚠️ V8 v5.5 या निम्न में मौजूद बग (Node.js <= 7)⚠️
आप सभी को पता है कि कष्टप्रद undefined एक function नहीं है, लेकिन इस बारे में क्या?
// Declare a class which extends null
class Foo extends null {}
// -> [Function: Foo]
new Foo() instanceof null;
// > TypeError: function is not a function
// > at … … …💡 व्याख्या:
यह स्पेसिफिकेशन का हिस्सा नहीं है। यह केवल एक बग है जिसे अब ठीक कर दिया गया है, इसलिए भविष्य में इसके साथ कोई समस्या नहीं होनी चाहिए।
Adding arrays
यदि आप दो सरणियों को जोड़ने का प्रयास करते हैं तो क्या होगा?
[1, 2, 3] + [4, 5, 6]; // -> '1,2,34,5,6'💡 व्याख्या:
संघात होता है। चरण-दर-चरण, ऐसा दिखता है:
[1, 2, 3] +
[4, 5, 6][
// call toString()
(1, 2, 3)
].toString() +
[4, 5, 6].toString();
// concatenation
"1,2,3" + "4,5,6";
// ->
("1,2,34,5,6");सरणी में अल्पविराम
आपने 4 खाली तत्वों के साथ एक सरणी बनाई है। सभी के बावजूद, आपको अल्पविराम के कारण तीन तत्वों के साथ एक सरणी मिलेगी:
let a = [, , ,];
a.length; // -> 3
a.toString(); // -> ',,'💡 व्याख्या:
Trailing commas (कभी-कभी "अंतिम कॉमा" कहा जाता है) जावास्क्रिप्ट कोड में नए तत्वों, मापदंडों या गुणों को जोड़ते समय उपयोगी हो सकता है। यदि आप एक नई संपत्ति जोड़ना चाहते हैं, तो आप बस पिछली पंक्ति को संशोधित किए बिना एक नई रेखा जोड़ सकते हैं यदि वह रेखा पहले से ही एक अल्पविराम का उपयोग करती है। इससे संस्करण-नियंत्रण अलग-अलग हो जाता है और एडिटिंग कोड कम परेशानी वाला हो सकता है।
— Trailing commas at MDN
ऐरे समानता एक राक्षस है
JS में Array समानता एक राक्षस है, जैसा कि आप नीचे देख सकते हैं:
[] == '' // -> true
[] == 0 // -> true
[''] == '' // -> true
[0] == 0 // -> true
[0] == '' // -> false
[''] == 0 // -> true
[null] == '' // true
[null] == 0 // true
[undefined] == '' // true
[undefined] == 0 // true
[[]] == 0 // true
[[]] == '' // true
[[[[[[]]]]]] == '' // true
[[[[[[]]]]]] == 0 // true
[[[[[[ null ]]]]]] == 0 // true
[[[[[[ null ]]]]]] == '' // true
[[[[[[ undefined ]]]]]] == 0 // true
[[[[[[ undefined ]]]]]] == '' // true💡 व्याख्या:
आपको उपरोक्त उदाहरणों के लिए बहुत सावधानी से देखना चाहिए! व्यवहार अनुभाग में वर्णित है7.2.13 Abstract Equality Comparison of the specification.
`undefined` and `Number`
यदि हम नंबर कंस्ट्रक्टर में कोई तर्क नहीं देते हैं, तो हम 0 प्राप्त करेंगे। मान 'अपरिभाषित' को औपचारिक तर्कों के लिए सौंपा गया है जब कोई वास्तविक तर्क नहीं हैं, तो आप उम्मीद कर सकते हैं कि बिना तर्क के संख्या अपने पैरामीटर के मान के रूप मेंअपरिभाषित 'लेता है। हालाँकि, जब हमअपरिभाषित पास करते हैं, तो हम NaN` प्राप्त करेंगे।
Number(); // -> 0
Number(undefined); // -> NaN💡 व्याख्या:
विनिर्देश के अनुसार:
- यदि इस फ़ंक्शन के आह्वान पर कोई तर्क नहीं दिया गया, तो
nको+ 0होने दें। - और, चलो
nहो?ToNumber (मान)। अपरिभाषितके मामले में,ToNumber (अपरिभाषित)कोNaNवापस करना चाहिए।
Here's the corresponding section:
`parseInt` एक बुरा आदमी है
parseInt अपने quirks द्वारा प्रसिद्ध है:
parseInt("f*ck"); // -> NaN
parseInt("f*ck", 16); // -> 15💡 व्याख्या: ऐसा इसलिए होता है क्योंकि parseInt चरित्र-दर-चरित्र को तब तक जारी रखेगा, जब तक कि वह एक चरित्र को हिट न कर दे, जो उसे पता नहीं है। F in 'f * ck' हेक्साडेसिमल अंक 15 है।
पूर्णांक के लिए इन्फिनिटी को पार्स करना कुछ…
//
parseInt("Infinity", 10); // -> NaN
// ...
parseInt("Infinity", 18); // -> NaN...
parseInt("Infinity", 19); // -> 18
// ...
parseInt("Infinity", 23); // -> 18...
parseInt("Infinity", 24); // -> 151176378
// ...
parseInt("Infinity", 29); // -> 385849803
parseInt("Infinity", 30); // -> 13693557269
// ...
parseInt("Infinity", 34); // -> 28872273981
parseInt("Infinity", 35); // -> 1201203301724
parseInt("Infinity", 36); // -> 1461559270678...
parseInt("Infinity", 37); // -> NaNBe careful with parsing null too:
parseInt(null, 24); // -> 23💡 व्याख्या:
यह
nullको स्ट्रिंगnull”में परिवर्तित कर रहा है और इसे परिवर्तित करने का प्रयास कर रहा है। 23 के माध्यम से मूलांक 0 के लिए, ऐसे कोई अंक नहीं हैं जो इसे रूपांतरित कर सकते हैं, इसलिए यह NaN लौटाता है। 24 में, 14 वें अक्षर `` एन '', को अंक प्रणाली में जोड़ा जाता है। 31 पर, 21 वें अक्षर `` यू '' को जोड़ा जाता है और पूरे स्ट्रिंग को डिकोड किया जा सकता है। 37 पर अब कोई वैध अंक सेट नहीं है जो उत्पन्न किया जा सकता है औरNaN` को लौटा दिया जाता है।— “parseInt(null, 24) === 23… wait, what?” at StackOverflow
अष्टक के बारे में मत भूलना:
parseInt("06"); // 6
parseInt("08"); // 8 if support ECMAScript 5
parseInt("08"); // 0 if not support ECMAScript 5**💡 व्याख्या:**यदि इनपुट स्ट्रिंग "0" से शुरू होती है, तो मूलांक आठ (अष्टक) या 10 (दशमलव) है। वास्तव में जो मूलांक चुना गया है वह कार्यान्वयन-निर्भर है। ECMAScript 5 निर्दिष्ट करता है कि 10 (दशमलव) का उपयोग किया जाता है, लेकिन सभी ब्राउज़र अभी तक इसका समर्थन नहीं करते हैं। इस कारण से parseInt का उपयोग करते समय हमेशा एक मूलांक निर्दिष्ट करें।
parseInt always convert input to string:
parseInt({ toString: () => 2, valueOf: () => 1 }); // -> 2
Number({ toString: () => 2, valueOf: () => 1 }); // -> 1फ़्लोटिंग पॉइंट मानों को पार्स करते समय सावधान रहें
parseInt(0.000001); // -> 0
parseInt(0.0000001); // -> 1
parseInt(1 / 1999999); // -> 5💡 व्याख्या: ParseInt एक स्ट्रिंग तर्क लेता है और निर्दिष्ट मूलांक का पूर्णांक देता है। ParseInt भी स्ट्रिंग पैरामीटर में पहले गैर-अंक के बाद और सहित कुछ भी स्ट्रिप्स। 0.000001 को स्ट्रिंग में परिवर्तित किया जाता है 0.000001 और पार्सेइंट`` 0 देता है। जब 0.0000001 को एक स्ट्रिंग में परिवर्तित किया जाता है तो इसे'1e-7' 'के रूप में माना जाता है और इसलिएparseInt 1` देता है। `1 / 1999999` की व्याख्या` 5.00000250000125e-7` और `पार्सेइंट 5` के रूप में की जाती है।
`सत्य` और` असत्य` के साथ गणित
चलो कुछ गणित करते हैं:
true +
true(
// -> 2
true + true
) *
(true + true) -
true; // -> 3हममम ... 🤔
💡 व्याख्या:
हम मानों को 'संख्या' निर्माता के साथ संख्याओं में भिन्न कर सकते हैं। यह काफी स्पष्ट है कि सच`` 1 के लिए मजबूर किया जाएगा:
Number(true); // -> 1यूनीरी प्लस ऑपरेटर अपने मूल्य को एक संख्या में बदलने का प्रयास करता है। यह पूर्णांकों और फ़्लोट्स के स्ट्रिंग अभ्यावेदन को परिवर्तित कर सकता है, साथ ही साथ गैर-स्ट्रिंग मान सत्य, असत्य, और अशक्त। यदि यह किसी विशेष मूल्य को पार्स नहीं कर सकता है, तो यह NaN का मूल्यांकन करेगा। इसका मतलब है कि हम सच को 1 आसान कर सकते हैं:
+true; // -> 1जब आप जोड़ या गुणा कर रहे होते हैं, तो ToNumber विधि लागू होती है। विनिर्देश के अनुसार, यह विधि वापस आती है:
If
argumentis true, return 1. Ifargumentis false, return +0.
इसलिए हम बूलियन मूल्यों को नियमित संख्या के रूप में जोड़ सकते हैं और सही परिणाम प्राप्त कर सकते हैं।
संगत अनुभाग:
HTML टिप्पणियाँ जावास्क्रिप्ट में मान्य हैं
आप प्रभावित होंगे, लेकिन <! - (जिसे HTML टिप्पणी के रूप में जाना जाता है) जावास्क्रिप्ट में एक मान्य टिप्पणी है।
// valid comment
<!-- valid comment too💡 व्याख्या:
Impressed? HTML जैसी टिप्पणियों का उद्देश्य उन ब्राउज़रों को अनुमति देना था जो समझ में नहीं आते थे<script> सुंदर ढंग से नीचा दिखाने के लिए टैग। ये ब्राउज़र, उदा। नेटस्केप 1.x अब लोकप्रिय नहीं हैं। तो अब आपके स्क्रिप्ट टैग में HTML कमेंट्स डालने का कोई मतलब नहीं है।
चूंकि Node.js V8 इंजन पर आधारित है, इसलिए HTML- जैसी टिप्पणियां Node.js रनटाइम द्वारा भी समर्थित हैं। इसके अलावा, वे विनिर्देश का एक हिस्सा हैं:
`NaN` is ~~not~~ a number
Type of NaN is a 'number':
typeof NaN; // -> 'number'💡 व्याख्या:
व्याख्याs of how typeof and instanceof operators work:
`[]` and `null` are objects
typeof []; // -> 'object'
typeof null; // -> 'object'
// however
null instanceof Object; // false💡 व्याख्या:
विनिर्देशन के इस भाग में `टाइपोफ़ 'ऑपरेटर के व्यवहार को परिभाषित किया गया है:
विनिर्देशन के अनुसार, टाइपऑफ़ ऑपरेटर [तालिका 35:टाइपोफ़ऑपरेटर परिणाम] (https://www.ecma-international.org/ecma-262/#table-35) के अनुसार एक स्ट्रिंग लौटाता है। Null, साधारण, मानक विदेशी और गैर-मानक विदेशी वस्तुओं के लिए, जो[[कॉल]]को लागू नहीं करते हैं, यह स्ट्रिंग ऑब्जेक्ट`` लौटाता है।
हालाँकि, आप toString विधि का उपयोग करके किसी ऑब्जेक्ट के प्रकार की जाँच कर सकते हैं।
Object.prototype.toString.call([]);
// -> '[object Array]'
Object.prototype.toString.call(new Date());
// -> '[object Date]'
Object.prototype.toString.call(null);
// -> '[object Null]'Magically increasing numbers
999999999999999; // -> 999999999999999
9999999999999999; // -> 10000000000000000
10000000000000000; // -> 10000000000000000
10000000000000000 + 1; // -> 10000000000000000
10000000000000000 + 1.1; // -> 10000000000000002💡 व्याख्या:
यह बाइनरी फ्लोटिंग-पॉइंट अंकगणित के लिए IEEE 754-2008 मानक के कारण होता है। इस पैमाने पर, यह निकटतम सम संख्या में गोल होता है। अधिक पढ़ें:
- 6.1.6 The Number Type
- IEEE 754 on Wikipedia
Precision of `0.1 + 0.2`
A well-known joke. An addition of 0.1 and 0.2 is deadly precise:
0.1 +
0.2(
// -> 0.30000000000000004
0.1 + 0.2
) ===
0.3; // -> false💡 व्याख्या:
The answer for the ”Is floating point math broken?” question on StackOverflow:
आपके कार्यक्रम में स्थिरांक
0.2और0.3भी उनके वास्तविक मूल्यों के सन्निकटन होंगे। ऐसा होता है कि निकटतमडबलसे0.2तर्कसंगत संख्या0.2से बड़ा है, लेकिन निकटतमडबलसे0.3तर्कसंगत संख्या0.3से छोटा है।0.1और0.2हवाओं का योग तर्कसंगत संख्या0.3से बड़ा है और इसलिए आपके कोड में निरंतरता से असहमत है।
यह समस्या इतनी ज्ञात है कि यहां तक कि एक वेबसाइट भी है जिसका नाम [0.30000000000000004.com] (http://0.30000000000000004.com/) है। यह हर भाषा में होता है जो फ़्लोटिंग पॉइंट गणित का उपयोग करता है, न कि केवल जावास्क्रिप्ट।
पैचिंग नंबर
आप रैपर ऑब्जेक्ट्स को नंबर या स्ट्रिंग जैसे तरीकों से जोड़ सकते हैं।
Number.prototype.isOne = function() {
return Number(this) === 1;
};
(1.0).isOne(); // -> true
(1).isOne(); // -> true
(2.0)
.isOne()(
// -> false
7
)
.isOne(); // -> false💡 व्याख्या:
जाहिर है, आप जावास्क्रिप्ट में किसी भी अन्य ऑब्जेक्ट की तरह नंबर ऑब्जेक्ट का विस्तार कर सकते हैं। हालाँकि, यह अनुशंसित नहीं है यदि परिभाषित पद्धति का व्यवहार विनिर्देश का हिस्सा नहीं है। यहाँ नंबर की संपत्तियों की सूची दी गई है:
Comparison of three numbers
1 < 2 < 3; // -> true
3 > 2 > 1; // -> false💡 व्याख्या:
इस तरह से काम क्यों करता है? खैर, समस्या एक अभिव्यक्ति के पहले भाग में है। यहां देखिए यह कैसे काम करता है:
1 < 2 < 3; // 1 < 2 -> true
true < 3; // true -> 1
1 < 3; // -> true
3 > 2 > 1; // 3 > 2 -> true
true > 1; // true -> 1
1 > 1; // -> falseहम इसे ठीक कर सकते हैं Greater than or equal operator (>=):
3 > 2 >= 1; // trueविनिर्देश में रिलेशनल ऑपरेटरों के बारे में अधिक पढ़ें:
मजेदार गणित
अक्सर जावास्क्रिप्ट में अंकगणितीय संचालन के परिणाम काफी अप्रत्याशित हो सकते हैं। इन उदाहरणों पर विचार करें:
3 - 1 // -> 2
3 + 1 // -> 4
'3' - 1 // -> 2
'3' + 1 // -> '31'
'' + '' // -> ''
[] + [] // -> ''
{} + [] // -> 0
[] + {} // -> '[object Object]'
{} + {} // -> '[object Object][object Object]'
'222' - -'111' // -> 333
[4] * [4] // -> 16
[] * [] // -> 0
[4, 4] * [4, 4] // NaN💡 व्याख्या:
पहले चार उदाहरणों में क्या हो रहा है? जावास्क्रिप्ट में अतिरिक्त समझने के लिए यहां एक छोटी तालिका दी गई है:
Number + Number -> addition
Boolean + Number -> addition
Boolean + Boolean -> addition
Number + String -> concatenation
String + Boolean -> concatenation
String + String -> concatenationअन्य उदाहरणों के बारे में क्या? एक ToPrimitive और ToString तरीकों को इसके अलावा [] और `{} के लिए निहित किया जा रहा है। विनिर्देश में मूल्यांकन प्रक्रिया के बारे में और पढ़ें:
- 12.8.3 The Addition Operator (
+) - 7.1.1 ToPrimitive(
input[,PreferredType]) - 7.1.12 ToString(
argument)
RegExps का जोड़
क्या आप जानते हैं कि आप इस तरह से नंबर जोड़ सकते हैं?
// Patch a toString method
RegExp.prototype.toString =
function() {
return this.source;
} /
7 /
-/5/; // -> 2💡 व्याख्या:
स्ट्रिंग्स `स्ट्रिंग` के उदाहरण नहीं हैं
"str"; // -> 'str'
typeof "str"; // -> 'string'
"str" instanceof String; // -> false💡 व्याख्या:
स्ट्रिंग कंस्ट्रक्टर एक स्ट्रिंग लौटाता है:
typeof String("str"); // -> 'string'
String("str"); // -> 'str'
String("str") == "str"; // -> trueLet's try with a new:
new String("str") == "str"; // -> true
typeof new String("str"); // -> 'object'वस्तु? वह क्या है?
new String("str"); // -> [String: 'str']विनिर्देश में स्ट्रिंग निर्माता के बारे में अधिक जानकारी:
बैकटिक्स के साथ कॉलिंग फ़ंक्शन
आइए एक फ़ंक्शन की घोषणा करते हैं जो कंसोल में सभी पैराम्स को लॉग करता है:
function f(...args) {
return args;
}कोई शक नहीं, आप जानते हैं कि आप इस फ़ंक्शन को इस तरह से कॉल कर सकते हैं:
f(1, 2, 3); // -> [ 1, 2, 3 ]लेकिन क्या आप जानते हैं कि आप किसी भी फंक्शन को बैकटिक्स कह सकते हैं।
f`true is ${true}, false is ${false}, array is ${[1, 2, 3]}`;
// -> [ [ 'true is ', ', false is ', ', array is ', '' ],
// -> true,
// -> false,
// -> [ 1, 2, 3 ] ]💡 व्याख्या:
यदि आप Tagged टेम्पलेट शाब्दिक से परिचित हैं, तो यह बिल्कुल भी जादू नहीं है। ऊपर दिए गए उदाहरण में, f फ़ंक्शन टेम्प्लेट शाब्दिक के लिए एक टैग है। टेम्प्लेट शाब्दिक से पहले टैग आपको फ़ंक्शन के साथ टेम्प्लेट शाब्दिकता को पार्स करने की अनुमति देता है। टैग फ़ंक्शन के पहले तर्क में स्ट्रिंग मानों की एक सरणी होती है। शेष तर्क भावों से संबंधित हैं। उदाहरण:
function template(strings, ...keys) {
// do something with strings and keys…
}यह [जादू के पीछे] (http://mxstbr.blog/2016/11/styled-compenders-magic-explained/) प्रसिद्ध पुस्तकालय जिसे [led स्टाइल-कंपोनेंट्स] कहा जाता है (https://www.styled-compenders.com) /), जो प्रतिक्रिया समुदाय में लोकप्रिय है।
विनिर्देशन के लिए लिंक:
पुकार पुकार पुकार
Found by @cramforce
console.log.call.call.call.call.call.apply(a => a, [1, 2]);💡 व्याख्या:
ध्यान दें, यह आपके दिमाग को तोड़ सकता है! अपने सिर में इस कोड को पुन: उत्पन्न करने का प्रयास करें: हम लागू विधि का उपयोग करके कॉल विधि लागू कर रहे हैं। अधिक पढ़ें:
- 19.2.3.3 Function.prototype.call(
thisArg, ...args) - **19.2.3.1 ** Function.prototype.apply(
thisArg,argArray)
A `constructor` property
const c = "constructor";
c[c][c]('console.log("WTF?")')(); // > WTF?💡 व्याख्या:
आइए इस उदाहरण पर विचार करें चरण-दर-चरण:
// Declare a new constant which is a string 'constructor'
const c = "constructor";
// c is a string
c; // -> 'constructor'
// Getting a constructor of string
c[c]; // -> [Function: String]
// Getting a constructor of constructor
c[c][c]; // -> [Function: Function]
// Call the Function constructor and pass
// the body of new function as an argument
c[c][c]('console.log("WTF?")'); // -> [Function: anonymous]
// And then call this anonymous function
// The result is console-logging a string 'WTF?'
c[c][c]('console.log("WTF?")')(); // > WTF?एक Object.prototyp.constructor उदाहरण ऑब्जेक्ट बनाने वाले Object कंस्ट्रक्टर फ़ंक्शन का संदर्भ देता है। स्ट्रिंग के साथ मामले में यह स्ट्रिंग है, संख्या के मामले में यह नंबर और इसी तरह है।
- एमडीएन के लिए [
Object.prototyp.constructor] (https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/constructor) - [** 19.1.3.1 ** Object.prototype.constructor] (https://www.ecma-international.org/ecma-262/#sec-object.prototyp.constructor)
वस्तु की संपत्ति की कुंजी के रूप में वस्तु
{ [{}]: {} } // -> { '[object Object]': {} }💡 व्याख्या:
यह काम क्यों करता है? यहां हम एक Computed संपत्ति name का उपयोग कर रहे हैं। जब आप उन कोष्ठकों के बीच एक वस्तु को पास करते हैं, तो यह एक स्ट्रिंग के लिए आपत्ति करता है, इसलिए हमें संपत्ति कुंजी '[ऑब्जेक्ट ऑब्जेक्ट] और मूल्य {} `मिलता है।
हम इस तरह "कोष्ठक नरक" बना सकते हैं:
({ [{}]: { [{}]: {} } }[{}][{}]); // -> {}
// structure:
// {
// '[object Object]': {
// '[object Object]': {}
// }
// }वस्तु शाब्दिक के बारे में यहाँ और अधिक पढ़ें:
- एमडीएन पर (ऑब्जेक्ट इनिशलाइज़र] (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Oference/Object_initializer)
- [** १२.२.६ ** वस्तु इनिशियलाइज़र] (http://www.ecma-international.org/ecma-262/6.0/#sec-object-initializer)
प्रोटोटाइपिंग को `__proto__` के साथ एक्सेस करना
जैसा कि हम जानते हैं, आदिमों के प्रोटोटाइप नहीं हैं। हालांकि, अगर हम आदिम के लिए __proto__ का मान प्राप्त करने का प्रयास करते हैं, तो हमें यह मिलेगा:
(1).__proto__.__proto__.__proto__; // -> null💡 व्याख्या:
ऐसा इसलिए होता है क्योंकि जब किसी चीज़ का प्रोटोटाइप नहीं होता है, तो इसे ToObject मेथड का इस्तेमाल करके रैपर ऑब्जेक्ट में लपेट दिया जाएगा। तो, चरण-दर-चरण:
(1)
.__proto__(
// -> [Number: 0]
1
)
.__proto__.__proto__(
// -> {}
1
).__proto__.__proto__.__proto__; // -> nullयहाँ __proto__ के बारे में अधिक जानकारी है:
- [** B.2.2.1 ** Object.prototype। ** proto **] (https://www.ecma-international.org/ecma-262/#sec-object.prototype.__proto__)
- [** 7.1.13 ** ToObject (
तर्क)] (https://www.ecma-international.org/ecma-262/#sec-toobject)
`` `$ {{वस्तु}}` ``
नीचे दिए गए अभिव्यक्ति का परिणाम क्या है?
`${{ Object }}`;The answer is:
// -> '[object Object]'💡 व्याख्या:
हमने एक ऑब्जेक्ट को एक संपत्ति के साथ परिभाषित किया है Object का उपयोग करके Shorthand संपत्ति अंकन:
`Js { वस्तु वस्तु; } `
फिर हमने इस ऑब्जेक्ट को टेम्पलेट शाब्दिक में पास कर दिया है, इसलिए उस ऑब्जेक्ट के लिए toString विधि कॉल करता है। इसलिए हमें स्ट्रिंग मिलती है '[ऑब्जेक्ट ऑब्जेक्ट]'।
- [** १२.२.९ ** टेम्पलेट साहित्य] (https://www.ecma-international.org/ecma-262/#sec-template-literals)
- एमडीएन पर (ऑब्जेक्ट इनिशलाइज़र] (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Oference/Object_initializer)
डिफ़ॉल्ट मानों के साथ विनाशकारी
इस उदाहरण पर विचार करें:
let x,
{ x: y = 1 } = { x };
y;उपरोक्त उदाहरण एक साक्षात्कार के लिए एक महान कार्य है। Y का मूल्य क्या है? उत्तर है:
// -> 1💡 व्याख्या:
let x,
{ x: y = 1 } = { x };
y;
// ↑ ↑ ↑ ↑
// 1 3 2 4ऊपर के उदाहरण के साथ:
- हम
xको बिना किसी मूल्य के घोषित करते हैं, इसलिए यहअपरिभाषितहै। - फिर हम
xका मान ऑब्जेक्ट प्रॉपर्टीxमें पैक करते हैं। - फिर हम विध्वंस का उपयोग करके
xका मान निकालते हैं औरyको असाइन करना चाहते हैं। यदि मान परिभाषित नहीं है, तो हम डिफ़ॉल्ट मान के रूप में1का उपयोग करने जा रहे हैं। yका मान लौटाएँ।
- Object initializer at MDN
डॉट्स और फैल रहा है
ऐरे के प्रसार के साथ दिलचस्प उदाहरणों की रचना की जा सकती है। इस पर विचार करो:
[...[..."..."]].length; // -> 3💡 व्याख्या:
क्यों 3? जब हम [स्प्रेड ऑपरेटर] (http://www.ecma-international.org/ecma-262/6.0/#sec-array-initializer) का उपयोग करते हैं, तो '@@ इट्रेटर' विधि को कहा जाता है, और लौटा हुआ पुनरावृत्ति है मूल्यों को पुनरावृत्त करने के लिए उपयोग किया जाता है। स्ट्रिंग के लिए डिफ़ॉल्ट पुनरावृत्ति एक स्ट्रिंग को वर्णों में फैलाता है। फैलने के बाद, हम इन पात्रों को एक सरणी में पैक करते हैं। फिर हम इस सरणी को फिर से फैलाते हैं और इसे वापस सरणी में पैक करते हैं।
A '...' स्ट्रिंग में तीन ``वर्ण होते हैं, इसलिए परिणामी सरणी की लंबाई3 है।
अब, चरण-दर-चरण:
[...'...'] // -> [ '.', '.', '.' ]
[...[...'...']] // -> [ '.', '.', '.' ]
[...[...'...']].length // -> 3जाहिर है, हम सरणी के तत्वों को जितनी बार चाहें उतनी बार फैला और लपेट सकते हैं:
[...'...'] // -> [ '.', '.', '.' ]
[...[...'...']] // -> [ '.', '.', '.' ]
[...[...[...'...']]] // -> [ '.', '.', '.' ]
[...[...[...[...'...']]]] // -> [ '.', '.', '.' ]
// and so on …लेबल
कई प्रोग्रामर जावास्क्रिप्ट में लेबल के बारे में नहीं जानते हैं। वे दिलचस्प हैं:
foo: {
console.log("first");
break foo;
console.log("second");
}
// > first
// -> undefined💡 व्याख्या:
लेबल स्टेटमेंट का उपयोग ब्रेक या जारी स्टेटमेंट के साथ किया जाता है। आप लूप की पहचान करने के लिए एक लेबल का उपयोग कर सकते हैं, और फिर प्रोग्राम को लूप को बाधित करना चाहिए या इसके निष्पादन को जारी रखने के लिए ब्रेक या जारी बयान का उपयोग करें।
ऊपर दिए गए उदाहरण में, हम एक लेबल foo की पहचान करते हैं। उसके बाद कंसोल.लॉग ('पहले'); निष्पादित होता है और फिर हम निष्पादन को बाधित करते हैं।
जावास्क्रिप्ट में लेबल के बारे में और पढ़ें:
Nested labels
a: b: c: d: e: f: g: 1, 2, 3, 4, 5; // -> 5💡 व्याख्या:
पिछले उदाहरणों के समान, इन लिंक का अनुसरण करें:
- [** १२.१६ ** कोमा संचालक (
,)] (https://www.ecma-international.org/ecma-262/#sec-comma-operator) - [** 13.13 ** लेबल किए गए विवरण] (https://tc39.github.io/ecma262/#sec-labelled-statements)
- [लेबल किए गए कथन] (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/label) MDN पर
कपटी `कोशिश..चेक`
यह अभिव्यक्ति क्या लौटेगी? 2 या 3?
(() => {
try {
return 2;
} finally {
return 3;
}
})();उत्तर 3 है। आश्चर्य चकित?
# व्याख्या:
- [** 13.15 ** द
कोशिशस्टेटमेंट] (https://www.ecma-international.org/ecma-262/#sec-try-statement)
क्या यह कई विरासत है?
नीचे दिए गए उदाहरण पर एक नज़र डालें:
new class F extends (String, Array) {}(); // -> F []क्या यह एक बहु विरासत है? नहीं।
# व्याख्या:
दिलचस्प हिस्सा फैली खंड ((स्ट्रिंग, सरणी) का मूल्य है। ग्रुपिंग ऑपरेटर हमेशा अपना अंतिम तर्क देता है, इसलिए (स्ट्रिंग, एरे) वास्तव में सिर्फ एरे है। इसका मतलब है कि हमने सिर्फ एक वर्ग बनाया है जो एरे का विस्तार करता है।
एक जनरेटर जो खुद उपजता है
एक जनरेटर के इस उदाहरण पर विचार करें जो स्वयं उपज देता है:
(function* f() {
yield f;
})().next();
// -> { value: [GeneratorFunction: f], done: false }जैसा कि आप देख सकते हैं, लौटाया गया मान एक वस्तु है जिसका मान`` एफ के बराबर है। उस मामले में, हम ऐसा कुछ कर सकते हैं:
(function* f() {
yield f;
})()
.next()
.value()
.next()(
// -> { value: [GeneratorFunction: f], done: false }
// and again
function* f() {
yield f;
}
)()
.next()
.value()
.next()
.value()
.next()(
// -> { value: [GeneratorFunction: f], done: false }
// and again
function* f() {
yield f;
}
)()
.next()
.value()
.next()
.value()
.next()
.value()
.next();
// -> { value: [GeneratorFunction: f], done: false }
// and so on
// …💡 व्याख्या:
यह समझने के लिए कि यह इस तरह क्यों काम करता है, विनिर्देश के इन वर्गों को पढ़ें:
- [** २५ ** नियंत्रण अमूर्त वस्तुएं] (https://www.ecma-international.org/ecma-262/#sec-control-abstraction-objects)
- [** २५.३ ** जेनरेटर ऑब्जेक्ट] (https://www.ecma-international.org/ecma-262/#sec-generator-objects)
कक्षा का एक वर्ग
इस ओफ़्सेटेड सिंटैक्स प्लेइंग पर विचार करें:
typeof new class {
class() {}
}(); // -> 'object'ऐसा लगता है जैसे हम क्लास के अंदर क्लास घोषित कर रहे हैं। एक त्रुटि होनी चाहिए, हालांकि, हमें स्ट्रिंग '' ऑब्जेक्ट '' मिलता है।
# व्याख्या:
ECMAScript 5 युग के बाद से keywords को property names के रूप में अनुमति दी गई है। तो इस सरल वस्तु उदाहरण के रूप में इसके बारे में सोचो:
const foo = {
class: function() {}
};और ईएस 6 मानकीकृत शॉर्टहैंड विधि परिभाषाएं। साथ ही, कक्षाएं अनाम हो सकती हैं। इसलिए यदि हम ड्रॉप करते हैं : फ़ंक्शन भाग, हम प्राप्त करने जा रहे हैं:
class {
class() {}
}डिफ़ॉल्ट वर्ग का परिणाम हमेशा एक साधारण वस्तु होती है। और इसके टाइपोफ को ऑब्जेक्ट' वापस करना चाहिए।
यहाँ और पढ़ें:
- [** १४.३ ** विधि परिभाषाएँ] (https://www.ecma-international.org/ecma-262/#sec-method-definitions)
- [** १४.५ ** कक्षा परिभाषाएँ] (https://www.ecma-international.org/ecma-262/#sec-class-definitions)
गैर-सहकर्मी वस्तुएं
प्रसिद्ध प्रतीकों के साथ, प्रकार की जबरदस्ती से छुटकारा पाने का एक तरीका है। जरा देखो तो:
function nonCoercible(val) {
if (val == null) {
throw TypeError("nonCoercible should not be called with null or undefined");
}
const res = Object(val);
res[Symbol.toPrimitive] = () => {
throw TypeError("Trying to coerce non-coercible object");
};
return res;
}अब हम इसका उपयोग इस तरह कर सकते हैं:
// objects
const foo = nonCoercible({ foo: "foo" });
foo * 10; // -> TypeError: Trying to coerce non-coercible object
foo + "evil"; // -> TypeError: Trying to coerce non-coercible object
// strings
const bar = nonCoercible("bar");
bar + "1"; // -> TypeError: Trying to coerce non-coercible object
bar.toString() + 1; // -> bar1
bar === "bar"; // -> false
bar.toString() === "bar"; // -> true
bar == "bar"; // -> TypeError: Trying to coerce non-coercible object
// numbers
const baz = nonCoercible(1);
baz == 1; // -> TypeError: Trying to coerce non-coercible object
baz === 1; // -> false
baz.valueOf() === 1; // -> true💡 व्याख्या:
- [सेर्गेई रुबनोव द्वारा एक तस्वीर] (https://gist.github.com/chicoxyzzy/5dd24608e886adf5444499896dff1197)
- [** ६.१.५.१ ** अच्छी तरह से ज्ञात प्रतीक] (https://www.ecma-international.org/ecma-262/#sec-well-ogn-symbols)
मुश्किल तीर कार्य
नीचे दिए गए उदाहरण पर विचार करें:
let f = () => 10;
f(); // -> 10ठीक है, ठीक है, लेकिन इस बारे में क्या:
let f = () => {};
f(); // -> undefined💡 व्याख्या:
आप अपरिभाषित के बजाय{}की उम्मीद कर सकते हैं। ऐसा इसलिए है क्योंकि घुंघराले ब्रेसिज़ तीर फ़ंक्शन के सिंटैक्स का हिस्सा हैं, इसलिए f अपरिभाषित वापस आ जाएगा। हालांकि, ब्रैकेट के साथ रिटर्न वैल्यू को संलग्न करके, तीर फ़ंक्शन से सीधे {} ऑब्जेक्ट को वापस करना संभव है।
let f = () => ({});
f(); // -> {}एरो फ़ंक्शंस एक निर्माता नहीं हो सकता है
नीचे दिए गए उदाहरण पर विचार करें:
let f = function() {
this.a = 1;
};
new f(); // -> { 'a': 1 }अब, एक तीर फ़ंक्शन के साथ ऐसा करने का प्रयास करें:
let f = () => {
this.a = 1;
};
new f(); // -> TypeError: f is not a constructor💡 व्याख्या:
एरो फ़ंक्शंस को कंस्ट्रक्टर के रूप में इस्तेमाल नहीं किया जा सकता है और नए के साथ उपयोग किए जाने पर एक त्रुटि होगी। क्योंकि एक शाब्दिक यह है, और एक प्रोटोटाइप संपत्ति नहीं है, इसलिए यह बहुत मतलब नहीं होगा।
`तर्क` और तीर कार्य
नीचे दिए गए उदाहरण पर विचार करें:
let f = function() {
return arguments;
};
f("a"); // -> { '0': 'a' }अब, एक तीर फ़ंक्शन के साथ ऐसा करने का प्रयास करें:
`Js let f = () => तर्क; च ( "एक"); // -> अनट्रेक्टेड रेफरेंस: तर्क को परिभाषित नहीं किया गया है `
# व्याख्या:
एरो फ़ंक्शंस शॉर्ट और लेक्सिकल this होने पर ध्यान देने के साथ नियमित फ़ंक्शंस का एक हल्का संस्करण है। उसी समय एरो फ़ंक्शंस आर्ग्यूमेंट्स ऑब्जेक्ट के लिए बाइंडिंग प्रदान नहीं करते हैं। एक वैध विकल्प के रूप में एक ही परिणाम प्राप्त करने के लिए बाकी मापदंडों का उपयोग करें:
let f = (...args) => args;
f("a");- Arrow functions at MDN.
मुश्किल वापसी
वापसी बयान भी मुश्किल है। इस पर विचार करो:
(function() {
return
{
b: 10;
}
})(); // -> undefined💡 व्याख्या:
वापसी और लौटी हुई अभिव्यक्ति एक ही पंक्ति में होनी चाहिए:
(function() {
return {
b: 10
};
})(); // -> { b: 10 }यह एक अवधारणा के कारण है जिसे स्वचालित अर्धविराम सम्मिलन कहा जाता है, जो स्वचालित रूप से अधिकांश न्यूक्लियर के बाद अर्धविराम सम्मिलित करता है। पहले उदाहरण में, रिटर्न स्टेटमेंट और ऑब्जेक्ट शाब्दिक के बीच एक अर्धविराम डाला जाता है, इसलिए फ़ंक्शन अपरिभाषित देता है और ऑब्जेक्ट शाब्दिक का कभी मूल्यांकन नहीं किया जाता है।
- [** 11.9.1 ** स्वचालित अर्धविराम सम्मिलन के नियम] (https://www.ecma-international.org/ecma-262/#sec-rules-of-automatic-semicolon-insertion)
- [** 13.10 ** द
रिटर्नस्टेटमेंट] (https://www.ecma-international.org/ecma-262/#sec-return-statement)
ऑब्जेक्ट पर कार्य असाइन करना
var foo = {n: 1};
var bar = foo;
foo.x = foo = {n: 2};
foo.x // -> undefined
foo // -> {n: 2}
bar // -> {n: 1, x: {n: 2}}दाएं से बाएं, {n: 2} को फू को सौंपा गया है, और इस असाइनमेंट का परिणाम {n: 2} को foo.x को सौंपा गया है, इसीलिए बार {n: 1, x: {है n: 2}} के रूप में बार फू के लिए एक संदर्भ है। लेकिन क्यों foo.x अपरिभाषित है जबकि bar.x नहीं है?
💡 व्याख्या:
फू और बार एक ही ऑब्जेक्ट को संदर्भित करते हैं {n: 1}, और लवलीन को असाइनमेंट से पहले हल किया जाता है। foo = {n: 2} एक नई वस्तु बना रहा है, और इसलिए उस नई वस्तु को संदर्भित करने के लिए foo अपडेट किया गया है। यहाँ चाल को 'foo.x = ...' में foo किया गया है क्योंकि एक लैवल्यू को पहले से हल किया गया था और अभी भी पुराने foo = {n: 1} ऑब्जेक्ट को संदर्भित करता है और इसे x मान जोड़कर अपडेट करें। उस श्रृंखला असाइनमेंट के बाद, बार अभी भी पुराने फू ऑब्जेक्ट को संदर्भित करता है, लेकिन फू नए {n: 2} ऑब्जेक्ट को संदर्भित करता है, जहां एक्स मौजूद नहीं है।
यह इसके बराबर है:
var foo = {n: 1};
var bar = foo;
foo = {n: 2} // -> {n: 2}
bar.x = foo // -> {n: 1, x: {n: 2}}
// bar.x point to the address of the new foo object
// it's not equivalent to: bar.x = {n: 2}सरणियों के साथ ऑब्जेक्ट गुण तक पहुँचना
var obj = { property: 1 };
var array = ["property"];
obj[array]; // -> 1छद्म बहुआयामी सरणियों के बारे में क्या?
var map = {};
var x = 1;
var y = 2;
var z = 3;
map[[x, y, z]] = true;
map[[x + 10, y, z]] = true;
map["1,2,3"]; // -> true
map["11,2,3"]; // -> true💡 व्याख्या:
कोष्ठक [] परिचालक उत्तीर्ण अभिव्यक्ति को स्ट्रींग का उपयोग करके परिवर्तित करता है। स्ट्रिंग में एक तत्व तत्व को परिवर्तित करना स्ट्रिंग में निहित तत्व को परिवर्तित करने के लिए एक समान है:
["property"].toString(); // -> 'property'Null and Relational Operators
null > 0; // false
null == 0; // false
null >= 0; // true💡 व्याख्या:
लंबी कहानी छोटी, यदि शून्य`` 0 से कम है, तो झूठा है, तो शून्य = 0 सत्य` है। इसके लिए [यहाँ] (https://blog.campvanilla.com/javascript-the-curious-case-of-null-0-7b131644e274) की गहन व्याख्या पढ़ें।
`Number.toFixed ()` विभिन्न संख्याएँ प्रदर्शित करता है
Number.toFixed () विभिन्न ब्राउज़रों में थोड़ा अजीब व्यवहार कर सकता है। इस उदाहरण को देखें:
(0.7875).toFixed(3);
// Firefox: -> 0.787
// Chrome: -> 0.787
// IE11: -> 0.788
(0.7876).toFixed(3);
// Firefox: -> 0.788
// Chrome: -> 0.788
// IE11: -> 0.788💡 व्याख्या:
हालांकि आपकी पहली वृत्ति यह हो सकती है कि IE11 सही है और फ़ायरफ़ॉक्स / क्रोम गलत है, वास्तविकता यह है कि फ़ायरफ़ॉक्स / क्रोम अधिक सीधे संख्याओं के लिए मानक मान रहे हैं (IEEE-754 फ़्लोटिंग पॉइंट), जबकि IE11 उन्हें पूरी तरह से अवज्ञा करता है (जो शायद है ) स्पष्ट परिणाम देने का प्रयास।
आप देख सकते हैं कि कुछ त्वरित परीक्षणों के साथ ऐसा क्यों होता है:
// 5 नीचे गोलाई के विषम परिणाम की पुष्टि करें
(0.7875).toFixed(3); // -> 0.787
// It looks like it's just a 5 when you expand to the
// limits of 64-bit (double-precision) float accuracy
(0.7875).toFixed(14); // -> 0.78750000000000
// But what if you go beyond the limit?
(0.7875).toFixed(20); // -> 0.78749999999999997780फ़्लोटिंग पॉइंट नंबरों को आंतरिक रूप से दशमलव अंकों की एक सूची के रूप में संग्रहीत नहीं किया जाता है, लेकिन एक अधिक जटिल कार्यप्रणाली के माध्यम से जो छोटी अशुद्धि पैदा करता है जो आमतौर पर स्ट्रींग और इसी तरह की कॉल से गोल होते हैं, लेकिन वास्तव में आंतरिक रूप से मौजूद होते हैं।
इस मामले में, कि अंत में "5" वास्तव में एक सच के नीचे एक बहुत छोटा सा अंश था। किसी भी उचित लंबाई पर इसे 5 के रूप में प्रस्तुत करना ... लेकिन यह वास्तव में आंतरिक रूप से 5 नहीं है।
IE11, हालांकि, केवल (शून्य) मामले में भी अंत तक संलग्न शून्य के साथ मूल्य इनपुट की रिपोर्ट करेगा, क्योंकि यह हार्डवेयर सीमाओं से परेशानियों को कम करने के लिए मूल्य को जबरन गोल करना प्रतीत होता है।
संदर्भ के लिए देखें नोट 2 ECMA-262 पर toFixed के लिए परिभाषा।
`Math.max()` less than `Math.min()`
Math.min(1, 4, 7, 2); // -> 1
Math.max(1, 4, 7, 2); // -> 7
Math.min(); // -> Infinity
Math.max(); // -> -Infinity
Math.min() > Math.max(); // -> true💡 व्याख्या:
- चार्ली हार्वे द्वारा Math.max () Math.min () से कम क्यों है? (Https://charlieharvey.org.uk/page/why_math_max_is_less_than_math_min)
तुलना `null` से` 0` तक
निम्नलिखित अभिव्यक्तियाँ विरोधाभास का परिचय देती हैं:
null == 0; // -> false
null > 0; // -> false
null >= 0; // -> trueNull न तो बराबर हो सकता है और न ही 0 से अधिक, अगर null> = 0 वास्तव में true है? (यह भी उसी तरह से कम के साथ काम करता है।)
💡 व्याख्या:
जिस तरह से इन तीनों भावों का मूल्यांकन किया जाता है, वे सभी भिन्न हैं और इस अप्रत्याशित व्यवहार के उत्पादन के लिए जिम्मेदार हैं।
सबसे पहले, अमूर्त समानता तुलना null == 0। आम तौर पर, यदि यह ऑपरेटर दोनों तरफ के मूल्यों की ठीक से तुलना नहीं कर सकता है, तो यह दोनों संख्याओं में परिवर्तित हो जाता है और संख्याओं की तुलना करता है। फिर, आप निम्न व्यवहार की अपेक्षा कर सकते हैं:
// This is not what happens
(null == 0 + null) == +0;
0 == 0;
true;हालांकि, कल्पना के एक करीबी पढ़ने के अनुसार, संख्या रूपांतरण वास्तव में उस तरफ नहीं होता है जो अशक्त या अपरिभाषित है। इसलिए, यदि आपके पास समान चिह्न के एक तरफ null है, तो दूसरी तरफ true वापस करने के लिए अभिव्यक्ति के लिए null या अनिर्धारित होना चाहिए। चूंकि यह मामला नहीं है, झूठ वापस आ गया है।
अगला, संबंधपरक तुलना अशक्त> 0। एल्गोरिथ्म यहाँ, अमूर्त समानता ऑपरेटर के विपरीत, will एक संख्या के लिए null कन्वर्ट। इसलिए, हमें यह व्यवहार मिलता है:
null > 0
+null = +0
0 > 0
falseअंत में, संबंधपरक तुलना शून्य> = 0। आप तर्क दे सकते हैं कि यह अभिव्यक्ति शून्य> 0 का परिणाम होना चाहिए null == 0; अगर ऐसा होता, तो उपरोक्त परिणामों का मतलब यह होगा कि यह भी गलत होगा। हालांकि, वास्तव में > = ऑपरेटर बहुत अलग तरीके से काम करता है, जो मूल रूप से < ऑपरेटर के विपरीत लेना है। क्योंकि ऊपर से ऑपरेटर से अधिक के साथ हमारा उदाहरण भी ऑपरेटर से कम के लिए है, इसका मतलब है कि इस अभिव्यक्ति का वास्तव में मूल्यांकन किया जाता है:
null >= 0;
!(null < 0);
!(+null < +0);
!(0 < 0);
!false;
true;समान परिवर्तनशील पुनर्वितरण
JS चरों को फिर से दिखाने की अनुमति देता है:
a;
a;
// यह भी मान्य है
a, a;सख्त मोड में भी काम करता है:
var a, a, a;
var a;
var a;💡 व्याख्या:
सभी परिभाषाओं को एक परिभाषा में मिला दिया गया है।
- [** 13.3.2 ** चर कथन] (https://www.ecma-international.org/ecma-262/#sec-variable-statement)
डिफ़ॉल्ट व्यवहार Array.prototyp.sort ()
कल्पना करें कि आपको संख्याओं की एक संख्या को क्रमबद्ध करना होगा।
[ 10, 1, 3 ].sort() // -> [ 1, 10, 3 ]💡 व्याख्या:
डिफ़ॉल्ट सॉर्ट ऑर्डर तत्वों को स्ट्रिंग्स में परिवर्तित करने पर बनाया गया है, फिर UTF-16 कोड इकाइयों के उनके अनुक्रमों की तुलना करते हैं।
संकेत
यदि आप कुछ भी लेकिन स्ट्रिंग को क्रमबद्ध करने की कोशिश करते हैं, तो 'तुलना' पास करें।
[ 10, 1, 3 ].sort((a, b) => a - b) // -> [ 1, 3, 10 ]📚 अन्य संसाधन
- [wtfjs.com] (http://wtfjs.com/) - वेब की भाषा के लिए उन विशेष अनियमितताओं, विसंगतियों और सीधे सादे दर्दभरे क्षणों का एक संग्रह।
- [वट] (https://www.destroyallsoftware.com/talks/wat) - कोडमश 2012 से गैरी बर्नहार्ट द्वारा एक बिजली की बात
- [क्या ... जावास्क्रिप्ट?] (Https://www.youtube.com/watch?v=2pL28CcEijU) - काइल सिम्पसन फॉरवर्ड 2 के लिए जावास्क्रिप्ट से "पागल को बाहर निकालने" का प्रयास करते हैं। वह क्लीनर, अधिक सुरुचिपूर्ण, अधिक पठनीय कोड बनाने में आपकी सहायता करना चाहता है, फिर लोगों को खुले स्रोत समुदाय में योगदान करने के लिए प्रेरित करता है।
🎓 License
File: README-it-it.md
What the f\*ck JavaScript?
Una raccolta di snippet ingannevoli e divertenti scritti in JavaScript
JavaScript è un ottimo linguaggio. Ha una sintassi semplice, un grande ecosistema e, quello che conta veramente, una community fantastica.
Allo stesso tempo, sappiamo che JavaScript è un linguaggio abbastanza strano con delle parti cervellotiche. Alcune di queste possono rendere il nostro lavoro un inferno, alcune invece possono farci ridere a crepapelle.
L'idea per WTFJS è di Brian Leroux. Questo elenco è largamente ispirato al suo talk “WTFJS” at dotJS 2012:
Node Packaged Manuscript
Puoi installare questo manuale con npm. Lancia semplicemente:
$ npm install wtfjsOra dovresti essere in grado di eseguire wtfjs dalla riga di comando. Altrimenti puoi continuare tranquillamente a leggerlo qui.
Il codice sorgente lo puoi trovare qui: https://github.com/denysdovhan/wtfjs
Traduzioni
Attualmente wtfjs è disponibile nelle seguenti lingue:
Table of Contents
- 💪🏻 Motivazione
- ✍🏻 Notazione
- 👀 Esempi
- [
[]è uguale a ` - [
trueè diverso da ` - true è false
- baNaNa
NaNnon èNaN- È un fail
[]è truthy, ma nontruenullè falsy, ma nonfalsedocument.allè un object, ma è undefined- Il numero più piccolo rappresentabile è maggiore di zero
- function non è una function
- Sommare array
- "Trailing commas" in un array
- L'operatore di uguaglianza sugli array è un mostro
undefinedeNumberparseIntè bast**do- Math con
trueefalse - I commenti HTML sono validi anche in JavaScript
NaNènota number[]enullsono objects- Incrementare numeri magicamente
- La precisione di
0.1 + 0.2 - Patchare numeri
- Confrontare tre numeri
- Matematica spassosa
- Somma di RegExps
- Le stringhe non sono istanze di
String - Richiamare funzioni con le backticks
- Call call call
- Una proprietà chiamata
constructor - Un Object usato come key nelle property di un oggetto
- Accedere ai prototypes con
__proto__ `${{Object}}`- Destructuring con valori di default
- Puntini e lo spreading
- Labels
- Labels annidate
- Un
try..catchinsidioso - Si tratta di ereditarietà multipla?
- Un generator che produce se stesso
- Una classe di tipo class
- Oggetti non-coercible
- Arrow functions strambe
- Arrow functions non possono essere un costruttore
argumentse arrow functions- Uno strano return
- Concatenare assegnamenti su un object
- Accedere alle properties di un object con gli array
- Null e gli operatori relazionali
Number.toFixed()mostra numeri diversiMath.max()più piccolo diMath.min()- Confrontare
nullcon0 - Alcune ridichiarazioni di variabili
- Comportamento di default di Array.prototype.sort()
- resolve() non restituisce un'istanza di Promise
- [
- 📚 Other resources
- 🎓 License
💪🏻 Motivazione
Just for fun
— “Just for Fun: The Story of an Accidental Revolutionary”, Linus Torvalds
Lo scopo principale di questo elenco è quello di raccogliere alcuni esempi strambi e mostrarne il loro funzionamento, se possibile. Semplicemente per il fatto che è divertente imparare qualcosa che non sapevamo prima.
Se sei un principiante puoi utilizzare questi appunti per approfondire JavaScript. Spero che questi appunti ti motivino a leggerne le specifiche.
Se sei uno sviluppatore senior, considera questi esempi come un'ottimo punto di riferimento per tutte quelle stranezze e stramberie del tuo amato JavaScript.
Ad ogni modo, leggilo. Probabilmente imparerai qualcosa di nuovo.
✍🏻 Notazione
// -> viene utilizzato per indicare il risultato di un'espressione. Ad esempio:
1 + 1; // -> 2// > significa il risultato di console.log o di un altro output. Ad esempio:
console.log("hello, world!"); // > hello, world!// è semplicemente un commento utilizzato per le spiegazioni. Esempio:
// Assegnare una funzione ad una costante
const foo = function() {};👀 Esempi
`[]` è uguale a `![]`
Array è uguale a not array:
[] == ![]; // -> true💡 Spiegazione:
L'opratore di abstract equality converte entrambi gli operandi prima di confrontarli, e diventano entrambi 0 per ragioni differenti. Gli Array sono truthy, quindi sulla destra, l'opposto di un valore truthy è false, che viene quindi forzato a diventare uno 0. Sul lato sinistro però l'array vuoto viene forzato a diventare un numero senza prima essere convertito in un valore booleano, e gli array vuoti vengono forzati a 0 a prescindere che siano truthy.
Qui possiamo vedere come viene semplificata l'espressione:
+[] == +![];
0 == +false;
0 == 0;
true;Vedi anche [] è truthy, ma non true.
`true` è diverso da `![]`, ma anche diverso da `[]`
Array è diverso da true, ma anche not Array è diverso da true;
Array è uguale a false, ma anche not Array è uguale a false:
true == []; // -> false
true == ![]; // -> false
false == []; // -> true
false == ![]; // -> true💡 Spiegazione:
true == []; // -> false
true == ![]; // -> false
// Secondo le specifiche
true == []; // -> false
toNumber(true); // -> 1
toNumber([]); // -> 0
1 == 0; // -> false
true == ![]; // -> false
![]; // -> false
true == false; // -> falsefalse == []; // -> true
false == ![]; // -> true
// Secondo le specifiche
false == []; // -> true
toNumber(false); // -> 0
toNumber([]); // -> 0
0 == 0; // -> true
false == ![]; // -> true
![]; // -> false
false == false; // -> truetrue è false
!!"false" == !!"true"; // -> true
!!"false" === !!"true"; // -> true💡 Spiegazione:
Considera questo, step-by-step:
// true è 'truthy' e rappresentato dal valore 1 (number), 'true' in formato stringa è NaN.
true == "true"; // -> false
false == "false"; // -> false
// 'false' non è la stringa vuota, quindi è un valore truthy
!!"false"; // -> true
!!"true"; // -> truebaNaNa
"b" + "a" + +"a" + "a"; // -> 'baNaNa'Questo è un giochino old-school in JavaScript, rivisitato. L'originale è questo:
"foo" + +"bar"; // -> 'fooNaN'💡 Spiegazione:
L'espressione viene valutata come 'foo' + (+'bar'), che converte 'bar' in "not a number".
`NaN` non è `NaN`
NaN === NaN; // -> false💡 Spiegazione:
Le specifiche definiscono rigorosamente la logica dietro a questo comportamento:
- Se
Type(x)è diverso daType(y), return false.- Se
Type(x)è Number, allora
- Se
xè NaN, return false.- Se
yè NaN, return false.- … … …
Seguendo la definizione di NaN da quella dell'IEEE:
Sono possibili quattro relazioni mutuamente esclusive: less than, equal, greater than, e unordered. L'ultimo caso si presenta quando almeno un operando è NaN. Tutt i NaN se comparati risulteranno unordered, inclusa la comparazione con se stesso.
— “What is the rationale for all comparisons returning false for IEEE754 NaN values?” at StackOverflow
È un fail
Non crederai ai tuoi occhi, ma...
(![] + [])[+[]] +
(![] + [])[+!+[]] +
([![]] + [][[]])[+!+[] + [+[]]] +
(![] + [])[!+[] + !+[]];
// -> 'fail'💡 Spiegazione:
Rompendo quell'ammasso di simboli in pezzettini, possiamo notare che il seguente pattern si ripete spesso:
![] + []; // -> 'false'
![]; // -> falseQuindi proviamo a sommare [] a false. Ma a causa di una serie di chiamate interne (binary + Operator -> ToPrimitive -> [[DefaultValue]]) otteniamo la conversione dell'operando a destra in una stringa:
![] + [].toString(); // 'false'Se pensiamo ad una stringa come un Array, possiamo accedere al suo primo elemento con [0]:
"false"[0]; // -> 'f'Il resto è ovvio, ma la i è complicata. La i in fail viene ottenuta generando la stringa 'falseundefined' e prendendo l'elemento all'indice ['10']
`[]` è truthy, ma non `true`
Un array è un valore truthy, ma non è uguale a true.
!![] // -> true
[] == true // -> false💡 Spiegazione:
Ecco i link alle sezioni corrispondenti della specifica ECMA-262:
`null` è falsy, ma non `false`
Nonostante il fatto che null sia un valore falsy, non è uguale a false.
!!null; // -> false
null == false; // -> falseAllo stesso modo, altri valori falsy, come 0 o '' sono uguali a false.
0 == false; // -> true
"" == false; // -> true💡 Spiegazione:
La spiegazione è la stessa dell'esempio precedente. Ecco il link corrispondente:
`document.all` è un object, ma è undefined
⚠️ Questo fa parte delle Browser API e non funziona su Node.js ⚠️
Nonostante il fatto che document.all sia un oggetto array-like e permette l'accesso al DOM della pagina, risponde alla funzione typeof con undefined.
document.all instanceof Object; // -> true
typeof document.all; // -> 'undefined'Allo stesso modo, document.all è diverso da undefined.
document.all === undefined; // -> false
document.all === null; // -> falseMa contemporaneamente:
document.all == null; // -> true💡 Spiegazione:
document.allveniva utilizzato per accedere agli elementi del DOM, nelle vecchie versioni di IE. Nonostante non sia mai diventato uno standard, veniva ampiamente utilizzato in codice JS non proprio recentissimo. Quando vennero rilasciate le nuove APIs (comedocument.getElementById) questa API divenne obsoleta e il comitato dello standard dovette decidere cosa farne. A causa del suo uso spropositato l'API venne mantenuta ma venne introdotta una violazione intenzionale nelle speficiche di JavaScript.
Il motivo per il quale risponde afalsequando si utilizza l'operatore di Strict Equality Comparison conundefined, mentretruequando si utilizza l'operatore di Abstract Equality Comparison è a causa della violazione intenzionale inserita nella specifica che la permette in modo esplicito.
— “Obsolete features - document.all” at WhatWG - HTML spec
— “Chapter 4 - ToBoolean - Falsy values” at YDKJS - Types & Grammar
Il numero più piccolo rappresentabile è maggiore di zero
Number.MIN_VALUE è il numero più piccolo rappresentabile, che è maggiore di zero:
Number.MIN_VALUE > 0; // -> true💡 Spiegazione:
Number.MIN_VALUEè5e-324, ovvero il più piccolo numero positivo che può essere rappresentato con precisione float, cioè quello che si può ottenere il più vicino possibile allo zero. Definisce la migliore risoluzione che un tipo di dato float può fornire.Il numero più piccolo in assoluto è
Number.NEGATIVE_INFINITYnonostante non sia effettivamente un tipo numerico.— “Why is
0less thanNumber.MIN_VALUEin JavaScript?” at StackOverflow
function non è una function
⚠️ Un bug presente in V8 v5.5 o inferiore (Node.js <=7) ⚠️
Tutti conoscerete la noiosa undefined is not a function, ma questa?
// Dichiara una classe che estende null
class Foo extends null {}
// -> [Function: Foo]
new Foo() instanceof null;
// > TypeError: function is not a function
// > at … … …💡 Spiegazione:
Questo non è parte delle specifiche. È semplicemente un bug che ora è stato risolto, quindi non dovrebbero esserci problemi con questo in futuro.
Sommare array
E se provassimo a sommare due array?
[1, 2, 3] + [4, 5, 6]; // -> '1,2,34,5,6'💡 Spiegazione:
Viene svolta la concatenazione. il procedimento step-by-step è il seguente:
[1, 2, 3] +
[4, 5, 6][
// chiama toString()
(1, 2, 3)
].toString() +
[4, 5, 6].toString();
// concatenazione
"1,2,3" + "4,5,6";
// ->
("1,2,34,5,6");"Trailing commas" in un array
Creiamo un array con 4 elementi vuoti. Nonostante ciò, si ottiene un array con 3 elementi, a causa delle "trailing commas":
let a = [, , ,];
a.length; // -> 3
a.toString(); // -> ',,'💡 Spiegazione:
Trailing commas (anche chiamate "final commas") sono utili quando si aggiungono nuovi elementi, parametri o proprietà in codice JavaScript. Se si vuole aggiungere una nuova proprietà si può semplicemente aggiungere una nuova riga senza modificare quella precedente, se quella linea presenta già una virgola alla fine. Questo rende i diffs dei sistemi di version-control più puliti e modificare il codice è leggermente meno problematico.
— Trailing commas at MDN
L'operatore di uguaglianza sugli array è un mostro
L'operatore di uguaglianza sugli array in JS è un mostro, come possiamo osservare sotto:
[] == '' // -> true
[] == 0 // -> true
[''] == '' // -> true
[0] == 0 // -> true
[0] == '' // -> false
[''] == 0 // -> true
[null] == '' // true
[null] == 0 // true
[undefined] == '' // true
[undefined] == 0 // true
[[]] == 0 // true
[[]] == '' // true
[[[[[[]]]]]] == '' // true
[[[[[[]]]]]] == 0 // true
[[[[[[ null ]]]]]] == 0 // true
[[[[[[ null ]]]]]] == '' // true
[[[[[[ undefined ]]]]]] == 0 // true
[[[[[[ undefined ]]]]]] == '' // true💡 Spiegazione:
Guarda attentamente gli esempi precedenti! Il comportamento viene spiegato nella sezione 7.2.13 Abstract Equality Comparison delle specifiche.
`undefined` e `Number`
Se non passiamo argomenti al costruttore di Number, otteniamo 0. Il valore undefined viene assegnato di default quando non viene passato alcun valore, quindi possiamo aspettarci che Number senza parametri prenda undefined come valore del suo parametro. Invece quando inseriamo undefined, otteniamo NaN.
Number(); // -> 0
Number(undefined); // -> NaN💡 Spiegazione:
In base alle specifiche:
- Se non viene passato alcun parametro durante l'invocazione della funzione,
nviene valorizzato a+0. - Altrimenti,
nsarà il risultato diToNumber(value). - Nel caso di
undefined,ToNumber(undefined)deve restituireNaN.
Qui la sezione corrispondente:
`parseInt` è bast\*\*do
parseInt è famoso per le sue stranezze:
parseInt("f*ck"); // -> NaN
parseInt("f*ck", 16); // -> 15💡 Spiegazione: Questo avviene perché parseInt continuerà a svolgere il parsing carattere per carattere fino a che non trova un carattere che non riconosce. La f in 'f*ck' è la rappresentazione esadecimale di 15.
Svolgere il parsing di Infinity a integer è qualcosa di...
//
parseInt("Infinity", 10); // -> NaN
// ...
parseInt("Infinity", 18); // -> NaN...
parseInt("Infinity", 19); // -> 18
// ...
parseInt("Infinity", 23); // -> 18...
parseInt("Infinity", 24); // -> 151176378
// ...
parseInt("Infinity", 29); // -> 385849803
parseInt("Infinity", 30); // -> 13693557269
// ...
parseInt("Infinity", 34); // -> 28872273981
parseInt("Infinity", 35); // -> 1201203301724
parseInt("Infinity", 36); // -> 1461559270678...
parseInt("Infinity", 37); // -> NaNAttenzione anche quando si svolge il parsing di null:
parseInt(null, 24); // -> 23💡 Spiegazione:
Si sta convertendo
nullalla stringa"null"e provando poi a convertirla a sua volta. Per le radici da 0 a 23, non ci sono numerali per svolgere la conversione, quindi viene restituito NaN. A 24,"n", la 14-esima lettera, viene aggiunta al sistema di numerazione. A 31,"u", la 21-esima lettera, viene aggiunta e l'intera stringa può essere decodificata. A 37 non c'è più un valido insieme di numerazione che si può generare quindi viene restituitoNaN.— “parseInt(null, 24) === 23… wait, what?” at StackOverflow
Non dimentichiamoci del sistema di numerazione ottale:
parseInt("06"); // 6
parseInt("08"); // 8 se è presente il supporto a ECMAScript 5
parseInt("08"); // 0 se assente il supporto a ECMAScript 5💡 Spiegazione: Se la stringa in input inizia con "0", la radice è 8 (octal) o 10 (decimal). Quale radice viene scelta dipende dall'implementazione. ECMAScript 5 specifica l'utilizzo di 10 (decimal), Ma non è ancora supportata da tutti i browser. Per questo motivo è sempre meglio specificare una radice quando si utilizza parseInt.
parseInt converte sempre l'input in stringa:
parseInt({ toString: () => 2, valueOf: () => 1 }); // -> 2
Number({ toString: () => 2, valueOf: () => 1 }); // -> 1Attenzione quando si svolge il parsin di valori in virgola mobile:
parseInt(0.000001); // -> 0
parseInt(0.0000001); // -> 1
parseInt(1 / 1999999); // -> 5💡 Spiegazione: ParseInt prende una stringa come argomento e restituisce un intero in base alla radice specificata. ParseInt inoltre elimina tutto ciò che viene dopo e incluso il primo carattere non numerico nella stringa passata come parametro. 0.000001 Viene convertito nella stringa "0.000001" e parseInt restituisce 0. Quando 0.0000001 viene convertito in stringa viene interpretato come "1e-7" e quindi parseInt restituisce 1. 1/1999999 viene interpretato come 5.00000250000125e-7 e parseInt restituisce 5.
Math con `true` e `false`
Facciamo un po' di calcoli:
true -
true +
// -> 2
(true + true) *
(true + true) -
true; // -> 3Hmmm... 🤔
💡 Spiegazione:
Possiamo forzare dei valori a numeri utilizzando il costruttore di Number. È abbastanza ovvio che true venga forzato a 1:
Number(true); // -> 1L'operatore unario + prova a convertire il suo valore in un numero. Può convertire la rappresentazione testuale di interie e float, così come i valori non testuali true, false, e null. Se non riesce a svolgere il parsing di un particolare valore, restuituirà NaN. Questo significa che possiamo forzare facilmente true a 1:
+true; // -> 1Quando svolgiamo addizioni o moltiplicazioni, viene invocato il metodo ToNumber. In base alla specifica questo metodo restituisce:
Se
parametroè true, restituisci 1. Separametroè false, restituisci +0.
È questo il motivo per il quale possiamo sommare valori booleani e ottenere risultati corretti.
Sezioni corrispondenti:
I commenti HTML sono validi anche in JavaScript
Non ci crederai, ma `
(function() {
return
{
b: 10;
}
})(); // -> undefined💡 Spiegazione:
return e l'espressione da restituire devono essere sulla stessa linea:
(function() {
return {
b: 10
};
})(); // -> { b: 10 }Questo a causa di un concetto chiamato Automatic Semicolon Insertion, che inserisce automagicamente punti e virgola dopo la maggior parte degli a capo. Nel primo esempio, c'è un punto e virgola inserito tra l'istruzione return e l'oggetto, quindi la funzione restituisce undefined e l'oggetto non viene mai valutato.
Concatenare assegnamenti su un object
var foo = { n: 1 };
var bar = foo;
foo.x = foo = { n: 2 };
foo.x; // -> undefined
foo; // -> {n: 2}
bar; // -> {n: 1, x: {n: 2}}Da destra a sinistra, {n: 2} viene assegnato a foo, e il risultato di questo assegnamento {n: 2} viene assegnato a foo.x, ecco perché bar è {n: 1, x: {n: 2}} in quanto bar è un riferimento a foo. Ma perché foo.x è undefined mentre bar.x non lo è?
💡 Spiegazione:
Foo e bar referenziano lo stesso oggetto {n: 1}, e gli lvalues vengono risolti prima dell'assegnamento. foo = {n: 2} sta creando un nuovo oggetto, quindi foo viene aggiornato per referenziare il nuovo oggetto. Il trick qui è in foo.x = ... in quanto il lvalue è stato risolto precedentemente e referenzia ancora il vecchio oggetto foo = {n: 1} e lo aggiorna inserendo il valore x. Dopo questa catena di assegnamenti, bar continua a referenziare il vecchio oggetto foo, ma foo referenzia il nuovo oggetto {n: 2}, dove x non esiste.
È equivalente a:
var foo = { n: 1 };
var bar = foo;
foo = { n: 2 }; // -> {n: 2}
bar.x = foo; // -> {n: 1, x: {n: 2}}
// bar.x point to the address of the new foo object
// it's not equivalent to: bar.x = {n: 2}Accedere alle properties di un object con gli array
var obj = { property: 1 };
var array = ["property"];
obj[array]; // -> 1E per quanto concerne gli array pseudo-multidimensionali?
var map = {};
var x = 1;
var y = 2;
var z = 3;
map[[x, y, z]] = true;
map[[x + 10, y, z]] = true;
map["1,2,3"]; // -> true
map["11,2,3"]; // -> true💡 Spiegazione:
L'operatore parentesi quadre [] converte l'espressione usando il metodo toString. Convertire un array di un solo elemento in una stringa è come convertire l'elemento contenuto nell'array in stringa.
["property"].toString(); // -> 'property'Null e gli operatori relazionali
null > 0; // false
null == 0; // false
null >= 0; // true💡 Spiegazione:
Per farla breve, se null che è minore di 0 è false, allora null >= 0 è true. Leggi la spiegazione approfondita per questo qui.
`Number.toFixed()` mostra numeri diversi
Number.toFixed() può comportarsi in modo bizzarro in certi browser. Guarda l'esempio seguente:
(0.7875).toFixed(3);
// Firefox: -> 0.787
// Chrome: -> 0.787
// IE11: -> 0.788
(0.7876).toFixed(3);
// Firefox: -> 0.788
// Chrome: -> 0.788
// IE11: -> 0.788💡 Spiegazione:
L'istinto potrebbe farci pensare che IE11 sia corretto e Firefox/Chrome sbaglino, la realtà è che Firefox/Chrome stanno rispettando gli standard per i numeri in virgola mobile (IEEE-754 Floating Point), mentre IE11 sta evitando di rispettarli (quello che probabilmente è) uno sforzo per restituire dei risultati più chiari.
Possiamo vedere perché questo accade con un semplice test:
// Confermare lo strano risultato dell'arrotondamento per difetto di 5
(0.7875).toFixed(3); // -> 0.787
// Sembra essere 5 quando si estende il
// limite a 64-bit (double-precision) di precisione
(0.7875).toFixed(14); // -> 0.78750000000000
// Ma se si supera il limite?
(0.7875).toFixed(20); // -> 0.78749999999999997780I numeri floating point non sono memorizzati come una sequenza di cifre decimali, ma attraverso un metodo più elaborato che produce delle piccole inacuratezze the solitamente vengono eliminate dalle chiamate a toString o simili, ma queste imprecisioni rimangono comunque presenti internamente.
In questo caso, il "5" alla fine era un numero infinitesimamente più piccolo del vero 5. Arrotondandolo ad una precisione ragionevole verrà mostrato come 5... ma internamente non è un 5.
IE11, invece, mostrerà il valore dato in input con degli zeri in coda, anche nel caso di toFixed(20), in quanto sembra forzare l'arrotondamento del valore per evitare problematiche causate dai limiti hardware.
Guarda il riferimento a NOTE 2 sulla definizione per toFixed nelle specifiche ECMA-262.
`Math.max()` più piccolo di `Math.min()`
Math.min(1, 4, 7, 2); // -> 1
Math.max(1, 4, 7, 2); // -> 7
Math.min(); // -> Infinity
Math.max(); // -> -Infinity
Math.min() > Math.max(); // -> true💡 Spiegazione:
- Perché Math.max() è più piccolo di Math.min()? by Charlie Harvey
Confrontare `null` con `0`
La seguente espressione sembra introdurre una contraddizione:
null == 0; // -> false
null > 0; // -> false
null >= 0; // -> trueCome può null non essere uguale a, o maggiore di 0, se null >= 0 è effettivamente true? (Funziona anche con "inferiore a" nello stesso modo.)
💡 Spiegazione:
Il modo in cui queste tre espressioni vengono valutate sono tutti diversi ed è per questo che viene prodotto questo comportamento un po' inaspettato.
Per prima cosa analizziamo il comportamento dell'operatore di abstract equality comparison, null == 0.
Solitamente, se l'operatore non riesce a confrontare i suoi operanti in modo opportuno, li converte in numeri e compara questi ultimo. Quindi ci si può aspettare il seguente comportamento:
// This is not what happens
(null == 0 + null) == +0;
0 == 0;
true;Invece, secondo una lettura attenta delle specifiche, la conversione a numero non avviene per l'operando che ha valore null o undefined. Quindi, se abbiamo null da un lato del simbolo uguale, l'altro lato deve essere null o undefined per fare in modo che venga restituito true. Dato che non è questo il caso, verrà restituito false.
Ora analizziamo l'operatore di comparazione null > 0. Qui l'algoritmo, a differenza dell'operatore di abstract equality, convertirà null in un numero. Quindi il comportamento sarà il seguente:
null > 0
+null = +0
0 > 0
falseInfine, analizziamo l'operatore relazionale null >= 0. Si può obiettare che questa espressione dovrebbe essere il risultato di null > 0 || null == 0; se fosse così, allora il risultato dell'espressione dovrebbe essere false. Invece l'operatore >= funziona in un modo completamente diverso, dove praticamente prende l'opposto dell'operatore <. Dato che l'esempio con l'operatore "maggiore di" produce lo stesso valore dell'operatore "minore di", l'espressione verrà valutata nel modo seguente:
null >= 0;
!(null < 0);
!(+null < +0);
!(0 < 0);
!false;
true;Alcune ridichiarazioni di variabili
JS permette la ridichiarazione di variabili:
a;
a;
// È valida anche questa
a, a;Funziona anche in modalità strict:
var a, a, a;
var a;
var a;💡 Spiegazione:
Tutte le definizione sono state unite in una sola.
Comportamento di default di Array.prototype.sort()
Supponiamo di voler ordinare un array di numeri.
[ 10, 1, 3 ].sort() // -> [ 1, 10, 3 ]💡 Spiegazione:
L'ordinamento di default viene realizzato convertendo gli elementi in stringhe, quindi confrontando i loro valore in UTF-16.
Suggerimento
Passa una comparefn se vuoi ordinare qualcosa che non è una stringa.
[ 10, 1, 3 ].sort((a, b) => a - b) // -> [ 1, 3, 10 ]resolve() non restituisce un'istanza di Promise
const theObject = {
a: 7
};
const thePromise = new Promise((resolve, reject) => {
resolve(theObject);
}); // -> Instance object di Promise
thePromise.then(value => {
console.log(value === theObject); // -> true
console.log(value); // -> { a: 7 }
});Il value che viene risolto da thePromise è esattamente theObject.
E se inserissimo un'altra Promise all'interno della funzione resolve?
const theObject = new Promise((resolve, reject) => {
resolve(7);
}); // -> Promise instance object
const thePromise = new Promise((resolve, reject) => {
resolve(theObject);
}); // -> Promise instance object
thePromise.then(value => {
console.log(value === theObject); // -> false
console.log(value); // -> 7
});💡 Spiegazione:
Questa funzione appiattisce livelli annidati di oggetti promise-like (ad esempio una promise che risolve a una promise che risolve a qualcosa) in un singolo livello.
La specifica è ECMAScript 25.6.1.3.2 Promise Resolve Functions. But it is not quite human-friendly.
📚 Other resources
- wtfjs.com — una raccolta di irregolarità e stranezze davvero speciali con un pizzico di momenti dolorosamente controintuitivi per il linguaggio del web.
- Wat — A lightning talk by Gary Bernhardt from CodeMash 2012
- What the... JavaScript? — Il talk di Kyle Simpsons alla Forward 2 che prova a "estrarre le stramberie” da JavaScript. Il suo desiderio è aiutare a scrivere un codice più pulito, elegante e leggibile, ispirare le persone a contribuire alla community open source.
🎓 License
File: README-kr.md
아니 X발? 자바스크립트 이게 뭐야??
재미있고 교묘한 JavaScript 예제
JavaScript는 훌륭한 언어입니다. JavaScript는 구문이 단순하며 큰 생태계를 가지고 있습니다. 가장 중요한 점은 훌륭한 공동체를 가지고 있다는 것입니다.
동시에, 우리 모두는 JavaScript가 까다로운 부분을 가진 꽤 재미있는 언어라는 것을 알고 있습니다. 몇몇 특징은 우리의 일상적인 일을 순식간에 지옥으로 바꾸기도 하고, 우리를 크게 웃게 만들기도 합니다.
WTFJS의 아이디어는 Brian Leroux에 속해있습니다. 이 목록들은 그의 이야기에서 꽤 영감을 받았습니다. “WTFJS” at dotJS 2012:
NPM 패키지 메뉴스크립트
이 핸드북은 npm를 이용하여 설치할 수 있습니다. 그냥 실행합시다:
$ npm install wtfjs이제 당신은 커맨드 창에서 'wtfjs'를 실행할 수 있게 되었습니다. 당신이 선택한 '$PAGER'에서 'wtfjs'가 열릴 것 입니다. 아니면 계속 여기서 읽어도 됩니다.
출처는 https://github.com/denysdovhan/wtfjs 여기에서 확인 할 수 있습니다.
번역
현재, wtfjs는 아래와 같은 언어로 번역되었습니다:
Table of Contents
- 💪🏻 시작하기에 앞서
- ✍🏻 표기법
- 👀 예제
- [
[]와 ` - [
true는 ` - true는 false
- baNaNa
NaN은NaN이 아니다- 이것은 실패다
[]은 truthy 이지만true는 아니다null은 falsy 이지만false은 아니다document.all은 객체이지만undefined이다- 최소 값은 0 보다 크다
- 함수는 함수가 아니다
- 배열 추가
- 배열의 후행 쉼표
- 배열 평등은 몬스터
undefined과NumberparseInt은 나쁜 놈이다true와false를 이용한 수학- HTML 주석은 JavaScript에서도 유효하다
NaN은 숫자가 아니다[]과null은 객체이다- 마법처럼 증가하는 숫자
- 정확도
0.1 + 0.2 - 패치 번호
- 세 숫자의 비교
- 재미있는 수학
- RegExps 추가
- 문자열은
String의 인스턴스가 아니다 - backticks으로 함수 호출
- Call call call
constructor속성- 객체 속성의 키로서의 객체
__proto__을 사용한 프로토 타입 접근`${{Object}}`- 디폴트 값으로 구조 해제
- Dots와 spreading
- 라벨
- 중첩된 라벨들
- 교활한
try..catch - 이것은 다중 상속인가?
- 스스로 생성되는 Generator
- 클래스의 클래스
- 강제할 수 없는 객체
- 까다로운 화살표 함수
- 화살표 함수는 생성자가 될 수 없다
arguments와 화살표 함수- 까다로운 return
- 객체에 할당 연결
- 배열을 사용한 객체 속성 접근 s
- Null 및 관계 연산자
Number.toFixed()다른 숫자 표시Math.max()이하Math.min()null과0비교- 동일한 변수 재선언
- 디폴트 동작 Array.prototype.sort()
- resolve()은 Promise instance를 반환하지 않는다
- [
- 📚 기타 resources
- 🎓 License
💪🏻 시작하기에 앞서
— “Just for Fun: 우연한 혁명가의 이야기”, Linus Torvalds
이 목록의 주요 목표는 가능한 JavaScript의 몇 가지의 엄청난 예제들을 모으고, 작동 방식을 설명하는 것 입니다. 이전에 우리가 몰랐던 것들을 배우는 것이 재미있기 때문입니다.
당신이 초보자라면, 이 노트를 사용하여 JavaScript에 대해 자세히 알아볼 수 있을 것입니다. 이 노트의 설명을 읽는 것에 더 많은 시간을 할애할 수 있기를 바랍니다.
당신이 전문 개발자라면, 우리가 사랑하는 JavaScript의 모든 기이한 점과 예상치 못한 것들에 대한 예시에 훌륭한 참조로 간주할 수 있습니다.
어쨌든, 이것을 읽읍시다. 당신은 아마 새로운 것들을 찾을 수 있을 것입니다.
✍🏻 표기법
// -> 식의 결과를 표시하는 데 사용됩니다. 예를 들면:
1 + 1; // -> 2// > console.log 또는 다른 출력의 결과를 의미합니다. 예를 들면:
console.log("hello, world!"); // > hello, world!// 설명에 사용되는 주석입니다. 예를 들면:
// Assigning a function to foo constant
const foo = function() {};👀 예제
`[]`와 `![]은 같다`
배열은 배열이 아닙니다:
[] == ![]; // -> true💡 설명:
추상 항등 연산자는 양쪽을 숫자로 변환하여 비교하고, 서로 다른 이유로 양 쪽의 숫자는 0이 됩니다. 배열은 truthy 하므로, 오른쪽의 값은 0을 강요하는 truthy value의 반대 값 즉, false입니다. 그러나 왼쪽은 빈 배열은 먼저 boolean이 되지 않고 숫자로 강제 변환되고 빈 배열은 truthy 임에도 불구하고 0으로 강요됩니다.
이 표현식이 어떻게 단순화 되는지는 아래와 같습니다:
+[] == +![];
0 == +false;
0 == 0;
true;`true`는 `![]`와 같지 않지만, `[]`와도 같지 않다
배열은 true와 같지 않지만 배열이 아닌것도 true와 같지 않습니다;
배열은 false와 같지만 배열이 아닌것도 false와 같습니다:
true == []; // -> false
true == ![]; // -> false
false == []; // -> true
false == ![]; // -> true💡 설명:
true == []; // -> false
true == ![]; // -> false
// According to the specification
true == []; // -> false
toNumber(true); // -> 1
toNumber([]); // -> 0
1 == 0; // -> false
true == ![]; // -> false
![]; // -> false
true == false; // -> falsefalse == []; // -> true
false == ![]; // -> true
// According to the specification
false == []; // -> true
toNumber(false); // -> 0
toNumber([]); // -> 0
0 == 0; // -> true
false == ![]; // -> true
![]; // -> false
false == false; // -> truetrue는 false
!!"false" == !!"true"; // -> true
!!"false" === !!"true"; // -> true💡 설명:
다음 단계를 고려합시다:
// true is 'truthy' and represented by value 1 (number), 'true' in string form is NaN.
true == "true"; // -> false
false == "false"; // -> false
// 'false' is not the empty string, so it's a truthy value
!!"false"; // -> true
!!"true"; // -> truebaNaNa
"b" + "a" + +"a" + "a"; // -> 'baNaNa'이것은 JavaScript에서 구식 농담이지만 재해석 되었습니다. 원본은 다음과 같습니다:
"foo" + +"bar"; // -> 'fooNaN'💡 설명:
식은 'foo' + (+'bar')으로 평가되고 숫자가 아닌 'bar' 형태로 변환됩니다.
`NaN`은 `NaN`이 아니다
NaN === NaN; // -> false💡 설명:
아래의 사항들로 동작의 논리를 엄격하게 정의합니다:
- 만약
Type(x)와Type(y)가 다르면 false를 반환합니다.- 만약
Type(x)이 숫자이고
x가 NaN이면 false를 반환합니다.y가 NaN이면 false를 반환합니다.- … … …
IEEE에서 정의한 NaN:
4 개의 상호 배타적인 관계 : 보다 작음, 같음, 보다 큼, 순서 없음. 마지막의 경우 하나 이상의 피연산자가 NaN일 때 발생합니다. 모든 NaN은 자신을 포함한 모든 것과 순서 없이 비교해야 합니다.
— “IEEE754 NaN 값에 false를 반환하는 것의 근거는 무엇입니까?” StackOverflow에서
이것은 실패다
당신은 믿지 않을지도 모르지만 …
(![] + [])[+[]] +
(![] + [])[+!+[]] +
([![]] + [][[]])[+!+[] + [+[]]] +
(![] + [])[!+[] + !+[]];
// -> 'fail'💡 설명:
기호를 하나하나 나누면 아래와 같은 패턴이 자주 발생하는 것을 알 수 있습니다:
![] + []; // -> 'false'
![]; // -> false그래서 []를 false으로 바꾸는 시도를 해봅니다. 하지만 많은 내부 함수 호출(binary + Operator -> ToPrimitive -> [[DefaultValue]])때문에 오른쪽 피 연산 문자열로 변환하게 됩니다:
![] + [].toString(); // 'false'문자열을 배열로 생각하면 [0]을 통해 첫 번째 문자에 접근할 수 있습니다:
"false"[0]; // -> 'f'나머지는 분명하지만 i는 꽤 까다롭습니다. fail 속 i는 'falseundefined'라는 문자열을 생성하고 ['10'] 인덱스를 사용하여 요소를 잡습니다.
`[]`은 truthy 이지만 `true`는 아니다
배열은 truthy 한 값이지만 true와 같지는 않다.
!![] // -> true
[] == true // -> false💡 설명:
다음은 ECMA-262 명세된 것의 세션에 대한 링크입니다:
`null`은 falsy 이지만 `false`은 아니다
null은 falsy 값이라는 사실에도 불구하고 false는 아닙니다.
!!null; // -> false
null == false; // -> false동시에 0 또는 ''와 같은 falsy 값은 false와 동일합니다.
0 == false; // -> true
"" == false; // -> true💡 설명:
설명은 이전 예제와 동일합니다. 다음은 해당 링크입니다:
`document.all`은 객체이지만 `undefined`이다
⚠️ 이 파트는 브라우저 API 의 일부이며 Node.js 환경에서는 작동하지 않습니다.⚠️
document.all은 배열과 같은 클래스이고 페이지의 DOM 노드에 대한 엑세스를 제공한다는 사실에도 불구하고 typeof함수의 undefined으로 반응합니다.
document.all instanceof Object; // -> true
typeof document.all; // -> 'undefined'동시에 document.all은 undefined와 동일하지 않습니다.
document.all === undefined; // -> false
document.all === null; // -> false하지만 동시에:
document.all == null; // -> true💡 설명:
특히 이전 버전의 IE에서
document.all은 DOM 요소에 접근하는 방법을 사용했습니다. 이것은 표준이 된 적은 없지만 이전 JavaScript 코드에서 사용되었습니다. 새로운 APIs(document.getElementById와 같은)에서 표준이 진행되었을 때 이 API 호출은 쓸모 없게 되었고 표준 위원회는 이를 어떻게 처리할지 결정해야 했습니다. 광범위하게 사용되기 때문에 그들은 API를 유지하기로 결정했지만 JavaScript 명세된 것을 고의로 위반했습니다.
이것이undefined의 상황에서 엄격한 평등 비교을 사용했을 때false를 응답하고 추상 평등 비교을 사용할 때true로 응답하는 이유는 명시적으로 허용하는 명세된 것의 의도적인 위반 때문입니다.— “오래된 특징 - document.all” WhatWG의 HTML 명세된 것
— “Chapter 4 - ToBoolean - Falsy values” YDKJS의 Types & Grammar
최소 값은 0 보다 크다
Number.MIN_VALUE은 0 보다 큰 가장 작은 숫자입니다:
Number.MIN_VALUE > 0; // -> true💡 설명:
Number.MIN_VALUE은5e-324입니다. 즉, 부동 소수점 정밀도 내에서 표현할 수 있는 가장 작은 양수입니다. 이 말은 0 에 도달할 수 있는 가장 가까운 값이라는 의미 입니다. 이것은 소수가 제공할 수 있는 최상의 값이라고 정의할 수 있습니다.비록 엄격하게 실제로 숫자는 아니지만 전체적으로 가장 작은 값은
Number.NEGATIVE_INFINITY이라고 할 수 있습니다.— “자바 스크립트에서 왜
0은Number.MIN_VALUE보다 작습니까?” StackOverflow에서
함수는 함수가 아니다
⚠️ V8 v5.5 또는 그 이하의 버전에서는 버그가 있을 수 있습니다.(Node.js <=7) ⚠️
이것을 undefined is not a function 모두가 알고 있지만 이건 어떨까요?
// Declare a class which extends null
class Foo extends null {}
// -> [Function: Foo]
new Foo() instanceof null;
// > TypeError: function is not a function
// > at … … …💡 설명:
이것은 명세된 것의 일부가 아닙니다. 현재 수정된 버그 일 뿐이므로 향후 아무 문제 없을 것입니다.
배열 추가
두 개의 배열을 추가하려면 어떻게 해야 할까요?
[1, 2, 3] + [4, 5, 6]; // -> '1,2,34,5,6'💡 설명:
연결이 발생합니다.차근차근 다음을 봅시다:
[1, 2, 3] +
[4, 5, 6][
// call toString()
(1, 2, 3)
].toString() +
[4, 5, 6].toString();
// concatenation
"1,2,3" + "4,5,6";
// ->
("1,2,34,5,6");배열의 후행 쉼표
4 개의 빈 배열을 만듭니다. 그럼에도 불구하고 후행 쉼표로 인해 세가지 , 요소가 있는 배열을 얻게 됩니다:
let a = [, , ,];
a.length; // -> 3
a.toString(); // -> ',,'💡 설명:
후행 쉼표 ("마지막 쉼표"라고도 함)는 JavaScript 에 새로운 요소, 매개 변수 또는 속성을 추가할 때 유용하게 사용할 수 있습니다. 만약 새 속성을 추가하려는 상황에서 이미 후행 쉼표를 사용하고 있는 경우 이전 마지막 줄을 수정하지 않고 새 줄을 추가할 수 있습니다. 이렇게 하면 버전 관리가 더 깔끔 해지고 코드 편집이 덜 번거로울 수 있습니다.
— 후행 쉼표 MDN에서
배열 평등은 몬스터
배열 평등은 아래에서 볼 수 있듯 JavaScript에서는 몬스터입니다:
[] == '' // -> true
[] == 0 // -> true
[''] == '' // -> true
[0] == 0 // -> true
[0] == '' // -> false
[''] == 0 // -> true
[null] == '' // true
[null] == 0 // true
[undefined] == '' // true
[undefined] == 0 // true
[[]] == 0 // true
[[]] == '' // true
[[[[[[]]]]]] == '' // true
[[[[[[]]]]]] == 0 // true
[[[[[[ null ]]]]]] == 0 // true
[[[[[[ null ]]]]]] == '' // true
[[[[[[ undefined ]]]]]] == 0 // true
[[[[[[ undefined ]]]]]] == '' // true💡 설명:
아래의 예제를 주의 깊게 살펴 보아야 합니다! 이 동작은 7.2.13 추상 동등 비교에 설명되어 있습니다.
`undefined`과 `Number`
Number생성자에 인수를 전달하지 않으면 0 값을 얻게 됩니다. 실제 인수가 없는 경우 undefined값이 형식 인수에 할당되기 때문에 인수가 없는 Number는 매개 변수 값으로 undefined를 사용합니다. 그러나 undefined를 통과하면 NaN을 얻을 수 있습니다.
Number(); // -> 0
Number(undefined); // -> NaN💡 설명:
명세된 것에 따르면:
- 함수의 호출로 인수가 전달되지 않은 경우
n은+0이 됩니다. - 또는 let
nbe ?ToNumber(value). undefined의 경우ToNumber(undefined)는NaN으로 반환해야 합니다.
다음은 해당 부분입니다:
`parseInt`은 나쁜 놈이다
parseInt은 특이한 점으로 유명합니다:
parseInt("f*ck"); // -> NaN
parseInt("f*ck", 16); // -> 15💡 설명: 이는 parseInt알 수 없는 문자에 도달할 때까지 문자별로 계속 구문 분석을 하기 때문에 발생합니다. 'f*ck'에서 f는 16 진수로 15입니다.
Infinity정수로 파싱하는 것은…
//
parseInt("Infinity", 10); // -> NaN
// ...
parseInt("Infinity", 18); // -> NaN...
parseInt("Infinity", 19); // -> 18
// ...
parseInt("Infinity", 23); // -> 18...
parseInt("Infinity", 24); // -> 151176378
// ...
parseInt("Infinity", 29); // -> 385849803
parseInt("Infinity", 30); // -> 13693557269
// ...
parseInt("Infinity", 34); // -> 28872273981
parseInt("Infinity", 35); // -> 1201203301724
parseInt("Infinity", 36); // -> 1461559270678...
parseInt("Infinity", 37); // -> NaNnull을 파싱하는 것에도 주의합시다:
parseInt(null, 24); // -> 23💡 설명:
null을 문자열"null"로 변환하려고 합니다. 0 부터 23 까지의 기수에 대해서 변환할 수 있는 숫자가 없으므로 NaN을 반환합니다. 24 에,"n", 14 번째 문자가 숫자 체계에 추가됩니다. 31에,"u", 21 번째 문자가 추가되고 전체 문자열을 디코딩 할 수 있게 되었습니다. 37에서 더 이상 생성할 수 있는 유효 숫자 집합이 없으며NaN이 반환됩니다.— “parseInt(null, 24) === 23… wait, what?” StackOverflow에서
8 진수에 대해서 잊지맙시다:
parseInt("06"); // 6
parseInt("08"); // 8 if support ECMAScript 5
parseInt("08"); // 0 if not support ECMAScript 5💡 설명: 입력 문자열이 "0"으로 시작하는 경우, 기수는 8 (octal) 또는 10 (decimal)입니다. 정확히는 어떤 기수가 선택되는가는 구현에 따라 다릅니다. ECMAScript 5는 10 (decimal)진수를 사용하도록 지정하지만 모든 브라우저가 이것을 지원하지는 않습니다. 그러므로 parseInt을 사용할 때는 항상 기수를 지정합시다.
parseInt항상 입력을 문자열로 변환:
parseInt({ toString: () => 2, valueOf: () => 1 }); // -> 2
Number({ toString: () => 2, valueOf: () => 1 }); // -> 1부동 소수점값을 파싱하는 동안 주의하세요.
parseInt(0.000001); // -> 0
parseInt(0.0000001); // -> 1
parseInt(1 / 1999999); // -> 5💡 설명: ParseInt은 문자열 인수를 취하고 지정된 기수의 정수를 반환합니다. 또한 ParseInt은 문자열 매개 변수에서 첫 번째가 아닌 숫자를 포함하여 모든 것을 제거합니다. 0.000001은 문자열 "0.000001"로 바뀌고parseInt은0으로 반환됩니다.0.0000001이 문자열로 변환되면"1e-7"로 되므로parseInt은1을 반환합니다.1/1999999은5.00000250000125e-7로 해석되고parseInt은5`을 리턴합니다.
`true`와 `false`를 이용한 수학
몇 가지 수학을 해봅시다:
true -
true +
// -> 2
(true + true) * (true + true) -
true; // -> 3흠… 🤔
💡 설명:
Number생성자를 사용하여 값을 숫자로 강제 변환할 수 있습니다. true가 1로 강제되는 것은 분명합니다:
Number(true); // -> 1단항 더하기 연산자는 값을 숫자로 변환하려고 합니다. 이것은 정수와 소수의 문자열 표현일 뿐아니라 비문자열인 true, false와 null값도 변환할 수 있습니다. 특정 값을 파싱할 수 없는 경우 NaN으로 평가됩니다. 그것은 더 쉽게 true를 1로 강제할 수 있음을 의미합니다:
+true; // -> 1덧셈 또는 곱셈을 수행할 때 ToNumber메서드가 호출됩니다. 명세된 것에 따르면 아래의 메서드를 반환합니다:
만약
argument이 true이면 1이 반환됩니다. 만약argument이 false이면 +0이 반환됩니다.
이 때문에 boolean 값을 일반 숫자로 추가하고 올바른 결과를 얻을 수 있습니다.
해당 부분:
HTML 주석은 JavaScript에서도 유효하다
이것이 `
(function() {
return
{
b: 10;
}
})(); // -> undefined💡 설명:
return과 반환된 표현식은 같은 줄에 있어야 합니다:
(function() {
return {
b: 10
};
})(); // -> { b: 10 }이는 대부분 줄 바꿈 뒤에 세미콜론을 자동으로 삽입하는 자동 세미콜론 삽입이라는 개념 때문입니다. 첫번째 예시에서 return문과 객체 리터럴 사이에 세미콜론이 삽입되어 있으므로 함수는 undefined를 반환하고 객체 리터럴은 평가되지 않습니다.
객체에 할당 연결
var foo = { n: 1 };
var bar = foo;
foo.x = foo = { n: 2 };
foo.x; // -> undefined
foo; // -> {n: 2}
bar; // -> {n: 1, x: {n: 2}}오른쪽에서 왼쪽으로, {n: 2}이 foo에 할당되고, 이 할당의 결과{n: 2}는 foo.x 에 할당되어 있고, bar는 foo를 할당하고 있기 때문에 bar는 {n: 1, x: {n: 2}}입니다. 그런데 bar.x가 아닌 반면에 foo.x는 왜 정의되지 않은 것일까요?
💡 설명:
Foo와 bar는 같은 객체 {n: 1}를 참조하고 있고 lvalues는 할당되기 전에 결정됩니다. foo = {n: 2}은 새로운 객체를 생성하고 있으므로 foo는 새로운 객체를 참조하도록 업데이트됩니다. 트릭은 foo.x = ...의 foo 에 있습니다. lvalue 값은 사전에 확인되었고 여전히 이전 foo = {n:1} 객체를 참조하고 x 값을 추가하여 업데이트합니다. 체인 할당 후에도 bar는 여전히 이전의 foo 객체를 참조하지만 foo는 x가 존재하지 않는 새로운 {n: 2}객체를 참조합니다.
다음과 동일합니다:
var foo = { n: 1 };
var bar = foo;
foo = { n: 2 }; // -> {n: 2}
bar.x = foo; // -> {n: 1, x: {n: 2}}
// bar.x point to the address of the new foo object
// it's not equivalent to: bar.x = {n: 2}배열을 사용한 객체 속성 접근 s
var obj = { property: 1 };
var array = ["property"];
obj[array]; // -> 1다차원 배열의 수도코드는 무엇입니까?
var map = {};
var x = 1;
var y = 2;
var z = 3;
map[[x, y, z]] = true;
map[[x + 10, y, z]] = true;
map["1,2,3"]; // -> true
map["11,2,3"]; // -> true💡 설명:
대괄호 연산자 []는 toString을 사용하여 전달된 식을 변환합니다. 단일 요소 배열을 문자열으로 변환하는 것은 포함된 요소를 문자열로 변환하는 것과 유사합니다:
["property"].toString(); // -> 'property'Null 및 관계 연산자
null > 0; // false
null == 0; // false
null >= 0; // true💡 설명:
긴 얘기를 짧게 하자면, 만약 null이 0 보다 작으면 false이고 null >= 0은 true입니다. 여기에서 이에 대한 자세한 설명을 읽으십시오 여기.
`Number.toFixed()` 다른 숫자 표시
Number.toFixed()는 다른 브라우저에서 약간 이상하게 작동할 수 있습니다. 아래 예를 확인하세요:
(0.7875).toFixed(3);
// Firefox: -> 0.787
// Chrome: -> 0.787
// IE11: -> 0.788
(0.7876).toFixed(3);
// Firefox: -> 0.788
// Chrome: -> 0.788
// IE11: -> 0.788💡 설명:
본능적으로 IE11은 올바르고 Firefox/Chrome이 잘못되었다고 생각할 수 있지만 사실은 Firefox/Chrome이 더 직접적으로 숫자의 표준(IEEE-754 Floating Point)을 준수하고 있는 반면 IE11는 더 명확한 결과를 제공하기 위한 노력으로 그것들을 미세하게 거역하고 있습니다.
몇 가지 간단한 테스트를 통해 이 문제가 발생하는 이유를 확인할 수 있습니다:
// Confirm the odd result of rounding a 5 down
(0.7875).toFixed(3); // -> 0.787
// It looks like it's just a 5 when you expand to the
// limits of 64-bit (double-precision) float accuracy
(0.7875).toFixed(14); // -> 0.78750000000000
// But what if you go beyond the limit?
(0.7875).toFixed(20); // -> 0.78749999999999997780부동 소수점 번호는 내부적으로 10진수 리스트로 저장되는 것이 아니라 대게 toString과 유사한 호출에 의해 반올림되지만 실제로 내부적으로는 매우 복잡한 방법론을 통해 저장됩니다.
이 경우 끝에 있는 "5"는 실제로 진짜 5 보다 매우 작은 부분입니다.합리적인 길이로 반올림하면 5...으로 렌더링되지만 실제로는 내부적으로 5는 아닙니다.
그러나 IE11은 하드웨어 한계에서 문제를 줄이기 위해 값을 강제로 반올림하는 것처럼 보이기 때문에 toFixed(20)의 사례에서도 끝에 0만 추가한 값을 입력 보고 할 것입니다.
toFixed에 대한 ECMA-262 정의의 NOTE 2를 참고하세요.
`Math.max()` 이하 `Math.min()`
Math.min(1, 4, 7, 2); // -> 1
Math.max(1, 4, 7, 2); // -> 7
Math.min(); // -> Infinity
Math.max(); // -> -Infinity
Math.min() > Math.max(); // -> true💡 설명:
- Why is Math.max() less than Math.min()? by Charlie Harvey
`null`과 `0` 비교
다음 표현들은 모순을 의미한것 같습니다:
null == 0; // -> false
null > 0; // -> false
null >= 0; // -> true만약 null >= 0이 실제로 true이면 어떻게 null이 0과 같지도 않고 크지도 않을까요? (이는 보다 적은 경우에도 동일하게 작동합니다.)
💡 설명:
이 세가지 식이 평가되는 방식은 모두 다르며 예기치 않은 동작들을 생성합니다.
첫째, 추상 평등 비교 null == 0입니다. 일반적으로 이 연산자가 양쪽 값을 제대로 비교할 수없으면 둘 다 숫자로 변환한 후 숫자를 비교합니다. 그러면 다음 동작을 예상할 수 있습니다:
// This is not what happens
(null == 0 + null) == +0;
0 == 0;
true;그러나 spec을 자세히 읽어보면 숫자 변환은 null이나 undefined의 한 면에서는 일어나지 않습니다. 그러므로 등호 한쪽에 null이 있으면 다른 한쪽에 null 또는 undefined가 있어야 true를 리턴합니다. 이 경우 그렇지 않기 때문에false을 리턴합니다.
다음은 관계 비교 null > 0입나다. 여기서 알고리즘은 추상 평등 연산자와 달리 null을 숫자로 변환합니다. 따라서 다음과 같은 동작이 발생합니다:
null > 0 + null = +0;
0 > 0;
false;마지막으로 관계 비교 null >= 0입니다. 이 표현이 null > 0 || null == 0의 결과라고 주장할 수 있는데, 만약 그렇다면, 위의 결과는 이 역시 false라는 것을 의미할 것입니다. 그러나 사실 >=연산자는 매우 다른 방식으로 작동하는데, 이는 기본적으로 <연산자와 반대되는 방식입니다. 위보다 큰 연산자를 사용한 예도 연산자보다 작기 때문에 이 식은 실제로 다음과 같이 평가됩니다.
null >= 0;
!(null < 0);
!(+null < +0);
!(0 < 0);
!false;
true;동일한 변수 재선언
JavaScript에서는 변수를 다시 선언할 수 있습니다:
a;
a;
// This is also valid
a, a;strict 모드에서도 작동합니다:
var a, a, a;
var a;
var a;💡 설명:
모든 정의가 하나의 정의로 병합됩니다.
디폴트 동작 Array.prototype.sort()
숫자 배열을 정렬해야 한다고 상상해보세요.
[ 10, 1, 3 ].sort() // -> [ 1, 10, 3 ]💡 설명:
기본 정렬 순서는 요소들을 문자열로 변환한 후 UTF-16 코드 단위 값의 시퀀스를 비교할 때 작성됩니다.
힌트
문자열 이외의 정렬을 시도하면 comparefn을 통과시키세요.
[ 10, 1, 3 ].sort((a, b) => a - b) // -> [ 1, 3, 10 ]resolve()은 Promise instance를 반환하지 않는다
const theObject = {
a: 7
};
const thePromise = new Promise((resolve, reject) => {
resolve(theObject);
}); // -> Promise instance object
thePromise.then(value => {
console.log(value === theObject); // -> true
console.log(value); // -> { a: 7 }
});value는 thePromise 정확하게 말하면 theObject에서 해결되는 것 입니다.
resolve함수에 또 다른 Promise를 넣는 것은 어떨까요?
const theObject = new Promise((resolve, reject) => {
resolve(7);
}); // -> Promise instance object
const thePromise = new Promise((resolve, reject) => {
resolve(theObject);
}); // -> Promise instance object
thePromise.then(value => {
console.log(value === theObject); // -> false
console.log(value); // -> 7
});💡 설명:
이 함수는 promise 같은 객체의 중첩된 레이어(예시: 무언가로 해결되는 promise으로 해결되는 promise)를 단일 레이어로 평탄화합니다.
명세서는 ECMAScript 25.6.1.3.2 Promise Resolve Functions입니다. 하지만 그것은 인간 친화적이지 않습니다.
📚 기타 resources
- wtfjs.com — a collection of those very special irregularities, inconsistencies and just plain painfully unintuitive moments for the language of the web.
- Wat — A lightning talk by Gary Bernhardt from CodeMash 2012
- What the... JavaScript? — Kyle Simpsons talk for Forward 2 attempts to “pull out the crazy” from JavaScript. He wants to help you produce cleaner, more elegant, more readable code, then inspire people to contribute to the open source community.