Instructor: Stefan Mitsch
{}
class
var empty = {}; /* Object literal with no contents */ var person = { /* Object literal with three properties */ name: "Alice", age: 50, addr: "243 S Wabash Ave"};
Access properties
> [ person.name, person.name.length ][ 'Alice', 5 ]
Update properties
> person.age = person.age + 151
Access undefined property
undefined
> person.occupationundefined /* huh???? */
Access property of undefined
> person.occupation.lengthUncaught TypeError: Cannot read property 'length' of undefined
var person = { name: "Alice", age: 50, addr: "243 S Wabash Ave" };
Add a property occupation
occupation
person.occupation = "Developer";
Add a property happy place
happy place
person["happy place"] = "Home";
List all properties
> for (p in person) { console.log (p + ": " + person[p]); }name: Aliceage: 50addr: 243 S Wabash Aveoccupation: Developerhappy place: Home
Turn object into a JSON string
> JSON.stringify(person)'{"name":"Alice","age":50,"addr":"243 S Wabash Ave", "occupation":"Developer","happy place":"Home"}'
// objects with number-property `n` and function-property `get`var counter1 = { n: 0, get: function () { return this.n++; } };var counter2 = { n: 0, get () { return this.n++; } };
> [counter1.get(), counter2.get()][0, 0]
Access the functions
> [counter1.get, counter2.get][ [Function: get], [Function: get] ]
Serialize functions to string
> [counter1.get.toString(), counter2.get.toString()][ 'function () { return this.n++; }', 'get () { return this.n++; }' ]
this is mandatory
this
var counter1 = { n: 0, get: function () { return this.n++; } };var counter3 = { n: 0, get: function () { return n++; } };
Increment counter1
counter1
> counter1.get()0
Increment counter3
counter3
> counter3.get()Uncaught ReferenceError: n is not defined at Object.get (repl:1:43)
var counter1 = { n: 0, get: function () { return this.n++; } };
> counter1.n = 30> counter1.get()30
function createCounter () { var n = 0; return { get: function () { return n++; } };};var [c4a,c4b] = [createCounter(), createCounter()]
createCounter
get
c4a
c4b
n
Increment c4a
> c4a.get()0
Increment both c4a and c4b
> [c4a,c4b].map (x => x.get())[ 1, 0 ]
function createCounter () { var n = 0; return { get: function () { console.log ("this.n=" + this.n); return this.n++; } };};var c5 = createCounter ();
c5
> c5.get ();this.n=undefinedNaN
o.m()
this===o
f()
this===global/window
new C()
Access this from method context
var o = { getThis () { return this; } };o.getThis() === o; // true
Access this from function context
var fThis = o.getThis; // save method as functionfThis() === o // false --- this=o not closed abovefThis() === global // true (in Node.js)fThis() === window // true (in Browser)
JavaScript
var oJS = { n: -1, next () { this.n += 1; return this.n; }};var fNext = oJS.next fNext() // NaN --- closure does not bind this=oJS
Scala
object oScala { var n = -1; def next() = { this.n += 1; this.n } }var fNext = oScala.next _fNext() // 0 --- closure binds this=oScala
var o = { v : 5, add : function (xs) { return xs.map(function(x){ return this.v + x; }); }}o.add([10,20,30]) // [ NaN, NaN, NaN ]
var o = { v : 5, add : function (xs) { var me = this; return xs.map(function(x){ return me.v + x; }); }}o.add([10,20,30]) // [ 15, 25, 35 ]
var o = { v : 5, add : function (xs) { return xs.map(function(x){ return this.v + x; }.bind(this)); }}o.add([10,20,30]) // [ 15, 25, 35 ]
var o = { v : 5, add : function (xs) { return xs.map(x => this.v + x); }}o.add([10,20,30]) // [ 15, 25, 35 ]
var y = {getThese: function(xs){ return xs.map(x => this)}};var z = {getThese: function(xs){ return xs.map(function(x){ return this; })}};
y.getThese([10,20,30])[0] === y // truez.getThese([10,20,30])[0] === global // true (in Node.js)z.getThese([10,20,30])[0] === window // true (in Browswer)
function createCounter () { return { n: -1, next: function () { return ++this.n; }, reset: function () { this.n = -1; } };}var c1 = createCounter (); // Function contextvar c2 = createCounter ();
> [c1.next(), c1.next(), c1.next(), c1.reset(), c1.next(), c1.next()][ 0, 1, 2, undefined, 0, 1 ]> [c2.next(), c2.next(), c2.next(), c2.reset(), c2.next(), c2.next()][ 0, 1, 2, undefined, 0, 1 ]
new
function Counter () { this.n = -1; this.next = function () { return ++this.n; }, this.reset = function () { this.n = -1; }}
var c1 = new Counter (); // Constructor contextvar c2 = new Counter ();
var cx = Counter (); // Function context! `this` points to global/window// cx is undefined (no return statement in Counter)
> c1.next === c2.next // false?!?
prototype
function Counter () { this.n = -1; }Counter.prototype.next = function () { return ++this.n; }Counter.prototype.reset = function () { this.n = -1; }var c1 = new Counter ();var c2 = new Counter ();
> [c1.next(), c1.next(), c1.reset()][ 0, 1, 2, undefined]> [c2.next(), c2.next(), c2.reset(), c2.next(), c2.next()][ 0, 1, undefined, 0, 1 ]
c1
c2
Counter.prototype
c1.next === c2.next // true
function Counter () { this.n = -1; }Counter.prototype.next = function () { return this.n += 1; }Counter.prototype.reset = function () { this.n = -1; }var c1 = new Counter ();
> [c1.next(), c1.next(), c1.next(), c1.reset(), c1.next(), c1.next()][ 0, 1, 2, undefined, 0, 1 ]
Change the behavior of next and reset
next
reset
> Counter.prototype.next = function () { return this.n += 2; }> Counter.prototype.reset = function () { this.n = -2; }
Output
> [c1.next(), c1.next(), c1.next(), c1.reset(), c1.next(), c1.next()][ 3, 5, 7, undefined, 0, 2 ]
__proto__
function Foo(y) { this.y = y; }Foo.prototype.x = 10;Foo.prototype.calculate = function (z) { return this.x + this.y + z;};var b = new Foo(20);var c = new Foo(30);
function UpCounter () { this.n = -1; }UpCounter.prototype.next = function () { return ++this.n; }function DownCounter () { this.n = 1; }DownCounter.prototype.next = function () { return --this.n; }var c = new UpCounter ();c.next (); // Returns 0c.next (); // Returns 1c.__proto__ = DownCounter.prototypec.next (); // Returns 0c.next (); // Returns -1
function ResetCounter () { this.reset(); }ResetCounter.prototype.reset = function () { this.n = -1; }var c = new ResetCounter ();c.next (); // TypeError: c.next is not a functionfunction Counter () { this.n = -1; }Counter.prototype.next = function () { return ++this.n; }ResetCounter.prototype.__proto__ = Counter.prototypec.next (); // Returns 0c.next (); // Returns 1
// implicit __proto__ = Object.prototypeclass Counter { // constructor function constructor() { this.n = -1; } // method "next" created on Counter.prototype next() { return ++this.n; }}// extends sets __proto__class ResetCounter extends Counter { // must call super constructor before doing other work constructor() { super(); this.reset(); } // method "reset" created on ResetCounter.prototype reset() { this.n = -1; }}class EvenCounter extends ResetCounter { next() { this.n += 2; return this.n; }}