【问题标题】:stoyanov javascript prototype exercises from object oriented javascriptstoyanov javascript原型练习来自面向对象的javascript
【发布时间】:2013-08-31 22:12:18
【问题描述】:

虽然我有一些使用 jQuery 和 JavaScript 的工作经验,但我仍然觉得很难理解原型继承。因此,我开始阅读 Stoyan Stefanov 的《面向对象的 JavaScript》一书。但是,我在解决书中的以下练习时遇到了问题:

  1. 创建一个名为shape 的对象,该对象具有type 属性和getType 方法。
  2. 定义一个原型为shape的Triangle构造函数。使用Triangle 创建的对象应具有三个自己的属性:abc,代表三角形的边。
  3. 向原型添加一个名为getPerimeter 的新方法。

使用此代码测试您的实现:

var t = new Triangle(1, 2, 3);
t.constructor;                 // Triangle(a, b, c)    
shape.isPrototypeOf(t);        // true
t.getPerimeter();              // 6
t.getType();                   // "triangle"

我已尝试使用以下代码解决此问题:

shape = {
    type : "",
    getType: function(){
        return this.type;
    }
};

function Triangle(a, b, c) {
}

Triangle.prototype = shape;

但是它似乎没有按预期工作。你将如何解决这个问题?请详细说明。我真的很想了解原型继承。

【问题讨论】:

  • 为什么你的代码里有一堆...?
  • 我直接从pdf中复制粘贴了

标签: javascript prototype


【解决方案1】:

您不会对传递给构造函数的参数做任何事情,可能假设它们只是分配给新创建的对象。问题是,他们不是。

你应该这样写……

var shape = {
  type: '',
  getType: function() { return this.type; }
};

function Triangle(a, b, c) {
  this.type = 'triangle';
  this.a = a;
  this.b = b;
  this.c = c;
}

Triangle.prototype = shape;
Triangle.prototype.getPerimeter = function() {
  return this.a + this.b + this.c;
};
Triangle.prototype.constructor = Triangle;

要点(为什么constructor 是为prototype 定义的)非常简单:每个Triangle 对象都应该知道它的构造函数,但是对于Triangle 的每个实例,这个属性都是相同的。这就是为什么它把它放在Triangle.prototype 上。

【讨论】:

  • t.constructor 仍然返回 function Object() { [native code] } 而不是 Triangle(a,b,c)
  • 你的意思是 Triangle.prototype.constructor = shape;但是代码仍然无法按预期工作,t.constructor 返回形状对象而不是 Triangle(a, b, c)
  • @viktor 实际上,我的意思是我写的。 shape 是一个原型对象。 Triangle 是一个构造函数。对于使用Triangle 函数创建的所有对象都是一样的,因此它被分配给Triangle.prototype,而不是Triangle 的每个实例。
  • 是的,这可以解决问题,我没有想到必须重置构造函数,但是在这种情况下,必须在 var t; 之后分配函数的参数;已定义,还有 getPerimeter() 方法。它是否正确?同样在这种情况下,三角形是否仍然继承自形状?
【解决方案2】:

这样的事情会起作用:

function Shape() {
    this.type = "shape";
    this.getType = function(){
        return this.type;
    }
}

function Triangle(a,b,c){
     this.type="triangle";
     this.a =a;
     this.b = b;
     this.c = c;
}

var shape = new Shape(); //follow the requirements a bit more literally :)
Triangle.prototype = shape;

Triangle.prototype.getPerimeter = function() {
  return this.a + this.b + this.c;
}

jsfiddle 示例:http://jsfiddle.net/TbR6q/1

切线,这是一个咖啡脚本非常好的领域,可以让你更加清晰/简洁。这是 Coffeescript 中的等价物。

class Shape 
  constructor: ->
    @type = "shape"
  getType : -> @type

class Triangle extends Shape
  constructor: (@a,@b,@c) ->
     @type="triangle"
  getPerimeter: () -> @a + @b + @c

http://jsfiddle.net/qGtmX/

【讨论】:

  • Shape 是一个对象而不是一个函数,并且 t.constructor 必须返回 Triangle(a,b,c);在创建函数 Triangle 后还会添加 getPerimeter。
  • 您使用 shape 作为构造函数来创建对象。您不需要这样做,但是当像这样进行继承时,它会产生更一致的表示法。您可以在创建三角形之后或期间添加 getPerimeter。如果在之后添加它,则需要将 a、b、c 变量设置为 Triangle 对象的属性。看起来确实是这个意图,因为它提到将其添加到原型中。
  • 我明白你的意思,但要求是:“创建一个名为 shape 的对象”。 getPerimeter 在创建 Triangle 之后添加。发现我可以为一个对象创建一个函数点对我来说是非常新鲜的事情,我认为为了学习目的而这样做更好。
  • 当然。随意做任何你想做的事情:) 我向你展示了一种我觉得很清楚的方法,并完成了你想做的事情。我没有命名我的对象形状,我用构造函数构建了它,但它仍然是一个在原型上具有这些属性的对象。如果您不喜欢我的答案/符号,则不必使用它:)rainna77wow 的答案也是正确的,并且似乎更符合要求。但是我们使用相同的概念,无论是构造函数、对象字面量,还是我的咖啡脚本示例。无论如何,概念是这里的重点。
  • 我同意你的观点,但是我对原型的工作方式非常粗略,我宁愿做一些更复杂的事情并遵循确切的要求以加深我的理解。我理解你的观点,你是对的,但我追求的是别的东西。
【解决方案3】:

你在正确的轨道上。你的代码是正确的。你只需要再添加几行代码:

shape = {
    type : "",
    getType: function () {
        return this.type;
    }
};

function Triangle(a, b, c) {
    this.type = "triangle";
    this.a = a;
    this.b = b;
    this.c = c;
}

Triangle.prototype = shape;

shape.getPerimeter = function () {
    return this.a + this.b + this.c;
};

要了解发生了什么,我建议您阅读以下答案:

  1. Object Inheritance in JavaScript
  2. What are the downsides of defining functions on prototype this way?
  3. JavaScript inheritance and the constructor property

【讨论】:

  • 不错的方法,但是如果我们运行 Triangle(1,2,3),getPerimeter 不会返回 6
  • @viktor 是的,它会的。他将函数附加到 shape,这与 Triangle.prototype 指向的对象相同。它工作正常,最终与我的答案或raina77ow 的相同。这也是我最初只是将函数放在 Shape 构造函数中的原因。既然一样。小提琴:jsfiddle.net/TbR6q/2
【解决方案4】:

为了学习,我会变成这样

function Shape(){
    this.type = 'Shape';
    this.getType = function()
    {
        return this.type;
    }
}

var shape = new Shape();

function Triangle(a, b ,c)
{
    this.type = 'triangle';
    this.arguments = arguments;
}

Triangle.prototype = shape;

var triangle = new Triangle(1, 2, 3);

triangle.getType();

Triangle.prototype.getParimeter = function()
{
    var perimeter = 0;
    for(i = 0; i < this.arguments.length; i++){
        perimeter = perimeter + this.arguments[i];
    }
    return perimeter;
}

console.log(triangle.getParimeter());

【讨论】:

    【解决方案5】:

    这是一种解决方案(对 cme​​ts 的解释):

    shape = {
        type : "",
        getType: function(){
            return this.type;
        }
    };
    
    function Triangle(a,b,c){
        //This three variables are defined inside a closure, so in this case
        //only the getPermiter function can access them
        var A = a, B = b, C = c;
    
        //The new Triangle object is crafted in the lines below as usual
        this.type = "triangle";
        this.getPerimeter = function(){
            return A + B + C;
        }
    }
    
    //Here we set the triangle prototype to point the shape object.
    //So every time we call the Triangle function with the "new" operator
    //the __proto__ internal property of the newly created object will be
    //the shape object.
    Triangle.prototype = Object.create(shape);
    
    //The problem is that the shape object doesn't have a constructor property,
    //so the shape constructor is shape.__proto__.constructor, which is the
    //Object function. 
    //All this means that when we create a new object with the Triangle function the
    //constructor property will be the Object function (shape.__proto__.constructor).
    //To avoid this we must manually set the constructor to be Triangle.
    Triangle.prototype.constructor = Triangle;
    
    var t = new Triangle(1, 2, 3);
    console.log(t.constructor === Triangle);   
    console.log(shape.isPrototypeOf(t) === true);
    console.log(t.getPerimeter() === 6);
    console.log(t.getType() === "triangle");
    

    【讨论】:

    • 为什么不使用 object.create 并为旧版浏览器 polyfil 它。之后可以设置 Triangle.prototype.constructor
    • 首先,在问题中指定了针对代码测试实现,并且该代码以“var t = new Triangle(1, 2, 3);”行开头。因此暗示我们必须使用函数来构造对象(因为 new 运算符)。我可以用 Object.create 创建一个对象,这是真的,但不需要使用 Triangle 函数。尽管如此,假设我创建了一个函数“Triangle”,在其中执行 Object.create 并返回新创建的对象,无论如何我都必须将 Triangle.prototype.contructor 设置为 Triangle,所以效果是一样的。
    • 你没有抓住重点; Triangle.prototype === shape,当我对 Square.prototpe 使用 shape 并设置 Square.prototype.constructor=Square 时,它​​会将 Triangle 的构造函数设置为 Square。像这样设置原型并不好,因为三角形是一个形状,但形状不是三角形。你应该这样做:Triangle.prototype = Object.create(shape);
    • 你说得对,我没听懂你的意思。我更正了我的回答。
    最近更新 更多