JavaScript 继承

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/7486825/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 02:28:22  来源:igfitidea点击:

JavaScript Inheritance

javascriptinheritance

提问by hungryMind

I am trying to implement inheritance in javascript. I came up with following minimal code to support it.

我正在尝试在 javascript 中实现继承。我想出了以下最少的代码来支持它。

function Base(){
    this.call = function(handler, args){
      handler.call(this, args);
    }
}

Base.extend = function(child, parent){
    parent.apply(child);
    child.base = new parent;
    child.base.child = child;
}

Experts, please let me know if this will be sufficient or any other important issue I may have missed. Based on similar issues faced please suggest other changes.

专家,请让我知道这是否足够或我可能遗漏了任何其他重要问题。根据面临的类似问题,请提出其他更改建议。

Here is complete test script:

这是完整的测试脚本:

function Base(){
    this.call = function(handler, args){
      handler.call(this, args);
    }
    this.superalert = function(){
        alert('tst');
    }
}

Base.extend = function(child, parent){
    parent.apply(child);
    child.base = new parent;
    child.base.child = child;
}

function Child(){
    Base.extend(this, Base);
    this.width = 20;
    this.height = 15;
    this.a = ['s',''];
    this.alert = function(){
        alert(this.a.length);
        alert(this.height);
    }
}

function Child1(){
    Base.extend(this, Child);
    this.depth = 'depth';
    this.height = 'h';
    this.alert = function(){
        alert(this.height); // display current object height
        alert(this.a.length); // display parents array length
        this.call(this.base.alert); 
          // explicit call to parent alert with current objects value
        this.call(this.base.superalert); 
          // explicit call to grandparent, parent does not have method 
        this.base.alert(); // call parent without overriding values
    }
}

var v = new Child1();
v.alert();
alert(v.height);
alert(v.depth);

回答by Lorenzo Polidori

To implement javascript inheritance in ECMAScript 5you can define the prototype of an object and use Object.createto inherit. You can also add/override properties as much as you want.

要在ECMAScript 5 中实现 javascript 继承,您可以定义对象的原型并用于Object.create继承。您还可以根据需要添加/覆盖属性。

Example:

例子:

/**
 * Transform base class
 */
function Transform() {
    this.type = "2d";
}

Transform.prototype.toString = function() {
    return "Transform";
}

/**
 * Translation class.
 */
function Translation(x, y) {
    // Parent constructor
    Transform.call(this);

    // Public properties
    this.x = x;
    this.y = y;
}

// Inheritance
Translation.prototype = Object.create(Transform.prototype);

// Override
Translation.prototype.toString = function() {
    return Transform.prototype.toString() + this.type + " Translation " + this.x + ":" + this.y;
}

/**
 * Rotation class.
 */
function Rotation(angle) {
    // Parent constructor
    Transform.call(this);

    // Public properties
    this.angle = angle;
}

// Inheritance
Rotation.prototype = Object.create(Transform.prototype);

// Override
Rotation.prototype.toString = function() {
    return Transform.prototype.toString() + this.type + " Rotation " + this.angle;
}

// Tests
translation = new Translation(10, 15);

console.log(translation instanceof Transform); // true
console.log(translation instanceof Translation); // true
console.log(translation instanceof Rotation); // false

console.log(translation.toString()) // Transform2d Translation 10:15

回答by Marcosc

I think Crockfords solution is too complicated, as is John's. It's much simpler to get javascript inheritance than both of them seem to describe. Consider:

我认为 Crockfords 的解决方案太复杂了,John 的也是如此。获得 javascript 继承比它们似乎描述的要简单得多。考虑:

//Classes
function A() {
    B.call(this);
}

function B() {
    C.call(this);
    this.bbb = function() {
        console.log("i was inherited from b!");
    }
}

function C() {
    D.call(this);
}

function D() {
    E.call(this);
}

function E() {
    //instance property 
    this.id = Math.random()
}

//set up the inheritance chain (order matters) 
D.prototype = new E();
C.prototype = new D();
B.prototype = new C();
A.prototype = new B();

//Add custom functions to each
A.prototype.foo = function() {
    console.log("a");
};
B.prototype.bar = function() {
    console.log("b");
};
C.prototype.baz = function() {
    console.log("c");
};
D.prototype.wee = function() {
    console.log("d");
};
E.prototype.woo = function() {
    console.log("e");
};

//Some tests
a = new A();
a.foo();
a.bar();
a.baz();
a.wee();
a.woo();
console.log(a.id);
a.bbb();
console.log(a instanceof A);
console.log(a instanceof B);
console.log(a instanceof C);
console.log(a instanceof D);
console.log(a instanceof E);?
var b = new B();
console.log(b.id)

I've written a complete description of the above solution on my blog.

我在我的博客上写了对上述解决方案的完整描述。

回答by Jan Turoň

As I played with JS objects, I found a more minimalistic solution :-) Enjoy!

当我使用 JS 对象时,我发现了一个更简约的解决方案 :-) 享受吧!

function extend(b,a,t,p) { b.prototype = a; a.apply(t,p); }

Example

例子

function A() {
    this.info1 = function() {
        alert("A");
    }
}

function B(p1,p2) {
    extend(B,A,this);
    this.info2 = function() {
        alert("B"+p1+p2);
    }
}

function C(p) {
    extend(C,B,this,["1","2"]);
    this.info3 = function() {
        alert("C"+p);
    }
}


var c = new C("c");
c.info1(); // A
c.info2(); // B12
c.info3(); // Cc

回答by Alexander Serkin

Here is the simplest and I hope the easiest way to understand inheritance in JS. Most helpful this example will be for PHP programmers.

这是最简单的,我希望是理解 JS 继承的最简单方法。这个例子对 PHP 程序员最有帮助。

function Mother(){
    this.canSwim = function(){
        console.log('yes');
    }
}

function Son(){};
Son.prototype = new Mother;
Son.prototype.canRun = function(){
    console.log('yes');
}

Now the son has one overridden method and one new

现在儿子有一个被覆盖的方法和一个新的

function Grandson(){}
Grandson.prototype = new Son;
Grandson.prototype.canPlayPiano = function(){
    console.log('yes');
};
Grandson.prototype.canSwim = function(){
    console.log('no');
}

Now the grandson has two overridden methods and one new

现在孙子有两个被覆盖的方法和一个新的

var g = new Grandson;
g.canRun(); // => yes
g.canPlayPiano(); // => yes
g.canSwim(); // => no

回答by Luka Bla?ecki

Why not use objects instead of functions :

为什么不使用对象而不是函数:

var Base = {
    superalert : function() {
        alert('tst');
    }
};

var Child = Object.create(Base);
Child.width = 20;
Child.height = 15;
Child.a = ['s',''];
Child.childAlert = function () {
        alert(this.a.length);
        alert(this.height);
    }

var Child1 = Object.create(Child);
Child1.depth = 'depth';
Child1.height = 'h';
Child1.alert = function () {
    alert(this.height);
    alert(this.a.length);
    this.childAlert();
    this.superalert();
};

And call it like this :

并这样称呼它:

var child1 = Object.create(Child1);
child1.alert();

This approach is much more cleaner then with functions. I found this blog explaining why inheritance with functions isn't a proper way to do it in JS : http://davidwalsh.name/javascript-objects-deconstruction

这种方法比函数更简洁。我发现这个博客解释了为什么在 JS 中使用函数继承不是正确的方法:http: //davidwalsh.name/javascript-objects-deconstruction

EDIT

编辑

var Child can also be written as :

var Child 也可以写成:

var Child = Object.create(Base, {
    width : {value : 20},
    height  : {value : 15, writable: true},
    a : {value : ['s', ''], writable: true},
    childAlert : {value : function () {
        alert(this.a.length);
        alert(this.height);
    }}
});

回答by Ajedi32

Here's my solution, which is based on the standard prototypical inheritance method described in Lorenzo Polidori'sanswer.

这是我的解决方案,它基于Lorenzo Polidori 的回答中描述的标准原型继承方法。

First, I start off by defining these helper methods, which make things easier to understand and more readable later on:

首先,我首先定义了这些辅助方法,它们使之后的事情更容易理解和更易读:

Function.prototype.setSuperclass = function(target) {
    // Set a custom field for keeping track of the object's 'superclass'.
    this._superclass = target;

    // Set the internal [[Prototype]] of instances of this object to a new object
    // which inherits from the superclass's prototype.
    this.prototype = Object.create(this._superclass.prototype);

    // Correct the constructor attribute of this class's prototype
    this.prototype.constructor = this;
};

Function.prototype.getSuperclass = function(target) {
    // Easy way of finding out what a class inherits from
    return this._superclass;
};

Function.prototype.callSuper = function(target, methodName, args) {
    // If methodName is ommitted, call the constructor.
    if (arguments.length < 3) {
        return this.callSuperConstructor(arguments[0], arguments[1]);
    }

    // `args` is an empty array by default.
    if (args === undefined || args === null) args = [];

    var superclass = this.getSuperclass();
    if (superclass === undefined) throw new TypeError("A superclass for " + this + " could not be found.");

    var method = superclass.prototype[methodName];
    if (typeof method != "function") throw new TypeError("TypeError: Object " + superclass.prototype + " has no method '" + methodName + "'");

    return method.apply(target, args);
};

Function.prototype.callSuperConstructor = function(target, args) {
    if (args === undefined || args === null) args = [];

    var superclass = this.getSuperclass();
    if (superclass === undefined) throw new TypeError("A superclass for " + this + " could not be found.");

    return superclass.apply(target, args);
};

Now, not only can you set the superclass of a class with SubClass.setSuperclass(ParentClass), but you can also call overridden methods with SubClass.callSuper(this, 'functionName', [argument1, argument2...]):

现在,您不仅可以使用 设置类的超类SubClass.setSuperclass(ParentClass),还可以使用 调用覆盖的方法SubClass.callSuper(this, 'functionName', [argument1, argument2...])

/**
 * Transform base class
 */
function Transform() {
    this.type = "2d";
}

Transform.prototype.toString = function() {
    return "Transform";
}

/**
 * Translation class.
 */
function Translation(x, y) {
    // Parent constructor
    Translation.callSuper(this, arguments);

    // Public properties
    this.x = x;
    this.y = y;
}
// Inheritance
Translation.setSuperclass(Transform);

// Override
Translation.prototype.toString = function() {
    return Translation.callSuper(this, 'toString', arguments) + this.type + " Translation " + this.x + ":" + this.y;
}

/**
 * Rotation class.
 */
function Rotation(angle) {
    // Parent constructor
    Rotation.callSuper(this, arguments);

    // Public properties
    this.angle = angle;
}
// Inheritance
Rotation.setSuperclass(Transform);

// Override
Rotation.prototype.toString = function() {
    return Rotation.callSuper(this, 'toString', arguments) + this.type + " Rotation " + this.angle;
}

// Tests
translation = new Translation(10, 15);

console.log(translation instanceof Transform); // true
console.log(translation instanceof Translation); // true
console.log(translation instanceof Rotation); // false

console.log(translation.toString()) // Transform2d Translation 10:15

Admittedly, even with the helper functions the syntax here is pretty awkward. Thankfully though, in ECMAScript 6 some syntactic sugar (maximally minimal classes) has been added to make things much prettier. E.g.:

诚然,即使有辅助函数,这里的语法也很笨拙。值得庆幸的是,在 ECMAScript 6 中添加了一些语法糖(最大限度地减少类)以使事情变得更漂亮。例如:

/**
 * Transform base class
 */
class Transform {
  constructor() {
    this.type = "2d";
  }

  toString() {
    return "Transform";
  } 
}

/**
 * Translation class.
 */
class Translation extends Transform {
  constructor(x, y) {
    super(); // Parent constructor

    // Public properties
    this.x = x;
    this.y = y;
  }

  toString() {
    return super(...arguments) + this.type + " Translation " + this.x + ":" + this.y;
  }
}

/**
 * Rotation class.
 */
class Rotation extends Transform {
  constructor(angle) {
    // Parent constructor
    super(...arguments);

    // Public properties
    this.angle = angle;
  }

  toString() {
    return super(...arguments) + this.type + " Rotation " + this.angle;
  }
}

// Tests
translation = new Translation(10, 15);

console.log(translation instanceof Transform); // true
console.log(translation instanceof Translation); // true
console.log(translation instanceof Rotation); // false

console.log(translation.toString()) // Transform2d Translation 10:15

Note that ECMAScript 6 is still in the draft stage at this point, and as far as I know is not implemented in any major web browser. However, if you wish you can use something like Traceur compilerto compile ECMAScript 6down to the plain old ECMAScript 5-based JavaScript. You can see the above example compiled using Traceur here.

请注意,此时 ECMAScript 6 仍处于草案阶段,据我所知,还没有在任何主流 Web 浏览器中实现。然而,如果你愿意,你可以使用类似Traceur 编译器的东西来编译ECMAScript 6成普通的ECMAScript 5基于旧的 JavaScript。您可以在此处查看使用 Traceur 编译的上述示例。

回答by Anand

While I agree with all above answers, I feel that JavaScript need not be Object Oriented, (Avoid inheritance), instead an object-based approachshould be sufficient in most cases.

虽然我同意上述所有答案,但我觉得 JavaScript 不需要面向对象(避免继承),而是在大多数情况下基于对象的方法就足够了。

I like the way Eloquent JavaScriptstarts its Chapter 8on Object Oriented Programming talking about OO. Instead of deciphering best way to implement Inheritance, more energy should be devoted to learn functional aspects of JavaScript, hence, I found Chapter 6on Functional Programming, more interesting.

我喜欢Eloquent JavaScript第 8 章面向对象编程开始谈论 OO 的方式。与其解释实现继承的最佳方式,不如将更多的精力花在学习 JavaScript 的函数方面,因此,我发现第 6 章函数式编程更有趣。

回答by Lars Christensen

//This is an example of how to override a method, while preserving access to the original.
//The pattern used is actually quite simple using JavaScripts ability to define closures:

    this.somefunction = this.someFunction.override(function(args){
        var result = this.inherited(args);
        result += this.doSomethingElse();
        return result;
    });

//It is accomplished through this piece of code (courtesy of Poul Krogh):

/***************************************************************
    function.override overrides a defined method with a new one, 
    while preserving the old method.
    The old method is only accessible from the new one.
    Use this.inherited() to access the old method.
***************************************************************/

    Function.prototype.override = function(func)
    {
        var remember = this;
        var f = function() 
        {
             var save = this.inherited; 
             this.inherited = remember;
             var result = func.apply(this, Array.prototype.slice.call(arguments));
             this.inherited = save;
             return result;
        };
        return f;
    }

回答by John Slegers

Basic prototypical inheritance

基本原型继承

A simple but effective way to do inheritance in JavaScript, is to use the following two-liner :

在 JavaScript 中进行继承的一种简单而有效的方法是使用以下两行代码:

B.prototype = Object.create(A.prototype);
B.prototype.constructor = B;

That is similar to doing this :

这类似于这样做:

B.prototype = new A();

The main difference between both is that the constructor of Ais not run when using Object.create, which is more intuitive and more similar to class based inheritance.

两者的主要区别在于Ausing 时不运行的构造函数,Object.create更直观,更类似于基于类的继承。

You can always choose to optionally run the constructor of Awhen creating a new instance of Bby adding adding it to the constructor of B:

您始终可以选择A在创建 的新实例时选择运行 的构造函数,方法B是将其添加到 的构造函数中B

function B(arg1, arg2) {
    A(arg1, arg2); // This is optional
}

If you want to pass all arguments of Bto A, you can also use Function.prototype.apply():

如果你想传递的所有参数BA,你也可以使用Function.prototype.apply()

function B() {
    A.apply(this, arguments); // This is optional
}

If you want to mixin another object into the constructor chain of B, you can combine Object.createwith Object.assign:

如果要将另一个对象混合到 的构造函数链中B,可以结合Object.create使用Object.assign

B.prototype = Object.assign(Object.create(A.prototype), mixin.prototype);
B.prototype.constructor = B;

Demo

演示

function A(name) {
  this.name = name;
}

A.prototype = Object.create(Object.prototype);
A.prototype.constructor = A;

function B() {
  A.apply(this, arguments);
  this.street = "Downing Street 10";
}

B.prototype = Object.create(A.prototype);
B.prototype.constructor = B;

function mixin() {

}

mixin.prototype = Object.create(Object.prototype);
mixin.prototype.constructor = mixin;

mixin.prototype.getProperties = function() {
  return {
    name: this.name,
    address: this.street,
    year: this.year
  };
};

function C() {
  B.apply(this, arguments);
  this.year = "2018"
}

C.prototype = Object.assign(Object.create(B.prototype), mixin.prototype);
C.prototype.constructor = C;

var instance = new C("Frank");
console.log(instance);
console.log(instance.getProperties());



Creating your own wrapper

创建自己的包装器

If you don't like writing roughly the same two-liner throughout your code, you could write a basic wrapper function like this :

如果您不喜欢在整个代码中编写大致相同的两行代码,您可以编写一个基本的包装函数,如下所示:

function inheritance() {
  var args = Array.prototype.slice.call(arguments);
  var firstArg = args.shift();
  switch (args.length) {
  case 0:
    firstArg.prototype = Object.create(Object.prototype);
    firstArg.prototype.constructor = firstArg;
    break;
  case 1:
    firstArg.prototype = Object.create(args[0].prototype);
    firstArg.prototype.constructor = firstArg;
    break;
  default:
    for(var i = 0; i < args.length; i++) {
      args[i] = args[i].prototype;
    }
    args[0] = Object.create(args[0]);
    var secondArg = args.shift();
    firstArg.prototype = Object.assign.apply(Object, args);
    firstArg.prototype.constructor = firstArg;
  }
}

How this wrapper works :

这个包装器是如何工作的:

  1. If you pass a one parameter, it's prototype will inherit from Object.
  2. If you pass two parameters, the first's prototype will inherit from the second's.
  3. If you pass more than two parameters, the first's prototype will inherit from the second's and the prototypes of other parameters will be mixed in.
  1. 如果您传递一个参数,它的原型将继承自Object.
  2. 如果传递两个参数,第一个的原型将从第二个继承。
  3. 如果传递两个以上的参数,第一个的原型将从第二个的原型继承,其他参数的原型将混入。

Demo

演示

function inheritance() {
  var args = Array.prototype.slice.call(arguments);
  var firstArg = args.shift();
  switch (args.length) {
  case 0:
    firstArg.prototype = Object.create(Object.prototype);
    firstArg.prototype.constructor = firstArg;
    break;
  case 1:
    firstArg.prototype = Object.create(args[0].prototype);
    firstArg.prototype.constructor = firstArg;
    break;
  default:
    for(var i = 0; i < args.length; i++) {
      args[i] = args[i].prototype;
    }
    args[0] = Object.create(args[0]);
    var secondArg = args.shift();
    firstArg.prototype = Object.assign.apply(Object, args);
    firstArg.prototype.constructor = firstArg;
  }
}

function A(name) {
  this.name = name;
}

inheritance(A);

function B() {
  A.apply(this, arguments);
  this.street = "Downing Street 10";
}

inheritance(B, A);

function mixin() {

}

inheritance(mixin);

mixin.prototype.getProperties = function() {
  return {
    name: this.name,
    address: this.street,
    year: this.year
  };
};

function C() {
  B.apply(this, arguments);
  this.year = "2018"
}

inheritance(C, B, mixin);

var instance = new C("Frank");
console.log(instance);
console.log(instance.getProperties());



Note

笔记

Object.createcan be safely used in every modern browser, including IE9+. Object.assigndoes not work in any version of IE nor some mobile browsers. It is recommended to polyfillObject.createand/or Object.assignif you want to use them and support browsers that do not implement them.

Object.create可以在所有现代浏览器中安全使用,包括 IE9+。Object.assign不适用于任何版本的 IE 或某些移动浏览器。建议使用polyfillObject.create和/或Object.assign如果您想使用它们并支持未实现它们的浏览器。

You can find a polyfill for Object.createhereand one for Object.assignhere.

你可以找到一个填充工具对Object.create这里和一个Object.assign在这里

回答by CodeMilian

How about this simple approach

这个简单的方法怎么样

    function Body(){
        this.Eyes = 2;
        this.Arms = 2;
        this.Legs = 2;
        this.Heart = 1;
        this.Walk = function(){alert(this.FirstName + ' Is Walking')};
    }

    function BasePerson() {
        var BaseBody = new Body(this);
        BaseBody.FirstName = '';
        BaseBody.LastName = '';
        BaseBody.Email = '';
        BaseBody.IntroduceSelf = function () { alert('Hello my name is ' + this.FirstName + ' ' + this.LastName); };
        return BaseBody;
    }

    function Person(FirstName,LastName)
    {
        var PersonBuild = new BasePerson();
        PersonBuild.FirstName = FirstName;
        PersonBuild.LastName = LastName;
        return PersonBuild;
    }

    var Person1 = new Person('Code', 'Master');
    Person1.IntroduceSelf();
    Person1.Walk();