A simple object.
p = {x: 1, y: 2};
a = p.x; // 1
b = p.y; // 2
p.z = p.x + p.y; // 3
function square(x) {
return x*x;
}
square(4); // returns 16
var sq = square;
sq(4); // returns 16
You can also add properties to it like other objects.
square.x = 1;
The body of the function has access to two magic variables: this and arguments.
The value of this variable depends on how the function is called.
f() -> this = top-level-objecta.f() -> this = awindow object.
The function object has an apply method. Calling f(2) is equivalent to calling:
f.apply(null, [2]);
And calling a.f(2) is equivalent to calling:
f.apply(a, [2]);
function square_me() {
return this.value * this.value;
}
var n = {"value": 2};
n.square = square_me;
n.square(); // returns 4
square_me.apply(n, []); // returns 4
arguments variableThe special variable arguments contains the list of arguments passed to the function.
function count() {
return arguments.length;
}
count(); // 0
count(1, 2, 3); // 3
count(4, 5); // 2
new operator.
function Point(x, y) {
this.x = x;
this.y = y;
}
var p = new Point(3, 4);
p.x; // 3
p.y; // 4
The new operator creates a new object and passes it as this object to the Point function.
function Point(x, y) {
this.x = x;
this.y = y;
}
var p = new Point(3, 4);
Point.prototype.name = "Point"; // Adding a name property to prototype
p.name; // returns "Point"
name property to p, it effects only that object and not the prototype.
p.name = "p"; Point.prototype.name; // will still be "Point"
Point.prototype.magnitude = function() {
return Math.sqrt(this.x*this.x + this.y*this.y);
}
p.magnitude(); // returns 5
function F() {}
F.prototype.x = 1;
var f1 = new F();
f1.x; // 1
// Changing the function prototype here!
F.prototype = {"x": 2};
var f2 = new F();
f2.x; // 2
// What happen to previously created objects when prototype changes?
f1.x; // ??
__proto__.
p.__proto__ == Point.prototype; // true
__proto__ property.
p.__proto__ = {
"name": "foo",
};
p.name; // "foo"
p1 p2
+-----------+ +-----------+
| x |-> 1 | x |---> 5
+-----------+ +-----------+
| y |-> 2 | y |---> 6
+-----------+ +-----------+
| __proto__ |----+ +-| __proto__ |
+-----------+ | | +-----------+
| |
| |
v v
Point.prototype
+--------------+
| name | --> "Point"
+--------------+
| magnitude | --> function() {...}
+--------------+
new operator?
function new_operator(func, args) {
var obj = {};
obj.__proto__ = func.prototype;
func.apply(obj, args);
return obj;
}
var p1 = new_operator(Point, [1, 2]);
p1 instanceof Point; // true
p1.x; // 1
p1.y; // 2
__proto__ is supported.
function new_operator(func, args) {
var obj = {};
obj.__proto__ = func.prototype;
func.apply(obj, args);
return obj;
}
var p1 = new_operator(Point, [1, 2]);
1. Create an empty object
obj +-----------+ | | +-----------+2. Link it with prototype
obj +> Point.prototype +-----------+ | +---------------+ | __proto__ |--+ | .... | +-----------+ +---------------+3. Call the constructor
obj +> Point.prototype +-----------+ | +-------------+ | x |->1 | | .... | +-----------+ | +-------------+ | y |->2 | +-----------+ | | __proto__ |----+ +-----------+
function F() {
this.f = "F";
}
function G() {
this.g = "G";
}
G.prototype.h = "H";
// Here is the magic. Prototype of F is an instance of G.
F.prototype = new G();
x = new F();
x.h; // ??
"H".
x +> F.prototype +> G.prototype +-----------+ | +-----------+ | +-----------+ | f |->"F" | | g |->"G" | | h |-> "H" +-----------+ | +-----------+ | +-----------+ | __proto__ |------+ | __proto__ |------+ | __proto__ |-> ... +-----------+ +-----------+ +-----------+Prototypes can be chained like this! Looks like some kind of inheritance?
var p = new Point(3, 4); p instanceof Point; // true p instanceof Object; // true
function F() {}
function G() {}
f = new F();
f instanceof F; // true
f instanceof G; // false
G.prototype = F.prototype;
f instanceof G; // true
f instanceof F; // true
instanceof operator as a function.
function my_instanceof(object, constructor) {
if (object === Object.prototype)
return false;
return (object.__proto__ === constructor.prototype
|| my_instanceof(object.__proto__, constructor));
}
instanceof and my_instanceof for primitive objects like numbers.
my_instanceof(1, Number); // true my_instanceof(1, Object); // true 1 instanceof Number; // false 1 instanceof Object; // falseDo you know why?
Now lets try to create classes similar to the traditional programming languages like Java and Ruby. We should build the following:
var Animal = Class({
init: function(name) {
this.name = name;
},
eat: function() {
return this.name + " is eating";
}
});
var a = new Animal("dog");
a.eat(); // "dog is eating"
var Bird = Animal.extend({
fly: function() {
return this.name + " is flying";
}
});
var b = new Bird("sparrow");
b.fly(); // sparrow is flying
b.eat(); // sparrow is eating
// Version 1 of Class implementation.
function Class(members) {
var F = function() {
if (members.init) {
members.init.apply(this, arguments);
}
};
F.prototype = members;
return F;
}
var Animal = Class({
init: function(name) {
this.name = name;
},
eat: function() {
return this.name + " is eating";
}});
F / Animal +---> members
+-----------+ | +-------+
| prototype |----+ | init |
+-----------+ +-------+
| eat |
+-------+
var a = new Animal("dog");
a.eat(); // "dog is eating"
a +> members +-----------+ | +------+ | name |->"dog"| | init | +-----------+ | +------+ | __proto__ |-------+ | eat | +-----------+ +------+
OK. We've solved the first part. What about the inheritance?
Lets borrow some ideas from Douglas Crockford.
function object(o) {
function F() {}
F.prototype = o;
return new F();
}
y = object(x);
y x
+-----------+ +-----+
| __proto__ |--->| ... |
+-----------+ +-----+
z = object(y); z y x +-----------+ +-----------+ +-----+ | __proto__ |--->| __proto__ |--->| ... | +-----------+ +-----------+ +-----+
// Version 2 of Class implementation
function Class(members) {
var F = function() {
if (members.init) {
members.init.apply(this, arguments);
}
};
F.prototype = members;
F.extend = function(members) {
// Creating new object using the prototype of F
var new_members = object(F.prototype);
// and adding members to it.
for (var name in members) {
new_members[name] = members[name];
}
return Class(new_members);
};
return F;
}
var Bird = Animal.extend({
fly: function() {
return this.name + " is flying";
}
});
Bird.prototype +--> Animal.prototype +-----------+ | +----------------+ | fly | | | ... | +-----------+ | +----------------+ | __proto__ |----+ +-----------+
var b = new Bird("sparrow");
b +--> Bird.prototype +--> Animal.prototype
+-----------+ | +-----------+ | +---------------+
| name | | | fly | | | ... |
+-----------+ | +-----------+ | +---------------+
| __proto__ |----+ | __proto__ |----+
+-----------+ +-----------+
/
#