JS实现“扩展”功能(真正的简单继承)

时间:2013-07-17 05:09:28

标签: javascript

说我在JS中有一个原型函数的classe ......

function Foo() {
    this.stuff = 7;
    this.otherStuff = 5;
}

Foo.prototype.doSomething = function() { };

Foo.prototype.doSomethingElse = function() { };

现在说我想通过继承它来“扩展”这个类。在Java中,这看起来像......

public class Bar extends Foo {}

现在我知道在JS中确实没有类的概念,一切都可以改变,而且这一切都只是归结为一大堆字典但是,我应该能够复制一个类的原型并将其附加到另一个原型上,对吧?

类似于vanilla JS的代码会是什么样的?

5 个答案:

答案 0 :(得分:4)

其中一种方式如下,

function Foo() {
    this.stuff = 7;
    this.otherStuff = 5;
}
Foo.prototype.doSomething = function() { alert("some"); };

Foo.prototype.doSomethingElse = function() { };

function Bar() {
   Foo.call(this); // this line
}
Bar.prototype = Object.create(Foo.prototype);

var b = new Bar();
b.doSomething(); 

答案 1 :(得分:3)

像这样......

function Bar(){
    // your code
}

Bar.prototype = new Foo(); // Bar extends Foo

答案 2 :(得分:2)

接受的答案绝对可以解决问题。而这个“简短”的解释变成了漫无边际,但希望它有所帮助。

有一件事你需要了解接受的答案。当基本上通过执行Bar.prototype = new Foo()“继承”时,你正在调用构造函数。因此,如果您的构造函数中的代码不希望用作另一个“类”的启动板,那么您将会遇到奇怪的效果。举个例子:

var Person = function (firstName, lastName) {
    ....
};

var Student = function (firstName, lastName, grade) {
    ....
};

假设StudentPerson的扩展名。您如何在Student上构建原型?可能不像Student.prototype = new Person("John", "Doe");

一种不同的处理方式,有点复杂但可以包含在另一个函数内部如下:

var extend = function (child, parent) {
    var f = function () {};
    f.prototype = parent.prototype;
    child.prototype = new f();
    child.prototype.constructor = parent;
}

var Person = function (firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
}
Person.prototype.getName = function () {
    return this.firstName + " " + this.lastName;
}
Person.prototype.setAge = function (age) {
    this.age = age;
}


var Student = function (firstName, lastName, grade) {
    Person.call(this, firstName, lastName);
    this.grade = grade;
};

extend(Student, Person); //<< do this before adding stuff to the prototype

Student.prototype.getName = function () {
    var name = Person.prototype.getName.apply(this);
    return name + ", Grade " + this.grade;
}
Student.prototype.tooOldToBeAStudent = function () {
    if(this.age > 18) { return true; }
}

var p = new Person("Joe", "DiMaggio");
var s = new Student("Jack", "Sparrow", 12);
console.log(p.getName(), s.getName());
//consoles "Joe DiMaggio" "Jack Sparrow, Grade 12"
console.log(s instanceof Person); 
//true - even with the weird looking inheritance, Student is still an instance of Person.
s.setAge(30);
console.log(s.age);
//30, just like what you'd expect with a Person object.
console.log(s.tooOldToBeAStudent);
//true - as mentioned previously, 'age' is set via its inherited method.

这不仅可以让您获得Person功能,还可以让您构建。有点像你实际继承的那些。

它是如何工作的?好问题。首先,通过引用[基本]分配对象。原型是一个对象。因此,在extend函数中,我创建了一个空白函数,它将作为child的代理。这会将parent的原型复制到自身,然后将自己的新实例作为child的原型。这样就不会调用parent的构造函数,但仍然使用父代的原型。为了确保instanceof仍然有效,child.prototype.constructor设置为parent - 有效地通知javascript孩子来自的是父母,而不是来自代理人。

此外,在覆盖方法时,您可以使用从原型方法继承的“类”和applycallthis一起使用 - 运行方法的范围为当前对象,您可以传递任何您想要的参数,但是您选择的函数会接受它们。例如,Person.prototype.getName.apply(this);在学生当前实例的上下文中运行Person getName。假设我们想要将setAge重写为简单的console.log年龄。

Student.prototype.setAge = function () {
    Person.prototype.setAge.apply(this, arguments);
    console.log(this.age);
}

这里发生的事情是使用传递给Person的{​​{1}}的参数调用setAge的{​​{1}}。所以,它基本上允许东西通过,而你不必知道原始方法的参数的细节。

答案 3 :(得分:2)

mohkhan提供的解决方案已过时。继承的新方法是:

function Bar() {
    Foo.call(this, /* additional arguments if required */);
}

Bar.prototype = Object.create(Foo.prototype);
Bar.prototype.constructor = Bar;              // don't forget to do this

如果您想使代码看起来更经典,请查看augment方法。使用扩充,您可以将上面的代码编写为:

var Bar = Foo.augment(function (base) {
    this.constructor = function () {
        base.constructor.call(this, /* additional arguments if required */);
    };
});

augment方法本身只有七行代码:

Function.prototype.augment = function (body) {
    var base = this.prototype;
    var prototype = Object.create(base);
    body.apply(prototype, Array.prototype.slice.call(arguments, 1).concat(base));
    if (!Object.hasOwnProperty.call(prototype, "constructor")) return prototype;
    var constructor = prototype.constructor;
    constructor.prototype = prototype;
    return constructor;
};

说真的,用新方法。这样更好。原因如下:Javascript inheritance: calling Object.create when setting a prototype

答案 4 :(得分:-3)

javascript中扩展类的模式与java不同

var Person = Class.extend({
  init: function(isDancing){
    this.dancing = isDancing;
  },
  dance: function(){
    return this.dancing;
  }
});

var Ninja = Person.extend({
  init: function(){
    this._super( false );
  },
  dance: function(){
    // Call the inherited version of dance()
    return this._super();
  },
  swingSword: function(){
    return true;
  }
});

var p = new Person(true);
p.dance(); // => true

var n = new Ninja();
n.dance(); // => false
n.swingSword(); // => true

// Should all be true
p instanceof Person && p instanceof Class &&
n instanceof Ninja && n instanceof Person && n instanceof Class