为什么在JavaScript中为实例变量声明属性

时间:2013-05-25 15:47:59

标签: javascript prototype this

我正试图了解这种名为JavaScript的黑色艺术 - 而且,我必须承认,对它非常兴奋。我一直在寻找代码示例,主要来自“easeljs”,因为这是我将主要使用的。而且我有点困惑..

我(我想)我理解使用prototype作为class变量的函数或属性,并使用this.someProp作为'实例'变量之间的区别(是的,我明白那里) JavaScript中没有类。)

我看过的代码,我用作我自己的代码declare prototype变量的模板,然后用它来引用它们。

在构造函数中:

this.name = name;

然后声明:

Object.prototype.name;

后来,

this.name = "Freddy";

这是在'new'调用的函数中,所以在这种情况下,据我所知,this引用了当前对象。令我困惑的是原型声明正在做什么,为什么我们将它用作实例变量?


澄清:在下面的代码中,我看不到radius的原型声明实现了什么:

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

4 个答案:

答案 0 :(得分:55)

原型上的值具有与直接在实例上设置的属性不同的键行为。试试这个:

// 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

如果没有原型继承,这是不可能的。

您可以使用hasOwnProperty方法测试属性是instance属性还是prototype属性。

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

实例可以覆盖prototype值。

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

并返回prototype值。

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

这是原型继承的一个重要部分。

在另一种模式中:

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

每个新实例都会再次声明this.name变量。

将方法作为在原型上声明的函数是有道理的。所有实例都可以共享一个函数,而不是在每个实例上重新声明函数定义。

就变量而非函数而言,在实例未设置自己的值的情况下,原型可能用于默认值。

The code in a fiddle

答案 1 :(得分:11)

原型上存储的值为该属性提供默认值。

如果您随后将值写入该属性,实例将获取该新值,隐藏原型上的值,该值将保持不变。

在代码的上下文中,您现在已添加到问题中:

MyCircle.prototype.radius;

绝对没有。这是一个无操作 - 它试图读取该属性,然后丢弃结果。

答案 2 :(得分:0)

是的,我同意原型可用于属性(变量)的默认值。构造函数不需要声明属性;它可以有条件地完成。

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!

了解露西的age是如何有条件地宣布和初始化的。

答案 3 :(得分:0)

其他答案已经解释了原型与实例属性之间的区别。

但只是为了添加答案,让我们分解你的代码片段:

(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. 创建IIFE作为内部代码的范围容器
  2. 使用构造函数模式声明一个名为MyCircle的函数(但请注意它永远不会被“构造”所以应该除掉大写字母,因为它会误导)
    • 在被调用时在被调用对象上创建radius实例属性
  3. 尝试访问不存在的radius函数MyCircle上的prototype属性,因此评估为undefined
  4. 在全局窗口对象上创建area实例属性并为其指定函数表达式
  5. MyCircle对象上创建window实例属性并为其分配MyCircle函数
  6. 摘要:似乎它正在全局area对象上创建MyCirclewindow属性,并且在调用MyCircle时创建一个额外的radius属性。

    用法:应该在区域之前调用MyCircle,因为区域依赖于MyCircle初始化半径:

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