function Cat (name) {
this.name = name;
}
Cat.__proto__ === Function.__proto; //true
Cat instanceof Cat; //false
Cat instanceof Function; //true
Cat instanceof Object; //true
new
keyword.var senea = new Cat("Senea");
Cat.__proto__ === senea.__proto__; //false
Cat.prototype === senea.__proto__; //true -- Cat.prototype and senea.__proto__ are the SAME OBJECT
senea instanceof Cat; //true
senea instanceof Function; //false
senea instanceof Object; //true
__proto__
property. This property points to the prototype property of the immediate constructor.Cat.prototype.noise = "Meow!"; //notice that we can change the prototype after senea has been created
console.log(senea.noise); //Meow!
console.log(senea.__proto__.noise); //Meow!
__proto__
console.log(senea.name); //Senea
typeof senea.__proto__.name; //undefined
typeof Cat.prototype.name; //undefined
__proto__
is an object, and hence has its own __proto__
property. This points not to Cat's prototype, but to the prototype of the object from which Cat was constructed.senea.__proto__.__proto__; //empty object {}
senea.__proto__.__proto__.type = "animal"; //a Senea is a type of animal
console.log(senea.type); //animal
console.log(Cat.type); //animal
//Let's be more specific.
Cat.prototype.type = "cat";
console.log(Cat.type); //still animal, but...
console.log(senea.type); //cat! we defined "type" on the Cat.prototype, so that was found first.
senea.type = "SeneaBeast";
console.log(senea.type); //SeneaBeast. Now the property is found immediately. No need to go up.
//Note: this is not how you would normally do things; it's just a cool demonstration.
senea.constructor; //Cat
senea.constructor.prototype.class = "feline";
senea.class; //feline
function Kitten(name, age) {
this.age = age;
Kitten._super.call(this, name, age); //Call the super constructor, i.e. Cat
}
Kitten.prototype = senea; //We set the prototype to a created object
Kitten._super = senea.constructor; //We set a reference to Senea's constructor function
var amala = new Kitten("Amala", 2);
console.log(amala.name); //Amala
console.log(amala.age); //2
console.log(amala.class); //feline -- yes, these properties, too
console.log(amala.type); //animal -- NOT SeneaBeast
//And, last but not least...
console.log(amala.noise); //Meow!
Cat.prototype.age = "unknown";
console.log(senea.age); //unknown
console.log(amala.age); //2
Cat.prototype.sayAge = function(){
console.log("My name is " + this.name + " and my age is " + this.age + ". " + this.noise);
}
senea.sayAge(); //"My name is Senea and my age is unknown. Meow!"
amala.sayAge(); //"My name is Amala and my age is 2. Meow!"
Cat.prototype.noise = "Bark!";
senea.age = 4;
senea.sayAge(); //"My name is Senea and my age is 4. Bark!"
amala.sayAge(); //"My name is Amala and my age is 2. Bark!"
JavaScript has function-level scope.
var name = "Terrence";
function area(length, width) {
console.log(name);
return length * width;
}
area(2, 2); //prints Terrence
function area2(length, width) {
var name = "Heather";
console.log(name);
return length * width;
}
area2(2, 2); //prints Heather
console.log(name); //prints Terrence
var name = "Terrence";
if(true) {
var name = "Heather";
}
console.log(name); //prints Heather
Variable and function declarations are silently moved to the top of the scope.
function area(length, width) {
if(length > 2) {
var name; //the declaration is moved to the top of the function; note that an assignment, e.g. name = "Heather" would not be!
}
name = "Heather";
console.log(name); //Heather
}
var name = "Terrence";
area(2, 2); //prints Heather. No error is raised.
console.log(name); //prints Terrence. The assignment in area was NOT global because the declaration was moved out of the conditional.
HOW a function is invoked determines the value of "this". Crockford sets out four function invocation patterns for JavaScript:
function Cat(name) {
this.name = name;
}
//Function invocation pattern
var cat = Cat("Senea");
console.log(this.name); //prints Senea -- oops, in Cat, "this" was bound to global scope!
delete this.name; //remove the errant property. this.name is now undefined.
var senea = new Cat("Senea"); //constructor invocation
console.log(this.name); //is undefined.
console.log(senea.name); //Senea
senea.makeNoise = function() {
console.log(this.name + " says meow!");
}
senea.makeNoise(); //Senea says meow. "this" is bound to the senea object.
//Apply/call
var amala = new Cat("Amala");
amala.makeNoise = function() {
console.log(this.name + " says meow!");
}
amala.makeNoise(); //Amala says meow. "this" is bound to the amala object.
amala.makeNoise.apply(senea); //Senea says meow. "this" is bound to the senea object.
This can cause problems when a function is executed inside another function. A value assigned to a property of "this" outside the inner function will not be accessible inside that function.
//Note, this is a stupid example. There are many better ways of doing this. It's just to demonstrate.
function purr(){
console.log(this.name + " purrs!");
}
function Cat(name, action) {
this.name = name;
action();
}
var senea = new Cat("Senea", purr); // prints "undefined purrs";
function Cat(name, action) {
this.name = name;
this.action = action;
this.action();
}
var senea = new Cat("Senea", purr); // prints "Senea purrs!" -- because of method invocation.
//However ...
function Cat(name) {
this.name = name;
}
Cat.prototype.doTheThing = function(aThing){
console.log(this.name + " is doing: " +aThing());
}
var senea = new Cat("Senea");
senea.doTheThing(function(){
return "counting the letters in her name: " + this.name;
}); // prints "Senea is doing: counting the letters in her name: undefined"
senea.doTheThing(function(){
return "counting the letters in her name: " + this.name;
}.bind(senea)); //prints "Senea is doing: counting the letters in her name: senea"
var
.Functions must be declared at the top of functions or scripts.
if(true) {
function Cat(name) {
this.name = name;
}
}