Why declare properties on the prototype for instan

2019-01-21 11:21发布

问题:

I'm trying to get my head around this black art called JavaScript - and, I must admit, pretty excited about it. I've been looking at code examples, mainly from "easeljs" since that is what I will be using mainly. And I'm a bit confused..

I (think I) understand the difference between using the prototype for functions or properties that are class variables and using this.someProp for 'instance' variables (Yes, I understand that there are no classes in JavaScript.)

The code I have looked at, and am using as templates for my own code, declare prototype variables and then refers to them with this i.e.

In the constructor:

this.name = name;

Then a declaration:

Object.prototype.name;

And later,

this.name = "Freddy";

This is within functions called with 'new' so in this case, as I understand it, this refers to the current object. What puzzles me is what the prototype declaration is doing and why do we use it for instance variables?


Clarification: In the following code, I don't see what the prototype declaration of radius is achieving:

(function(){
    // constructor
    function MyCircle(radius){
        this.radius = radius;
    }
    MyCircle.prototype.radius;
    this.area = function(){
        return 3.14*this.radius*this.radius;
    };
    window.MyCircle = MyCircle;
}());

回答1:

The value on a prototype has a key behaviour that is different from a property set directly on the instance. Try this:

// Create a constructor
function A() {}

// Add a prototype property
A.prototype.name = "Freddy";

// Create two object instances from
// the constructor
var a = new A();
var b = new A();

// Both instances have the property
// that we created on the prototype
console.log(a.name); // Freddy
console.log(b.name); // Freddy

// Now change the property on the
// prototype
A.prototype.name = "George";

// Both instances inherit the change.
// Really they are just reading the
// same property from the prototype
// rather than their own property
console.log(a.name); // George
console.log(b.name); // George

This would not possible without prototypical inheritance.

You can test whether the property is the instances property or the prototype property using the hasOwnProperty method.

console.log(a.hasOwnProperty("name")); // false

An instance can override the prototype value.

b.name = "Chris";
console.log(b.hasOwnProperty("name")); // true
console.log(a.name); // George
console.log(b.name); // Chris

And return to the prototype value.

delete b.name;
console.log(b.hasOwnProperty("name")); // false
console.log(b.name); // George

This is a powerful part of prototypical inheritance.

In the other pattern:

function A() {
  this.name = "George";
}

The this.name variable is declared again with every new instance.

It makes some sense to have methods as functions declared on the prototype. Rather than the function definition being re-declared on every instance, all instances can share a single function.

In terms of variables, rather than functions, the prototype can possibly be used for default values in the case that an instance does not set its own value.

The code in a fiddle



回答2:

A value stored on the prototype provides a default value for that property.

If you subsequently write a value to that property, the instance will acquire that new value, hiding the value that's on the prototype, which will be left intact.

In the context of the code you've now added to the question:

MyCircle.prototype.radius;

does absolutely nothing. It's a no-op - it attempts to read that property and then discards the result.



回答3:

Yeah, I agree the prototype can be used for default values of properties (variables). The constructor function doesn't need to declare a property; it may be done conditionally.

function Person( name, age ) {
    this.name = name;

    if ( age ) {
        this.age = age;
    }
}

Person.prototype.sayHello = function() {
    console.log( 'My name is ' + this.name + '.' );
};

Person.prototype.sayAge = function() {
    if ( this.age ) {
        console.log( 'I am ' + this.age + ' yrs old!' ); 
    } else {
        console.log( 'I do not know my age!' );
    }
};

Person.prototype.age = 0.7;

//-----------

var person = new Person( 'Lucy' );
console.log( 'person.name', person.name ); // Lucy
console.log( 'person.age', person.age );   // 0.7
person.sayAge();                           // I am 0.7 yrs old!

See how Lucy's age is conditionally declared and initialized.



回答4:

Other answers have already explained the difference between prototype vs instance properties.

But just to add to the answer, let's break down your code snippet:

(function(){                         // <------- 1
   // constructor
   function MyCircle(radius){        // <------- 2
       this.radius = radius;         // <------- 2.1
   }
   MyCircle.prototype.radius;        // <------- 3
   this.area = function(){           // <------- 4
       return 3.14*this.radius*this.radius;
   };
   window.MyCircle = MyCircle;       // <------- 5
}());
  1. Creating an IIFE which acts as a scope container for the inner code
  2. Declaring a function called MyCircle using a constructor pattern (but observe that it never gets "constructed" so should probably get rid of the capital letter since it's misleading)
    • when invoked creates a radius instance property on the invoked object
  3. Attempting to access a radius property on MyCircle function's prototype which doesn't exist so evaluates to undefined
  4. Creating an area instance property on the global window object and assigning it a function expression
  5. Creating a MyCircle instance property on a window object and assigning to it the MyCircle function

Summary: It seems like it's creating an area and MyCircle properties on the global window object, and when MyCircle is invoked it creates an additional radius property.

Usage: MyCircle should be invoked before area since area relies on MyCircle initialising the radius:

window.MyCircle(10);
window.area(); // evaluates to 314