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

Ale*_*ner 88 javascript prototypal-inheritance

我用OOP语言编程已超过10年,但我现在正在学习JavaScript,这是我第一次遇到基于原型的继承.通过学习优秀的代码,我倾向于学得最快.什么是正确使用原型继承的JavaScript应用程序(或库)的精心编写的示例?您能否(简要地)描述原型继承的使用方式/原因,我知道从哪里开始阅读?

Dyn*_*nom 76

如上所述,道格拉斯克罗克福德的电影给出了一个很好的解释,为什么,它涵盖了如何.但是把它放在几行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'
);
Run Code Online (Sandbox Code Playgroud)

但是,这种方法的问题在于,每次创建对象时都会重新创建对象.另一种方法是在原型堆栈上声明对象,如下所示:

// 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');
Run Code Online (Sandbox Code Playgroud)

在内省方面存在轻微的缺点.倾倒testOne,会导致信息不太有用."testOne"中的私有财产"privateVariable"在所有情况下都是共享的,在shesek的回复中也有帮助.

  • 请注意,在testOne中,`privateVariable`只是[IIFE](http://benalman.com/news/2010/11/immediately-invoked-function-expression/)范围内的一个变量,并且它在所有内容中共享实例,因此您不应该在其上存储特定于实例的数据.(在testTwo上它是特定于实例的,因为每次调用testTwo()都会创建一个新的每个实例的范围) (3认同)

Gre*_*osz 48

Douglas Crockford在JavaScript Prototypal Inheritance上有一个很好的页面:

五年前,我用JavaScript 编写了Classical Inheritance.它表明JavaScript是一种无类别的原型语言,它具有足够的表达能力来模拟经典系统.从那时起,我的编程风格就已经发生了变化,就像任何优秀的程序员一样.我学会了完全接受原型主义,并将自己从经典模型的范围中解放出来.

Dean Edward的Base.js,Mootools的类John Resig的简单继承作品是在JavaScript中进行经典继承的方法.


Vla*_*den 26

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);
Run Code Online (Sandbox Code Playgroud)

  • 也许在你的回答中添加这个链接可能会更多地完成图片:https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/create (3认同)

Rol*_*man 14

我会看看YUI和Dean Edward的Base图书馆:http://dean.edwards.name/weblog/2006/03/base/

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


Kev*_*nes 5

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

有很多很好的MSDN文章,包括使用面向对象技术创建高级Web应用程序.


sup*_*nee 5

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

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

组合 - 对象的功能由包含其他对象实例的不同类的聚合组成。继承 - 对象的功能由它自己的功能加上来自其父类的功能组成。如果必须继承,请使用普通的旧 JS

如果你必须实现继承,至少避免使用另一个非标准的实现/魔法函数。以下是如何在纯 ES3 中实现合理的继承复制品(只要您遵循永远不要在原型上定义属性的规则):

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();
Run Code Online (Sandbox Code Playgroud)

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


Cir*_*四事件 5

ES6classextends

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

首先了解有关原型链和.属性查找的更多信息:/sf/answers/1671419431/

现在让我们来解构发生了什么:

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
    }
}
Run Code Online (Sandbox Code Playgroud)
// Inheritance syntax works as expected.
(new C(1)).inc() === 2
(new D(1)).inc() === 2
(new D(1)).inc2() === 3
Run Code Online (Sandbox Code Playgroud)
// "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
Run Code Online (Sandbox Code Playgroud)
// "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
Run Code Online (Sandbox Code Playgroud)
// Class variables
// No ES6 syntax sugar apparently:
// /sf/ask/1577027721/
C.c = 1
C.c === 1
// Because `D.__proto__ === C`.
D.c === 1
// Nothing makes this work.
d.c === undefined
Run Code Online (Sandbox Code Playgroud)

没有所有预定义对象的简化图:

      __proto__
(C)<---------------(D)         (d)
| |                |           |
| |                |           |
| |prototype       |prototype  |__proto__
| |                |           |
| |                |           |
| |                | +---------+
| |                | |
| |                | |
| |                v v
|__proto__        (D.prototype)
| |                |
| |                |
| |                |__proto__
| |                |
| |                |
| | +--------------+
| | |
| | |
| v v
| (C.prototype)--->(inc)
|
v
Function.prototype
Run Code Online (Sandbox Code Playgroud)