|
| 1 | +# About |
| 2 | + |
| 3 | +Knowning what the type of a piece of data is, is often very important for code to run smoothly and without errors. |
| 4 | + |
| 5 | +Javascript has several ways to check the type of a value or object. |
| 6 | + |
| 7 | +```exercism/note |
| 8 | +Javascript's type checking mechanisms can be somewhat unreliable. |
| 9 | +
|
| 10 | +For better type safety and stronger types, you should probably use TypeScript, a language that builds on JavaScript, but with the type syntax of a static-typed language. |
| 11 | +``` |
| 12 | + |
| 13 | +## The `typeof` operator |
| 14 | + |
| 15 | +The `typeof` operator returns the type of its operand. |
| 16 | +The output is a string matching the name of one of the [primitive data types][primitives], except for `"null"`. |
| 17 | +It can also be `"function"` or `"object"`. |
| 18 | + |
| 19 | +```javascript |
| 20 | +typeof undefined; |
| 21 | +// => "undefined" |
| 22 | + |
| 23 | +typeof true; |
| 24 | +// => "boolean" |
| 25 | + |
| 26 | +typeof 42; |
| 27 | +// => "number" |
| 28 | + |
| 29 | +typeof 'Hello, World!'; |
| 30 | +// => "string" |
| 31 | + |
| 32 | +typeof function () { |
| 33 | + return 'Hello, World'; |
| 34 | +}; |
| 35 | +// => "function" |
| 36 | + |
| 37 | +typeof [1, 2, 3, 4]; |
| 38 | +// => "object" |
| 39 | + |
| 40 | +typeof { city: 'Stockholm', country: 'Sweden' }; |
| 41 | +// => "object" |
| 42 | +``` |
| 43 | + |
| 44 | +For [historical reasons][`typeof null` is `"object"`]. |
| 45 | + |
| 46 | +## The `instanceof` operator |
| 47 | + |
| 48 | +For checking the type of an object, you can use the `instanceof` operator. |
| 49 | +It evaluates into a `boolean` depending on whether the second operand is included in the first operands' [prototype chain][prototype chain]. |
| 50 | +To clarify, `instanceof` will return whether the first operand is an instance of second operand or one of its child classes. |
| 51 | +`instanceof` only works on objects. |
| 52 | + |
| 53 | +```javascript |
| 54 | +class Beverage { |
| 55 | + // ... |
| 56 | +} |
| 57 | + |
| 58 | +// The Coffee class is a child of the Beverage class. |
| 59 | +class Coffee extends Beverage { |
| 60 | + // ... |
| 61 | +} |
| 62 | + |
| 63 | +const java = new Coffee(); |
| 64 | + |
| 65 | +java instanceof Coffee; |
| 66 | +// => true |
| 67 | + |
| 68 | +java instanceof Beverage; |
| 69 | +// => true |
| 70 | +``` |
| 71 | + |
| 72 | +````exercism/advanced |
| 73 | +The `Array` class has a method called `Array.isArray()` that checks if its argument is an array. |
| 74 | +
|
| 75 | +While `instanceof Array` will not work with an array created in a different realm such as an `iframe` in a webpage, `Array.isArray()` will. |
| 76 | +
|
| 77 | +This is because the Array class has a different constructor in each realm, and each `iframe` has its own ream, meaning that the function in the prototype chain will be different, causing `instanceof Array` to fail. |
| 78 | +`Array.isArray()` is capable of ignoring this, and should always be used when possible. |
| 79 | +
|
| 80 | +It can also survive false positives where an object isn't actually an `Array`, and merely has `Array` in its prototype chain. |
| 81 | +
|
| 82 | +```javascript |
| 83 | +({ __proto__: Array.prototype }) instanceof Array |
| 84 | +// => true |
| 85 | +
|
| 86 | +Array.isArray({ __proto__: Array.prototype }) |
| 87 | +// => false |
| 88 | +``` |
| 89 | +
|
| 90 | +```` |
| 91 | + |
| 92 | +## The `in` operator |
| 93 | + |
| 94 | +The `in` operator returns whether the first operand is a property of the second operand. |
| 95 | +It does not check that the property has a defined value. |
| 96 | +A property set to `undefined` will still be detected by `in`. |
| 97 | + |
| 98 | +```javascript |
| 99 | +class Coffee { |
| 100 | + constructor() { |
| 101 | + this.temperature = 'hot'; |
| 102 | + this.isDarkMatter = undefined; |
| 103 | + } |
| 104 | + |
| 105 | + coolDown() { |
| 106 | + this.temperature = 'warm'; |
| 107 | + } |
| 108 | +} |
| 109 | + |
| 110 | +const espresso = new Coffee(); |
| 111 | + |
| 112 | +'temperature' in espresso; |
| 113 | +// => true |
| 114 | + |
| 115 | +'color' in espresso; |
| 116 | +// => false |
| 117 | + |
| 118 | +'isDarkMatter' in espresso; |
| 119 | +// => true |
| 120 | +``` |
| 121 | + |
| 122 | +````exercism/note |
| 123 | +`in` will return `true` for inherited properties and methods. |
| 124 | +
|
| 125 | +```javascript |
| 126 | +"coolDown" in espresso |
| 127 | +// => true |
| 128 | +
|
| 129 | +"constructor" in espresso |
| 130 | +// => true |
| 131 | +``` |
| 132 | +
|
| 133 | +To avoid this, use `Object.hasOwn()` instead |
| 134 | +```` |
| 135 | + |
| 136 | +## The `Object.hasOwn()` function |
| 137 | + |
| 138 | +The `Object.hasOwn()` method returns whether the specified object _owns the given property_ (it is not inherited or a method). |
| 139 | + |
| 140 | +```javascript |
| 141 | +class Coffee { |
| 142 | + constructor() { |
| 143 | + this.temperature = 'hot'; |
| 144 | + } |
| 145 | + |
| 146 | + coolDown() { |
| 147 | + this.temperature = 'warm'; |
| 148 | + } |
| 149 | +} |
| 150 | +const cappuccino = new Coffee(); |
| 151 | + |
| 152 | +Object.hasOwn(cappucino, 'temperature'); |
| 153 | +// => true |
| 154 | + |
| 155 | +Object.hasOwn(cappucino, 'constructor'); |
| 156 | +// => false |
| 157 | + |
| 158 | +Object.hasOwn(cappucino, 'coolDown'); |
| 159 | +// => false |
| 160 | +``` |
| 161 | + |
| 162 | +[primitives]: https://developer.mozilla.org/en-US/docs/Glossary/Primitive |
| 163 | +[typeof null is object]: https://2ality.com/2013/10/typeof-null.html |
| 164 | +[prototype chain]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Inheritance_and_the_prototype_chain |
0 commit comments