2023-06-01 16:53:25 +03:00
|
|
|
/**********************************************************************
|
|
|
|
|
*
|
|
|
|
|
* JavaScript types and objects
|
|
|
|
|
*
|
|
|
|
|
*
|
|
|
|
|
**********************************************************************/
|
2023-06-02 15:27:00 +03:00
|
|
|
//
|
2023-06-03 23:04:56 +03:00
|
|
|
// Types and objects
|
|
|
|
|
// =================
|
2023-06-02 15:27:00 +03:00
|
|
|
//
|
|
|
|
|
// JavaScript's type system is split into two categories of enteties:
|
|
|
|
|
// basic types or values and objects, they differ in several aspects.
|
|
|
|
|
//
|
|
|
|
|
// | values | objects
|
|
|
|
|
// ----------------+-------------------+--------------------------
|
|
|
|
|
// mutability | imutable | mutable
|
|
|
|
|
// ----------------+-------------------+--------------------------
|
|
|
|
|
// identity | equal values are | different objects
|
|
|
|
|
// | the same entity | can have same
|
2023-06-14 14:04:00 +03:00
|
|
|
// | (singletons) | structure / "value"
|
2023-06-02 15:27:00 +03:00
|
|
|
// ----------------+-------------------+--------------------------
|
|
|
|
|
//
|
|
|
|
|
//
|
2023-06-14 14:04:00 +03:00
|
|
|
// Value vs. Identity
|
|
|
|
|
// ------------------
|
|
|
|
|
//
|
|
|
|
|
// Imagine an apple, it's a "thing" that is an "apple", or we say that
|
|
|
|
|
// it has a value "apple". There are lots of apples in the world,
|
2023-06-19 19:54:54 +03:00
|
|
|
// each one is slightly different but all are apples. Now imagine two
|
|
|
|
|
// people, each looking at an apple, we can say that each person sees
|
|
|
|
|
// the value "apple", those values are equal, and if those people are
|
|
|
|
|
// sitting at the same table and looking at the same apple, we say that
|
|
|
|
|
// their apples are the same apple, or in JavaScript-ish, they are of
|
|
|
|
|
// the same identity.
|
2023-06-14 14:04:00 +03:00
|
|
|
// Then if we can take a different set of people looking at apples, but
|
2023-06-15 21:37:25 +03:00
|
|
|
// now each one has their own personal apple, the values are still the
|
2023-06-19 19:54:54 +03:00
|
|
|
// same, both apples are still looking at apples but now their apples
|
|
|
|
|
// are different, aren't they? And thus we say they are of different
|
|
|
|
|
// identities.
|
2023-06-15 21:37:25 +03:00
|
|
|
// We'll come back to this concept a bit later, once we introduce
|
|
|
|
|
// JavaScript values and types.
|
2023-06-14 14:04:00 +03:00
|
|
|
//
|
|
|
|
|
//
|
2023-06-01 16:53:25 +03:00
|
|
|
// Basic values
|
2023-06-03 23:04:56 +03:00
|
|
|
// ------------
|
2023-06-01 16:53:25 +03:00
|
|
|
//
|
2023-06-14 14:04:00 +03:00
|
|
|
// Numbers
|
2023-06-01 16:53:25 +03:00
|
|
|
var integer = 123
|
|
|
|
|
var floating_point = 3.1415
|
2023-06-14 14:04:00 +03:00
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Note that all numbers are of the same "type", this is different to
|
2023-06-19 19:54:54 +03:00
|
|
|
// allot of other languages where numbers are implemented closer to the
|
2023-06-14 14:04:00 +03:00
|
|
|
// low-level hardware implementation and thus are represented by a
|
|
|
|
|
// whole range of number types.
|
|
|
|
|
//
|
|
|
|
|
// Numbers can also be written using different base notations:
|
|
|
|
|
var bin = 0b101010
|
|
|
|
|
var oct = 052
|
2023-06-01 16:53:25 +03:00
|
|
|
var hex = 0xFF
|
2023-06-14 14:04:00 +03:00
|
|
|
var dec = 42
|
2023-06-19 19:54:54 +03:00
|
|
|
var exp = .42e2
|
2023-06-14 14:04:00 +03:00
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// But note that these are just different notations and all of the
|
|
|
|
|
// above resolve to the same number.
|
|
|
|
|
//
|
2023-06-01 16:53:25 +03:00
|
|
|
|
2023-06-19 19:54:54 +03:00
|
|
|
// Numbers also have several limitations:
|
|
|
|
|
//
|
|
|
|
|
// - precision, rounding errors and fractions (IEEE-754)
|
|
|
|
|
0.1 + 0.2 == 0.3 // -> false
|
|
|
|
|
|
2023-06-20 17:43:02 +03:00
|
|
|
// XXX
|
|
|
|
|
|
2023-06-19 19:54:54 +03:00
|
|
|
// - large number rounding
|
2023-06-20 17:43:02 +03:00
|
|
|
Number.MAX_SAFE_INTEGER + 10 - 10 == Number.MAX_SAFE_INTEGER
|
|
|
|
|
|
|
|
|
|
// In general numbers larger than Number.MAX_SAFE_INTEGER and
|
|
|
|
|
// smaller than Number.MIN_SAFE_INTEGER should not be used for
|
|
|
|
|
// math operations (see BigInt).
|
|
|
|
|
//
|
|
|
|
|
// Note that neither issue is specific to JavaScript but rather are
|
|
|
|
|
// side-effects of number implementations in modern computers and
|
|
|
|
|
// the trade-offs of these implementation on each level from the
|
|
|
|
|
// CPU to the high-level languages.
|
|
|
|
|
//
|
|
|
|
|
// For more details see:
|
2023-06-22 23:25:44 +03:00
|
|
|
// https://en.wikipedia.org/wiki/IEEE_754-2008_revision
|
2023-06-23 22:38:42 +03:00
|
|
|
// XXX
|
2023-06-19 19:54:54 +03:00
|
|
|
|
2023-06-14 14:04:00 +03:00
|
|
|
|
|
|
|
|
// Strings
|
2023-06-01 16:53:25 +03:00
|
|
|
var string = 'string'
|
|
|
|
|
var another_string = "also a string"
|
|
|
|
|
var template = `
|
|
|
|
|
a template string.
|
|
|
|
|
this can include \\n's
|
|
|
|
|
also summorts expansions ${ '.' }`
|
|
|
|
|
|
2023-06-14 14:04:00 +03:00
|
|
|
// XXX a note on template strings
|
|
|
|
|
|
2023-06-19 19:54:54 +03:00
|
|
|
|
|
|
|
|
// Booleans
|
2023-06-01 16:53:25 +03:00
|
|
|
var t = true
|
|
|
|
|
var f = false
|
|
|
|
|
|
2023-06-19 19:54:54 +03:00
|
|
|
|
2023-06-14 14:04:00 +03:00
|
|
|
// Nulls
|
2023-06-01 16:53:25 +03:00
|
|
|
var n = null
|
|
|
|
|
var u = undefined
|
|
|
|
|
var not_a_number = NaN
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Values are in general:
|
|
|
|
|
//
|
|
|
|
|
// - singletons
|
|
|
|
|
var a = 3.14
|
|
|
|
|
var b = 3.14
|
|
|
|
|
a === b // -> true
|
|
|
|
|
|
|
|
|
|
// In general equal basic values are the same value and there is
|
|
|
|
|
// no way to create two copies of the same value.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// - imutable
|
|
|
|
|
var a = 1
|
|
|
|
|
var b = a
|
|
|
|
|
|
|
|
|
|
// a and b hold the same value (1)
|
|
|
|
|
a === b // -> true
|
|
|
|
|
|
|
|
|
|
// now we update a...
|
|
|
|
|
a += 1
|
|
|
|
|
a === b // -> false
|
|
|
|
|
// Note that we updated the value referenced by a, i.e. the old
|
|
|
|
|
// value (1) was not modified by the addition (b is still 1),
|
|
|
|
|
// rather a new value (2) was created and assigned to a.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-06-02 17:00:33 +03:00
|
|
|
// Equality and identity
|
|
|
|
|
//
|
2023-06-14 14:04:00 +03:00
|
|
|
// XXX
|
2023-06-02 17:00:33 +03:00
|
|
|
|
|
|
|
|
|
2023-06-04 23:16:10 +03:00
|
|
|
// Automatic type coercion
|
|
|
|
|
//
|
2023-06-14 14:04:00 +03:00
|
|
|
// XXX
|
2023-06-04 23:16:10 +03:00
|
|
|
|
2023-06-02 17:00:33 +03:00
|
|
|
|
2023-06-05 23:24:06 +03:00
|
|
|
// Type checking
|
2023-06-03 23:04:56 +03:00
|
|
|
//
|
2023-06-14 14:04:00 +03:00
|
|
|
// XXX
|
2023-06-10 14:59:13 +03:00
|
|
|
|
2023-06-06 19:18:04 +03:00
|
|
|
typeof(42) // -> 'number'
|
2023-06-07 23:58:52 +03:00
|
|
|
typeof('meaning of life') // -> 'string'
|
2023-06-03 23:04:56 +03:00
|
|
|
|
|
|
|
|
|
2023-06-17 22:29:50 +03:00
|
|
|
// Note that this has a small "inconsistency" that can be used to check
|
|
|
|
|
// if a variable is defined.
|
|
|
|
|
|
|
|
|
|
typeof(unknown_variable) // -> 'undefined'
|
|
|
|
|
|
|
|
|
|
|
2023-06-03 23:04:56 +03:00
|
|
|
|
2023-06-01 16:53:25 +03:00
|
|
|
// Objects
|
2023-06-03 23:04:56 +03:00
|
|
|
// -------
|
2023-06-01 16:53:25 +03:00
|
|
|
//
|
2023-06-18 23:42:12 +03:00
|
|
|
// One very useful distinction/simplification from a lot of other
|
|
|
|
|
// languages is that JavaScript for most of its history did not have a
|
|
|
|
|
// dict/map/hashmap type, it was unified with the generic object type.
|
|
|
|
|
//
|
|
|
|
|
// Note that a Map type was added in ES6 but that is mostly a duplication
|
|
|
|
|
// of base object functionality with a set of added methods with one
|
|
|
|
|
// important difference -- Map keys can be any arbitrary object while
|
|
|
|
|
// object keys can only be strings.
|
|
|
|
|
//
|
2023-06-03 23:04:56 +03:00
|
|
|
|
|
|
|
|
// Type cheking
|
2023-06-01 16:53:25 +03:00
|
|
|
//
|
2023-06-06 19:18:04 +03:00
|
|
|
// Here thesame approach as for simple types is not productive:
|
|
|
|
|
|
|
|
|
|
typeof([42]) // -> 'object'
|
|
|
|
|
typeof({}) // -> 'object'
|
|
|
|
|
|
|
|
|
|
// so a better approach would be to:
|
|
|
|
|
|
|
|
|
|
[42] instanceof Array // -> true
|
|
|
|
|
|
|
|
|
|
// but since all objects are objects the test can get quite generic (XXX)
|
|
|
|
|
|
|
|
|
|
[42] instanceof Object // -> true
|
|
|
|
|
{} instanceof Object // -> true
|
|
|
|
|
|
2023-06-14 14:04:00 +03:00
|
|
|
//
|
2023-06-08 22:17:35 +03:00
|
|
|
// this essentially checks if the left oprtand is related to (i.e. in the
|
2023-06-09 16:50:19 +03:00
|
|
|
// inheritance chain of) the second operand's .prototype, or we can say
|
2023-06-08 22:17:35 +03:00
|
|
|
// that it id "inherited" from the constructor.
|
2023-06-14 14:04:00 +03:00
|
|
|
//
|
2023-06-06 19:18:04 +03:00
|
|
|
|
2023-06-01 16:53:25 +03:00
|
|
|
|
2023-06-03 23:04:56 +03:00
|
|
|
|
2023-06-01 16:53:25 +03:00
|
|
|
// Prototypes and inheritance
|
|
|
|
|
//
|
2023-06-14 00:50:35 +03:00
|
|
|
// XXX
|
2023-06-08 22:17:35 +03:00
|
|
|
|
2023-06-09 16:50:19 +03:00
|
|
|
var a = {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var b = Object.create(a)
|
|
|
|
|
|
|
|
|
|
var c = {
|
|
|
|
|
__proto__: b,
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-01 16:53:25 +03:00
|
|
|
|
|
|
|
|
// Constructors
|
2023-06-13 23:41:18 +03:00
|
|
|
//
|
|
|
|
|
// A constructor is simply a function that "constructs" or populates an
|
|
|
|
|
// object.
|
|
|
|
|
//
|
|
|
|
|
// By convention constructor functions are capitalized (Pascal-case)
|
|
|
|
|
//
|
|
|
|
|
// Classic constructors are called with a "new" keyword which creates a
|
|
|
|
|
// bare instance and passes it to the function as the call context.
|
2023-06-01 16:53:25 +03:00
|
|
|
//
|
2023-06-08 22:17:35 +03:00
|
|
|
|
2023-06-09 16:50:19 +03:00
|
|
|
function A(){
|
|
|
|
|
this.attr = 42
|
|
|
|
|
this.method = function(){
|
|
|
|
|
console.log('Hello world!')
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var x = new A()
|
|
|
|
|
|
|
|
|
|
var y = {
|
|
|
|
|
__proto__: A.prototype,
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-14 00:50:35 +03:00
|
|
|
//
|
|
|
|
|
// The problem with the default way this is done is that now a
|
|
|
|
|
// constructor will behave differently when called directly or if called
|
|
|
|
|
// via the new syntax. This can be desirable in some cases but in
|
|
|
|
|
// general this is a pitfall, so let's unify the two cases:
|
|
|
|
|
//
|
2023-06-09 16:50:19 +03:00
|
|
|
|
|
|
|
|
function B(){
|
|
|
|
|
var obj = {
|
|
|
|
|
__proto__: B.prototype,
|
|
|
|
|
}
|
|
|
|
|
return obj
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-14 00:50:35 +03:00
|
|
|
// this can be called with and without new:
|
2023-06-09 16:50:19 +03:00
|
|
|
var z = B()
|
|
|
|
|
|
2023-06-14 00:50:35 +03:00
|
|
|
// less naive -- reuses the instance created by new...
|
2023-06-09 16:50:19 +03:00
|
|
|
function C(){
|
|
|
|
|
var obj = this instanceof C ?
|
|
|
|
|
this
|
|
|
|
|
: { __proto__: C.prototype }
|
|
|
|
|
return obj
|
|
|
|
|
}
|
2023-06-10 14:59:13 +03:00
|
|
|
// make C instances related to B...
|
|
|
|
|
C.prototype.__proto__ = B.prototype
|
|
|
|
|
|
2023-06-14 00:50:35 +03:00
|
|
|
//
|
|
|
|
|
// Note that constructor extension is trivial if you think of how
|
|
|
|
|
// prototypical inheritance works, to link A and B "instances" all we
|
|
|
|
|
// needed to do is link the constructor prototypes in the code above.
|
|
|
|
|
//
|
2023-06-10 14:59:13 +03:00
|
|
|
|
|
|
|
|
|
|
|
|
|
// Extending builtin types
|
|
|
|
|
//
|
2023-06-14 14:04:00 +03:00
|
|
|
// XXX
|
2023-06-09 16:50:19 +03:00
|
|
|
|
2023-06-12 10:58:05 +03:00
|
|
|
// Mixing builtin types
|
|
|
|
|
//
|
|
|
|
|
// In general this is impossible in JavaScript due to the lack of any
|
|
|
|
|
// mechanism of horizontal name resolution in the inheritance chain like
|
|
|
|
|
// multiple inheritance (hence why we call it a chain and not a tree).
|
|
|
|
|
//
|
|
|
|
|
// So there is no way, for example, to make something both an array and
|
|
|
|
|
// a function at the same time.
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-06-10 14:59:13 +03:00
|
|
|
// XXX Reflect.construct(Function, args, newConstructor)
|
|
|
|
|
// mainly usefull if the resulting instance has to be of a builtin
|
|
|
|
|
// type like a function (callable) or an array...
|
2023-06-11 23:27:52 +03:00
|
|
|
// ...especially when overloading the constructor
|
|
|
|
|
// XXX should this be in advanced topics???
|
2023-06-10 14:59:13 +03:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Classes and JavaScript
|
|
|
|
|
//
|
|
|
|
|
// Since the class syntax is simply a more restrictive way to do the
|
|
|
|
|
// same as the above, in addition to introducing more "the same but
|
|
|
|
|
// slightly different" ways to define functions and methods thus adding
|
|
|
|
|
// lots of new details, pitfalls and nuances that give us essentially
|
|
|
|
|
// the same functionaly that already existed in the language with
|
|
|
|
|
// the onus of additional complexity, we will be completely ignoring
|
|
|
|
|
// them in this document.
|
2023-06-09 16:50:19 +03:00
|
|
|
|
2023-06-01 16:53:25 +03:00
|
|
|
|
|
|
|
|
|
|
|
|
|
/**********************************************************************
|
|
|
|
|
* vim:set ts=4 sw=4 : */
|