const str = 'Hello world!'
const num = 42
const boo = true
typeof str // ??
typeof num // ??
typeof boo // ??
const str = 'Hello world!'
const num = 42
const boo = true
typeof str // ??
typeof num // ??
typeof boo // ??
const str = 'Hello world!'
const num = 42
const boo = true
typeof str // ??
typeof num // ??
typeof boo // ??
str instanceof String // ??
num instanceof Number // ??
boo instanceof Boolean // ??
const str = 'Hello world!'
const num = 42
const boo = true
typeof str // ??
typeof num // ??
typeof boo // ??
str instanceof String // ??
str instanceof Object // ??
num instanceof Number // ??
num instanceof Object // ??
boo instanceof Boolean // ??
boo instanceof Object // ??
[] + [] == ""
NaN
(Bientôt 8 avec BigInt)
An ECMAScript language type corresponds to values that are directly manipulated by an ECMAScript programmer using the ECMAScript language.
ecma-262 9.0 (ECMAScript 2018)
The ECMAScript language types are Undefined, Null, Boolean, String, Symbol, Number, and Object.
An ECMAScript language value is a value that is characterized by an ECMAScript language type.
Tout autre valeur sera de type Object
(Bientôt 7 types primitifs avec BigInt)
7 retours possibles... mais pas (tout à fait) les mêmes
typeof null // "object"
(function () {
var undefined = 42
var foo
console.log(foo === undefined, foo, undefined)
})()
(function (undefined) {
var foo
console.log(foo === undefined, foo, undefined)
})(42)
(function () {
var undefined = 42
var foo
console.log(foo === undefined, foo, undefined) // false undefined 42
})()
(function (undefined) {
var foo
console.log(foo === undefined, foo, undefined) // false undefined 42
})(42)
Ne faites pas ça !
(function () {
undefined = 42
var foo
console.log(foo === undefined, foo, undefined)
})()
(function () {
'use strict'
undefined = 42
var foo
console.log(foo === undefined, foo, undefined)
})()
(function () {
undefined = 42
var foo
console.log(foo === undefined, foo, undefined) // true undefined undefined
})()
(function () {
'use strict'
undefined = 42 // TypeError
var foo
console.log(foo === undefined, foo, undefined)
})()
Ne faites pas ça !
(function (w, d, undefined) {
// Code where undefined is the undefined value no matter what
})(window, document)
(function (w, d, undefined) {
// Ici undefined a pour valeur undefined quoi qu'il arrive
})(window, document)
(function ($, w, d, undefined) {
// jQuery plugin code
})(jQuery, window, document)
jQuery ? Tay sérieux, mec ?
NaN // Not a Number
0 and -0
Infinity and -Infinity
NaN // Not a Number
0 and -0
Infinity and -Infinity
// Statement/Operation Result Note
typeof NaN "number" // ...Yes, I know, right?
1/0 Infinity
1/-0 -Infinity
typeof Infinity "number"
typeof -Infinity "number"
NaN // Not a Number
0 and -0
Infinity and -Infinity
// Statement/Operation Result Note
typeof NaN "number" // ...Yes, I know, right?
1/0 Infinity
1/-0 -Infinity
typeof Infinity "number"
typeof -Infinity "number"
0 === -0 true // Wut?
NaN === NaN false // Only value in ES that is not equal to itself
NaN // Not a Number
0 and -0
Infinity and -Infinity
// Statement/Operation Result Note
typeof NaN "number" // ...Yes, I know, right?
1/0 Infinity
1/-0 -Infinity
typeof Infinity "number"
typeof -Infinity "number"
0 === -0 true // Wut?
NaN === NaN false // Only value in ES that is not equal to itself
isNaN(NaN) true // OK
isNaN("foo") true // Wut?
NaN // Not a Number
0 and -0
Infinity and -Infinity
// Statement/Operation Result Note
typeof NaN "number" // ...Yes, I know, right?
1/0 Infinity
1/-0 -Infinity
typeof Infinity "number"
typeof -Infinity "number"
0 === -0 true // Wut?
NaN === NaN false // Only value in ES that is not equal to itself
isNaN(NaN) true // OK
isNaN("foo") true // Wut?
Number.isNaN(NaN) true // Number.isNaN() appeared in ES2015 (ES6)
Number.isNaN("foo") false
NaN // Not a Number
0 and -0
Infinity and -Infinity
// Statement/Operation Result Note
typeof NaN "number" // ...Yes, I know, right?
1/0 Infinity
1/-0 -Infinity
typeof Infinity "number"
typeof -Infinity "number"
0 === -0 true // Wut?
NaN === NaN false // Only value in ES that is not equal to itself
isNaN(NaN) true // OK
isNaN("foo") true // Wut?
Number.isNaN(NaN) true // Number.isNaN() appeared in ES2015 (ES6)
Number.isNaN("foo") false
Object.is(0, -0) false // Object.is() appeared in ES2015 (ES6)
Object.is(NaN, NaN) true
1 + 2 === 3 // ??
1 + 2 === 3 // true
0.1 + 0.2 === 0.3 // ??
1 + 2 === 3 // true
0.1 + 0.2 === 0.3 // false
0.1 + 0.2 // 0.30000000000000004
Math.pow(2, -52) // Epsilon
Math.pow(2, -52) // Epsilon
Number.EPSILON // ES6
Number.EPSILON === Math.pow(2, -52) // true
Number.EPSILON === 2**-52 // true
const isCloseEnough = (a, b) => ( Math.abs(a - b) < Number.EPSILON )
Math.pow(2, -52) // Epsilon
Number.EPSILON // ES6
Number.EPSILON === Math.pow(2, -52) // true
Number.EPSILON === 2**-52 // true
const isCloseEnough = (a, b) => ( Math.abs(a - b) < Number.EPSILON )
isCloseEnough(0.4 + 0.02, 0.42) // true
Math.pow(2, -52) // Epsilon
Number.EPSILON // ES6
Number.EPSILON === Math.pow(2, -52) // true
Number.EPSILON === 2**-52 // true
const isCloseEnough = (a, b) => ( Math.abs(a - b) < Number.EPSILON )
isCloseEnough(0.4 + 0.02, 0.42) // true
isCloseEnough(0.4 + 0.019999999999999, 0.42) // false
Math.pow(2, -52) // Epsilon
Number.EPSILON // ES6
Number.EPSILON === Math.pow(2, -52) // true
Number.EPSILON === 2**-52 // true
const isCloseEnough = (a, b) => ( Math.abs(a - b) < Number.EPSILON )
isCloseEnough(0.4 + 0.02, 0.42) // true
isCloseEnough(0.4 + 0.019999999999999, 0.42) // false
isCloseEnough(0.4 + 0.0199999999999999, 0.42) // true
https://0.30000000000000004.com/
const boxedStr = new String('Hello world!')
boxedStr instanceof String // ??
boxedStr instanceof Object // ??
typeof boxedStr // ??
const boxedNum = new Number(42)
boxedNum instanceof Number // ??
boxedNum instanceof Object // ??
typeof boxedNum // ??
const boxedBoo = new Boolean(true)
boxedBoo instanceof Boolean // ??
boxedBoo instanceof Object // ??
typeof boxedBoo // ??
const boxedStr = new String('Hello world!')
boxedStr instanceof String // true
boxedStr instanceof Object // true
typeof boxedStr // "object"
const boxedNum = new Number(42)
boxedNum instanceof Number // true
boxedNum instanceof Object // true
typeof boxedNum // "object"
const boxedBoo = new Boolean(true)
boxedBoo instanceof Boolean // true
boxedBoo instanceof Object // true
typeof boxedBoo // "object"
42..toString() // ??
42..toString() // "42"
true.valueOf() // true
'Hello world!'.toUpperCase() // "HELLO WORLD!"
42..toString() // "42"
true.valueOf() // true
'Hello world!'.charAt(1) // ??
42..toString() // "42"
true.valueOf() // true
'Hello world!'.toUpperCase() // "HELLO WORLD!"
typeof 'Hello world!'.toUpperCase() // "string"
typeof (new String('Hello world!').toUpperCase()) // ??
42..toString() // "42"
true.valueOf() // true
'Hello world!'.toUpperCase() // "HELLO WORLD!"
typeof 'Hello world!'.toUpperCase() // ??
42..toString() // "42"
true.valueOf() // ??
42..toString() // "42"
true.valueOf() // true
'Hello world!'.toUpperCase() // "HELLO WORLD!"
typeof 'Hello world!'.toUpperCase() // "string"
typeof (new String('Hello world!').toUpperCase()) // "string"
const unboxedStr = String(boxedStr) // "'Hello world!"
const unboxedNum = Number(boxedNum) // 42
const unboxedBoo = Boolean(boxedBoo) // true
typeof unboxedStr // ??
typeof unboxedNum // ??
typeof unboxedBoo // ??
const unboxedStr = String(boxedStr) // "'Hello world!"
const unboxedNum = Number(boxedNum) // 42
const unboxedBoo = Boolean(boxedBoo) // true
typeof unboxedStr // "string"
typeof unboxedNum // "number"
typeof unboxedBoo // "boolean"
String(42)
Number('42')
Boolean(42)
String(42) // "42"
Number('42') // 42
Boolean(42) // true
Boolean('true') // ???
Boolean('false') // ???
Boolean(0) // ???
Boolean('') // ???
Boolean('0') // ???
Boolean({}) // ???
Boolean([]) // ???
Number(true) // ???
Number('') // ???
Number('\n\r\t ') // ???
Number(null) // ???
Number(undefined) // ???
Number({}) // ???
Number([]) // ???
String(4.2e1) // ???
String(0) // ???
String(-0) // ???
String({}) // ???
String([]) // ???
'' + 42 // ???
42 + '' // ???
+'42' // ??
+'4.2e1' // ??
+null // ???
+undefined // ???
40 + 2 + '' // ???
'' + 40 + 2 // ???
'true' == true // ???
'false' == false // ???
!'true' == true // ???
!!'true' == true // ???
[] + 42 // ???
40 + 2 + [] // ???
[] + 40 + 2 // ???
{} + 40 + 2 // ???
40 + 2 + {} // ???
if (42) {
}
if ('') {
}
if ('0') {
}
if ('') {
}
if (42) {
}
if ('') {
}
if ('0') {
}
if ('') {
}
if (Boolean(x) === true) {
}
if (42) {
}
if ('') {
}
if ('0') {
}
if ('') {
}
if (Boolean(x) === true) { // Please... don't do this
}
[] + {} // ???
[] == ![] // ???
{} + [] // ???
({}) + [] // ???
[] + [] // ???
[] + +[] // ???
[] ++[] // ???
{} + {} // ???
{foo: 'bar'} + [] // ???
({foo: 'bar'}) + [] // ???
1 - - + + + - + -41 // ???
1 - - + + + - + -'41' // ???
1 + '41' // ???
43 - '1' // ???
!!~'areyouthere'.indexOf('no')
Abstract operation ToPrimitive takes an input argument and an optional argument PreferredType.
PreferredType can either be "string", or "number", and it defaults to "number"
if @@toPrimitive is present, use it, if it returns an object, throw TypeError
else if PreferredType is "string", returns .toString() or .valueOf()*
else if PreferredType is "number", returns .valueOf() or .toString()*
* The first method that returns a non-object value
if @@toPrimitive is present, use it, if it returns an object, throw TypeError
const foo = {
[Symbol.toPrimitive]: () => 'foo object',
}
// Will be OK to convert to primitive
const bar = {
[Symbol.toPrimitive]: () => ({foo: 'bar'})
}
// Will throw TypeError when trying to convert to primitive
const foo = {
[Symbol.toPrimitive]: () => 'foo object',
}
// Will be OK to convert to primitive
const bar = {
[Symbol.toPrimitive]: () => ({foo: 'bar'})
}
// Will throw TypeError when trying to convert to primitive
String(foo) // 'foo object'
String(bar) // TypeError: can't convert ({[Symbol.toPrimitive]() { return {foo: 'bar'}}})
// to number: its [Symbol.toPrimitive] method returned an object
const foo = {
valueOf: () => 42,
toString: () => '42 in string',
}
String(foo) // '42 in string'
Number(foo) // 42
foo + 1 // ??
foo - 1 // ??
foo + '1' // ??
const foo = {
valueOf: () => 42,
toString: () => '42 in string',
}
String(foo) // '42 in string'
Number(foo) // 42
foo + 1 // ??
foo - 1 // ??
foo + '1' // ???
else if PreferredType is "string", returns .toString() or .valueOf()*
else if PreferredType is "number", returns .valueOf() or .toString()*
PreferredType can either be "string", or "number", and it defaults to "number"
const foo = {
valueOf: () => 42,
toString: () => '42 in string',
}
String(foo) // '42 in string'
Number(foo) // 42
foo + 1 // 43
foo - 1 // ??
foo + '1' // ??
const foo = {
valueOf: () => 42,
toString: () => '42 in string',
}
String(foo) // '42 in string'
Number(foo) // 42
foo + 1 // 43
foo - 1 // 41
foo + '1' // ??
const foo = {
valueOf: () => 42,
toString: () => '42 in string',
}
String(foo) // '42 in string'
Number(foo) // 42
foo + 1 // 43
foo - 1 // 41
foo + '1' // 421
// Type Result
Undefined false
Null false
Boolean argument
Number 0, NaN are false, otherwise true
String Empty string "" is false, otherwise true
Symbol true
Object true
// Falsy values
null
undefined
NaN
""
false
0
-0
// Falsy values // Truthy values
null Everything else
undefined
NaN
""
false
0
-0
// Type Result
Undefined false
Null false
Boolean argument
Number -0, 0, NaN are false, otherwise true
String Empty string "" is false, otherwise true
Symbol true
Object true
Boolean('true') // true
Boolean('false') // true
Boolean(0) // false
Boolean(NaN) // false
Boolean('') // false
Boolean('0') // true
Boolean({}) // true
Boolean(Symbol('')) // true
Boolean(new Boolean(false)) // true
!!'true' // true
!!'false' // true
!!0 // false
!!NaN // false
!!'' // false
!!'0' // true
!!{} // true
!!Symbol('') // true
!!new Boolean(false) // true
// Type Result
Undefined NaN
Null 0
Boolean true is 1, false is 0
Number No coercion
String ...Complicated, see next slide
Symbol TypeError
Object ToNumber on the result of ToPrimitive
const bar = {
valueOf: () => true,
toString: () => '42'
}
Number(bar) // ??
String(bar) // ??
const bar = {
valueOf: () => true,
toString: () => '42'
}
Number(bar) // 1
String(bar) // "42"
bar + 1 // ??
bar + '1' // ??
// Type Result
Undefined NaN
Null 0
Boolean true is 1, false is 0
Number No coercion
String The number if string can be parsed as litteral number, NaN otherwise
Symbol TypeError
Object ToNumber on the result of ToPrimitive
Number('') // 0
Number(' ') // 0
Number(' \
') // 0
Number('1') // 1
Number('1a') // NaN
Number('1e') // NaN
Number('1e1') // 10
Number('Infinity') // Infinity
Number('-0') // -0
+'' // 0
+' ' // 0
+' \
' // 0
+'1' // 1
+'1a' // NaN
+'1e' // NaN
+'1e1' // 10
+'Infinity' // Infinity
+'-0' // -0
// let x be a value of any type
// Operation Result
x | 0 integer on 32 bits, so you cannot rely on
coercion with anything above 2**31,
and since NaN and Infinity are not "32-bit safe",
the result for them will be 0
// Operation Result
x = Infinity
x | 0 0
x = 0
1/x | 0 0
x = NaN
x | 0 0
x = 'foo'
x | 0 0
x = '42'
x | 0 ?
x = {}
x | 0 ?
ToInt32(argument) converts argument to one of 2^32 integer values in the range [-2^31, (2^31)-1]
// Operation Result
true | 0 1
false | 0 0
2**30 | 0 1073741824
2**31 | 0 -2147483648
2**32 | 0 0
4**31 | 0 ?
5**31 | 0 -2128609280
6**31 | 0 -2147483648
8**31 | 0 ?
// let x be a value of any type
// Operation Result
x | 0 integer on 32 bits, so you cannot rely on
coercion with anything above 2**31,
and since NaN and Infinity are not "32-bit safe",
the result for them will be 0
// Operation Result
x = Infinity
x | 0 0
x = 0
1/x | 0 0
x = NaN
x | 0 0
x = 'foo'
x | 0 0
x = '42'
x | 0 42
x = {}
x | 0 0
// Type Result
Undefined "undefined"
Null "null"
Boolean true is "true", false is "false"
Number NumberToString(argument)
String argument
Symbol TypeError
Object [[ToString]] on the result of [[ToPrimitive]]
(on the result of [[DefaultValue]])
// Value Result
NaN "NaN"
Infinity "Infinity"
-Infinity "-Infinity"
0 ou -0 "0"
2e20 "200000000000000000000"
2e21 "2e21"
2e-6 "0.000002"
2e-7 "2e-7"
if (<coercion here>) {
}
x == y // Coercion on one side following a set of rules
x + y // Coercion on at least one operand following a set of rules
// Other arithmetic operators
x - y
x / y
x * y
Si les 2 opérandes sont de type Number :
- Si au moins l'un des deux opérandes est NaN, false
- Si les 2 opérandes ont la même valeur, true,
- Si les 2 opérandes ont pour valeurs 0 ou -0, true
- Sinon, false
Sinon, retourner le résultat de SameValueNonNumber(x, y)
SameValueNonNumber(x, y)
Null == Undefined ou Undefined == Null
true
if (variable === null || variable === undefined) { // Wrong
(...)
}
if (variable == null) { // Right
(...)
}
if (variable === null || variable === undefined) { // Verbose
(...)
}
if (variable == null) { // Expressive
(...)
}
if (typeof variable === 'function') { // Useless
(...)
}
if (typeof variable == 'function') { // Better (I think)
}
if (variable === null || variable === undefined) { // Verbose
(...)
}
if (variable == null) { // Expressive
(...)
}
if (typeof variable === 'function') { // Useless
(...)
}
if (typeof variable == 'function') { // Better (I think)
}
// .eslintrc.js
{
rules: {
eqeqeq: ["error", "smart"],
}
}
[] + {} // ???
[] == ![] // ???
{} + [] // ???
({}) + [] // ???
[] + [] // ???
[] + +[] // ???
[] ++[] // ???
{} + {} // ???
{foo: 'bar'} + [] // ???
({foo: 'bar'}) + [] // ???
1 - - + + + - + -41 // ???
1 - - + + + - + -'41' // ???
1 + '41' // ???
43 - '1' // ???
!!~'areyouthere'.indexOf('no')
(of JavaScript)
[] + {} // ???
[] == ![] // ???
{} + [] // ???
({}) + [] // ???
[] + [] // ???
[] + +[] // ???
[] ++[] // ???
{} + {} // ???
{foo: 'bar'} + [] // ???
({foo: 'bar'}) + [] // ???
1 - - + + + - + -41 // ???
1 - - + + + - + -'41' // ???
1 + '41' // ???
43 - '1' // ???
!!~'areyouthere'.indexOf('no')
?
[] + {}
GetValue([]) + GetValue({})
[] + {}
ToPrimitive([]) + ToPrimitive({})
'' + '[object Object]'
// Résultat final
'[object Object]'
?
[] == ![]
[] == !(ToBoolean(ToPrimitive([]))
[] == !(ToBoolean(''))
[] == !true
[] == false
// Les types sont différents, donc conversion de type
ToPrimitive(GetValue([])) == ToNumber(false)
ToPrimitive([]) == 0
'' == 0
ToNumber('') == 0
0 == 0
// Résultat final
true
?
{} + []
+ [] // {} en début de ligne est un bloc de code... vide
ToNumber(ToPrimitive(GetValue([])))
ToNumber('')
// Résultat final
0