JavaScript的好例子';基于原型的继承

JavaScript的好例子';基于原型的继承,javascript,prototypal-inheritance,Javascript,Prototypal Inheritance,我已经用OOP语言编程超过10年了,但我现在正在学习JavaScript,这是我第一次遇到基于原型的继承。我倾向于通过学习好的代码来学习最快的东西。正确使用原型继承的JavaScript应用程序(或库)的编写良好的示例是什么?您能(简要地)描述一下原型继承是如何/在哪里使用的,这样我就知道从哪里开始阅读了吗?我想看看Dean Edward的Base库: 对于YUI,您可以快速查看方法,尤其是方法。然后,您可以浏览一些小部件或实用程序的源代码,并查看它们如何使用该方法。还有Microsoft的Aj

我已经用OOP语言编程超过10年了,但我现在正在学习JavaScript,这是我第一次遇到基于原型的继承。我倾向于通过学习好的代码来学习最快的东西。正确使用原型继承的JavaScript应用程序(或库)的编写良好的示例是什么?您能(简要地)描述一下原型继承是如何/在哪里使用的,这样我就知道从哪里开始阅读了吗?

我想看看Dean Edward的
Base
库:

对于YUI,您可以快速查看方法,尤其是方法。然后,您可以浏览一些小部件或实用程序的源代码,并查看它们如何使用该方法。

还有Microsoft的Ajax库


也有很多好的MSDN文章,包括。

道格拉斯·克罗克福德在以下方面有一个很好的页面:

五年前,我用JavaScript写过。它表明JavaScript是一种无类的原型语言,并且它有足够的表达能力来模拟经典系统。从那时起,我的编程风格不断发展,任何优秀的程序员都应该如此。我已经学会完全接受原型主义,并将自己从古典模式的束缚中解放出来


Dean Edward's或works是用JavaScript实现的方法。

我建议查看PrototypeJS的类。创建:

第83行

我见过的最好的例子是道格拉斯·克罗克福德的。它绝对值得购买,以帮助你对语言有一个平衡的看法


负责JSON格式,并作为JavaScript专家在雅虎工作。

如前所述,道格拉斯·克罗克福德(Douglas Crockford)的电影很好地解释了为什么和如何使用JSON格式。但将其放在几行JavaScript中:

// Declaring our Animal object
var Animal = function () {

    this.name = 'unknown';

    this.getName = function () {
        return this.name;
    }

    return this;
};

// Declaring our Dog object
var Dog = function () {

    // A private variable here        
    var private = 42;

    // overriding the name
    this.name = "Bello";

    // Implementing ".bark()"
    this.bark = function () {
        return 'MEOW';
    }  

    return this;
};


// Dog extends animal
Dog.prototype = new Animal();

// -- Done declaring --

// Creating an instance of Dog.
var dog = new Dog();

// Proving our case
console.log(
    "Is dog an instance of Dog? ", dog instanceof Dog, "\n",
    "Is dog an instance of Animal? ", dog instanceof Animal, "\n",
    dog.bark() +"\n", // Should be: "MEOW"
    dog.getName() +"\n", // Should be: "Bello"
    dog.private +"\n" // Should be: 'undefined'
);
但是,这种方法的问题是,每次创建对象时,它都会重新创建对象。另一种方法是在原型堆栈上声明对象,如下所示:

// Defining test one, prototypal
var testOne = function () {};
testOne.prototype = (function () {
    var me = {}, privateVariable = 42;
    me.someMethod = function () {
        return privateVariable;
    };

    me.publicVariable = "foo bar";
    me.anotherMethod = function () {
        return this.publicVariable;
    };

    return me;

}());


// Defining test two, function
var testTwo = ​function() {
    var me = {}, privateVariable = 42;
    me.someMethod = function () {
        return privateVariable;
    };

    me.publicVariable = "foo bar";
    me.anotherMethod = function () {
        return this.publicVariable;
    };

    return me;
};


// Proving that both techniques are functionally identical
var resultTestOne = new testOne(),
    resultTestTwo = new testTwo();

console.log(
    resultTestOne.someMethod(), // Should print 42
    resultTestOne.publicVariable // Should print "foo bar"
);

console.log(
    resultTestTwo.someMethod(), // Should print 42
    resultTestTwo.publicVariable // Should print "foo bar"
);



// Performance benchmark start
var stop, start, loopCount = 1000000;

// Running testOne
start = (new Date()).getTime(); 
for (var i = loopCount; i>0; i--) {
    new testOne();
}
stop = (new Date()).getTime();

console.log('Test one took: '+ Math.round(((stop/1000) - (start/1000))*1000) +' milliseconds');



// Running testTwo
start = (new Date()).getTime(); 
for (var i = loopCount; i>0; i--) {
    new testTwo();
}
stop = (new Date()).getTime();

console.log('Test two took: '+ Math.round(((stop/1000) - (start/1000))*1000) +' milliseconds');

当涉及到内省时,有一个轻微的缺点。转储testOne将导致有用信息减少。此外,“testOne”中的私有属性“privateVariable”在所有实例中都是共享的,shesek的回复中提到了这一点,这是很有帮助的。

有一个ECMAScript版本特定实现的片段。它将根据当前运行时自动选择在ES6、ES5和ES3实现之间使用哪一个。

这是我在Mixu的节点手册()中找到的最清晰的示例:

function Shape(x, y) {
    this.x = x;
    this.y = y;
}

// 1. Explicitly call base (Shape) constructor from subclass (Circle) constructor passing this as the explicit receiver
function Circle(x, y, r) {
    Shape.call(this, x, y);
    this.r = r;
}

// 2. Use Object.create to construct the subclass prototype object to avoid calling the base constructor
Circle.prototype = Object.create(Shape.prototype);
我喜欢构图而不是继承:

合成-对象的功能由以下各项的集合组成 通过包含其他对象的实例来创建不同的类。 继承-对象的功能由它自己的功能组成 功能加上来自其父类的功能。如果必须的话 要继承,请使用普通的旧JS

如果必须实现继承,至少要避免使用其他继承 非标准实现/魔术功能。这是你能做到的 在纯ES3中实现合理的继承复制(只要 当您遵循从不在原型上定义属性的规则时):

这与经典继承不同,但事实确实如此 标准、可理解的Javascript,并具有 人们主要寻求:可链接的构造函数和调用 超类的方法


ES6
扩展

ES6
扩展
只是以前可能的原型链操作的语法糖,因此可以说是最规范的设置

首先了解有关原型链和属性查找的更多信息,请访问:

现在让我们来解构发生的事情:

class C {
    constructor(i) {
        this.i = i
    }
    inc() {
        return this.i + 1
    }
}

class D extends C {
    constructor(i) {
        super(i)
    }
    inc2() {
        return this.i + 2
    }
}
没有所有预定义对象的简化图:

      __proto__
(C)<---------------(D)         (d)
| |                |           |
| |                |           |
| |prototype       |prototype  |__proto__
| |                |           |
| |                |           |
| |                | +---------+
| |                | |
| |                | |
| |                v v
|__proto__        (D.prototype)
| |                |
| |                |
| |                |__proto__
| |                |
| |                |
| | +--------------+
| | |
| | |
| v v
| (C.prototype)--->(inc)
|
v
Function.prototype
\uuuu proto__
(C) (公司)
|
v
功能原型

在Javascript中添加基于原型的继承示例

// Animal Class
function Animal (name, energy) {
  this.name = name;
  this.energy = energy;
}

Animal.prototype.eat = function (amount) {
  console.log(this.name, "eating. Energy level: ", this.energy);
  this.energy += amount;
  console.log(this.name, "completed eating. Energy level: ", this.energy);
}

Animal.prototype.sleep = function (length) {
  console.log(this.name, "sleeping. Energy level: ", this.energy);
  this.energy -= 1;
  console.log(this.name, "completed sleeping. Energy level: ", this.energy);
}

Animal.prototype.play = function (length) {
  console.log(this.name, " playing. Energy level: ", this.energy);
  this.energy -= length;
  console.log(this.name, "completed playing. Energy level: ", this.energy);
}

// Dog Class
function Dog (name, energy, breed) {
  Animal.call(this, name, energy);
  this.breed = breed;
}

Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;

Dog.prototype.bark = function () {
  console.log(this.name, "barking. Energy level: ", this.energy);
  this.energy -= 1;
  console.log(this.name, "done barking. Energy level: ", this.energy);
}

Dog.prototype.showBreed = function () {
  console.log(this.name,"'s breed is ", this.breed);
}

// Cat Class
function Cat (name, energy, male) {
  Animal.call(this, name, energy);
  this.male = male;
}

Cat.prototype = Object.create(Animal.prototype);
Cat.prototype.constructor = Cat;

Cat.prototype.meow = function () {
  console.log(this.name, "meowing. Energy level: ", this.energy);
  this.energy -= 1;
  console.log(this.name, "done meowing. Energy level: ", this.energy);
}

Cat.prototype.showGender = function () {
  if (this.male) {
    console.log(this.name, "is male.");
  } else {
    console.log(this.name, "is female.");
  }
}

// Instances
const charlie = new Dog("Charlie", 10, "Labrador");
charlie.bark();
charlie.showBreed();

const penny = new Cat("Penny", 8, false);
penny.meow();
penny.showGender();

ES6使用了更简单的继承实现,使用了构造函数和超级关键字。

您有机会查看这个基本库吗?它真的很好,而且很小。如果你喜欢,考虑把我的答案标记为答案。蒂娅,罗兰,我想我和你是同一条船上的人。我还想学习一点关于这种原型语言的知识,而不仅仅局限于oop框架或类似的框架,即使它们很棒,我们也需要学习,对吗?不仅仅是一些框架为我做了这些,即使我打算使用它。但是学习如何用新的语言以新的方式创造新的东西,跳出框框思考。我喜欢你的风格。我会尽力帮助我,也许还会帮助你。我一发现什么就告诉你,有责任吗?这听起来几乎像是“有罪的”:@Roland我认为JSON是一种很好的存储数据的非详细格式。不过,他肯定没有发明它,早在2002年,Steam中的配置设置就有这种格式,我也这么认为-我越来越希望我们都能跳过XML作为交换格式,马上开始使用JSON。没什么可发明的:JSON是JavaScript自己的对象文字语法的一个子集,它在1997年左右就已经在语言中出现了。@Time good point-我从一开始就没有意识到它的存在。请注意,在testOne
privateVariable
中,它只是的范围内的一个变量,并且在所有实例中共享,因此不应该在其上存储特定于实例的数据。(在testTwo上,它是实例特定的,因为每次调用testTwo()都会创建一个新的、每个实例的作用域)我投了赞成票,因为您展示了另一种方法,为什么不使用它,因为它会复制每次重新创建对象的问题主要是由于为每个新对象重新创建的方法。但是,我们可以通过在
Dog.prototype
上定义方法来缓解这个问题。因此,我们不必使用
this.bark=function(){…}
,而可以使用
Dot.prototype.bark=function(){。
// "Classes" are just function objects.
C.constructor === Function
C.__proto__ === Function.prototype
D.constructor === Function
// D is a function "indirectly" through the chain.
D.__proto__ === C
D.__proto__.__proto__ === Function.prototype
// "extends" sets up the prototype chain so that base class
// lookups will work as expected
var d = new D(1)
d.__proto__ === D.prototype
D.prototype.__proto__ === C.prototype
// This is what `d.inc` actually does.
d.__proto__.__proto__.inc === C.prototype.inc
// Class variables
// No ES6 syntax sugar apparently:
// https://stackoverflow.com/questions/22528967/es6-class-variable-alternatives
C.c = 1
C.c === 1
// Because `D.__proto__ === C`.
D.c === 1
// Nothing makes this work.
d.c === undefined
      __proto__
(C)<---------------(D)         (d)
| |                |           |
| |                |           |
| |prototype       |prototype  |__proto__
| |                |           |
| |                |           |
| |                | +---------+
| |                | |
| |                | |
| |                v v
|__proto__        (D.prototype)
| |                |
| |                |
| |                |__proto__
| |                |
| |                |
| | +--------------+
| | |
| | |
| v v
| (C.prototype)--->(inc)
|
v
Function.prototype
// Animal Class
function Animal (name, energy) {
  this.name = name;
  this.energy = energy;
}

Animal.prototype.eat = function (amount) {
  console.log(this.name, "eating. Energy level: ", this.energy);
  this.energy += amount;
  console.log(this.name, "completed eating. Energy level: ", this.energy);
}

Animal.prototype.sleep = function (length) {
  console.log(this.name, "sleeping. Energy level: ", this.energy);
  this.energy -= 1;
  console.log(this.name, "completed sleeping. Energy level: ", this.energy);
}

Animal.prototype.play = function (length) {
  console.log(this.name, " playing. Energy level: ", this.energy);
  this.energy -= length;
  console.log(this.name, "completed playing. Energy level: ", this.energy);
}

// Dog Class
function Dog (name, energy, breed) {
  Animal.call(this, name, energy);
  this.breed = breed;
}

Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;

Dog.prototype.bark = function () {
  console.log(this.name, "barking. Energy level: ", this.energy);
  this.energy -= 1;
  console.log(this.name, "done barking. Energy level: ", this.energy);
}

Dog.prototype.showBreed = function () {
  console.log(this.name,"'s breed is ", this.breed);
}

// Cat Class
function Cat (name, energy, male) {
  Animal.call(this, name, energy);
  this.male = male;
}

Cat.prototype = Object.create(Animal.prototype);
Cat.prototype.constructor = Cat;

Cat.prototype.meow = function () {
  console.log(this.name, "meowing. Energy level: ", this.energy);
  this.energy -= 1;
  console.log(this.name, "done meowing. Energy level: ", this.energy);
}

Cat.prototype.showGender = function () {
  if (this.male) {
    console.log(this.name, "is male.");
  } else {
    console.log(this.name, "is female.");
  }
}

// Instances
const charlie = new Dog("Charlie", 10, "Labrador");
charlie.bark();
charlie.showBreed();

const penny = new Cat("Penny", 8, false);
penny.meow();
penny.showGender();