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 16You 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-object
a.f() -> this = a
window
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; // 4The
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__ |----+ +-----------+ +-----------+
/
#