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

发布于 2024-08-18 01:03:43 字数 163 浏览 11 评论 0原文

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

I have been programming with OOP languages for over 10 years but I'm learning JavaScript now and it's the first time I've encountered prototype-based inheritance. I tend to learn fastest by studying good code. What's a well-written example of a JavaScript application (or library) that properly uses prototypal inheritance? And can you describe (briefly) how/where prototypal inheritance is used, so I know where to start reading?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(11

孤独患者 2024-08-25 01:03:43

如前所述,道格拉斯·克罗克福德 (Douglas Crockford) 的电影很好地解释了原因和方法。但用几行 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 的回复中也提到了这一点。

As mentioned, the movies by Douglas Crockford give a good explanation about the why and it covers the how. But to put it in a couple of lines of 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'
);

The problem with this approach however, is that it will re-create the object every time you create one. Another approach is to declare your objects on the prototype stack, like so:

// 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');

There is a slight downside when it comes to introspection. Dumping testOne, will result in less useful information. Also the private property "privateVariable" in "testOne" is shared in all instances, als helpfully mentioned in the replies by shesek.

策马西风 2024-08-25 01:03:43

Douglas Crockford 在 JavaScript 原型继承 上有一个不错的页面:

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

Dean Edward 的 Base.jsMootools 类John Resig 类简单继承工作是在JavaScript中执行经典继承的方法。

Douglas Crockford has a nice page on JavaScript Prototypal Inheritance:

Five years ago I wrote Classical Inheritance in JavaScript. It showed that JavaScript is a class-free, prototypal language, and that it has sufficient expressive power to simulate a classical system. My programming style has evolved since then, as any good programmer's should. I have learned to fully embrace prototypalism, and have liberated myself from the confines of the classical model.

Dean Edward's Base.js, Mootools's Class or John Resig's Simple Inheritance works are ways to do classical inheritance in JavaScript.

雨后咖啡店 2024-08-25 01:03:43
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);
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);
另类 2024-08-25 01:03:43

我会看一下 YUI 和 Dean Edward 的 Base 库: http://dean.edwards.name/weblog/2006/03/base/

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

I would take a look at YUI, and at Dean Edward's Base library: http://dean.edwards.name/weblog/2006/03/base/

For YUI you can take a quick look at the lang module, esp. the YAHOO.lang.extend method. And then, you can browse the source of some widgets or utilities and see how they use that method.

居里长安 2024-08-25 01:03:43

还有 Microsoft 的 ASP.NET Ajax 库,http://www.asp.net/ajax/

还有很多不错的 MSDN 文章,包括创建高级采用面向对象技术的 Web 应用程序

There's also Microsoft's ASP.NET Ajax library, http://www.asp.net/ajax/.

There are a lot of good MSDN articles around as well, including Create Advanced Web Applications With Object-Oriented Techniques.

屌丝范 2024-08-25 01:03:43

这是我发现的最清晰的例子,来自 Mixu 的 Node 书籍 (http://book.mixu. net/node/ch6.html):

我更喜欢组合而不是继承:

组合 - 对象的功能由以下内容的集合组成
通过包含其他对象的实例来区分不同的类。
继承——对象的功能由它自己组成
功能加上其父类的功能。如果你必须
有继承,使用普通的旧JS

如果你必须实现继承,至少避免使用另一个
非标准实现/魔术函数。您可以通过以下方法
在纯 ES3 中实现合理的继承传真(只要
因为您遵循从不在原型上定义属性的规则):

函数动物(名称){
  this.name = 名称;
};
Animal.prototype.move = 函数(米) {
  console.log(this.name+"移动了"+meters+"m.");
};

函数蛇(){
  Animal.apply(this, Array.prototype.slice.call(arguments));
};
Snake.prototype = new Animal();
Snake.prototype.move = function() {
  console.log("滑行...");
  Animal.prototype.move.call(this, 5);
};

var sam = new Snake("Python 萨米");
萨姆.移动();

这与经典继承不同 - 但它是
标准的、易于理解的 Javascript 并具有以下功能
人们主要寻求:可链接的构造函数和调用的能力
超类的方法。

This is the clearest example I've found, from Mixu's Node book (http://book.mixu.net/node/ch6.html):

I favor composition over inheritance:

Composition - Functionality of an object is made up of an aggregate of
different classes by containing instances of other objects.
Inheritance - Functionality of an object is made up of it's own
functionality plus functionality from its parent classes. If you must
have inheritance, use plain old JS

If you must implement inheritance, at least avoid using yet another
nonstandard implementation / magic function. Here is how you can
implement a reasonable facsimile of inheritance in pure ES3 (as long
as you follow the rule of never defining properties on prototypes):

function Animal(name) {
  this.name = name;
};
Animal.prototype.move = function(meters) {
  console.log(this.name+" moved "+meters+"m.");
};

function Snake() {
  Animal.apply(this, Array.prototype.slice.call(arguments));
};
Snake.prototype = new Animal();
Snake.prototype.move = function() {
  console.log("Slithering...");
  Animal.prototype.move.call(this, 5);
};

var sam = new Snake("Sammy the Python");
sam.move();

This is not the same thing as classical inheritance - but it is
standard, understandable Javascript and has the functionality that
people mostly seek: chainable constructors and the ability to call
methods of the superclass.

西瓜 2024-08-25 01:03:43

ES6 classextends

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

首先了解有关原型链和 . 属性查找的更多信息:https://stackoverflow.com/a/ 23877420/895245

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

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
    }
}
// Inheritance syntax works as expected.
(new C(1)).inc() === 2
(new D(1)).inc() === 2
(new D(1)).inc2() === 3
// "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

ES6 class and extends

ES6 class and extends are just syntax sugar for previously possible prototype chain manipulation, and so arguably the most canonical setup.

First learn more about the prototype chain and . property lookup at: https://stackoverflow.com/a/23877420/895245

Now let's deconstruct what happens:

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
    }
}
// Inheritance syntax works as expected.
(new C(1)).inc() === 2
(new D(1)).inc() === 2
(new D(1)).inc2() === 3
// "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

Simplified diagram without all predefined objects:

      __proto__
(C)<---------------(D)         (d)
| |                |           |
| |                |           |
| |prototype       |prototype  |__proto__
| |                |           |
| |                |           |
| |                | +---------+
| |                | |
| |                | |
| |                v v
|__proto__        (D.prototype)
| |                |
| |                |
| |                |__proto__
| |                |
| |                |
| | +--------------+
| | |
| | |
| v v
| (C.prototype)--->(inc)
|
v
Function.prototype
孤云独去闲 2024-08-25 01:03:43

我建议查看 PrototypeJS 的 Class.create:
第 83 行 @ http://prototypejs.org/assets/2009/8/31 /prototype.js

I suggest looking at PrototypeJS' Class.create:
Line 83 @ http://prototypejs.org/assets/2009/8/31/prototype.js

瑕疵 2024-08-25 01:03:43

我见过的最好的例子是 Douglas Crockford 的 JavaScript:好的部分。它绝对值得购买,可以帮助您对语言有一个平衡的看法。

Douglas Crockford 负责 JSON 格式,在雅虎担任 JavaScript 专家。

The best examples I've seen are in Douglas Crockford's JavaScript: The Good Parts. It's definitely worth buying to help you get a balanced view on the language.

Douglas Crockford is responsible for the JSON format and works at Yahoo as a JavaScript guru.

乱了心跳 2024-08-25 01:03:43

有一个片段 JavaScript Prototype-based Inheritance 与 ECMAScript 版本特定实施。它会根据当前运行时自动选择使用 ES6、ES5 和 ES3 实现。

There is a snippet JavaScript Prototype-based Inheritance with ECMAScript version specific implementations. It will automatically choose which to use between ES6, ES5 and ES3 implementations according to current runtime.

三生一梦 2024-08-25 01:03:43

在 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 使用更简单的继承实现构造函数和 super 关键字。

Adding an example of prototype-based inheritance in 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 uses a far easier implementation of inheritance with the use of constructor and super keywords.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文