JavaScript 扩展类
Posted
技术标签:
【中文标题】JavaScript 扩展类【英文标题】:JavaScript Extending Class 【发布时间】:2013-02-18 00:28:10 【问题描述】:我有一个基类:
function Monster()
this.health = 100;
Monster.prototype.growl = function()
console.log("Grr!");
我想扩展并创建另一个类:
function Monkey extends Monster()
this.bananaCount = 5;
Monkey.prototype.eatBanana
this.bananaCount--;
this.health++; //Accessing variable from parent class monster
this.growl(); //Accessing function from parent class monster
我已经进行了大量研究,似乎有许多复杂的解决方案可以在 javascript 中执行此操作。在 JS 中实现这一点的最简单和最可靠的方法是什么?
【问题讨论】:
How to inherit from a class in javascript?的可能重复 【参考方案1】:下面为 ES6 更新
2013 年 3 月和 ES5
这个 MDN 文档很好地描述了扩展类:
https://developer.mozilla.org/en-US/docs/JavaScript/Introduction_to_Object-Oriented_JavaScript
特别是,现在他们处理它:
// define the Person Class
function Person()
Person.prototype.walk = function()
alert ('I am walking!');
;
Person.prototype.sayHello = function()
alert ('hello');
;
// define the Student class
function Student()
// Call the parent constructor
Person.call(this);
// inherit Person
Student.prototype = Object.create(Person.prototype);
// correct the constructor pointer because it points to Person
Student.prototype.constructor = Student;
// replace the sayHello method
Student.prototype.sayHello = function()
alert('hi, I am a student');
// add sayGoodBye method
Student.prototype.sayGoodBye = function()
alert('goodBye');
var student1 = new Student();
student1.sayHello();
student1.walk();
student1.sayGoodBye();
// check inheritance
alert(student1 instanceof Person); // true
alert(student1 instanceof Student); // true
注意Object.create()
在一些旧版浏览器中不受支持,包括 IE8:
如果您需要支持这些,链接的 MDN 文档建议使用 polyfill 或以下近似值:
function createObject(proto)
function ctor()
ctor.prototype = proto;
return new ctor();
像Student.prototype = createObject(Person.prototype)
这样使用new Person()
更可取,因为它在继承原型时avoids calling the parent's constructor function,并且仅在调用继承者的构造函数时才调用父构造函数。
2017 年 5 月和 ES6
谢天谢地,JavaScript 设计人员听到了我们的求助请求,并采用了更合适的方式来解决此问题。
MDN 有另一个很好的 ES6 类继承示例,但我将展示与上面在 ES6 中复制的完全相同的类集:
class Person
sayHello()
alert('hello');
walk()
alert('I am walking!');
class Student extends Person
sayGoodBye()
alert('goodBye');
sayHello()
alert('hi, I am a student');
var student1 = new Student();
student1.sayHello();
student1.walk();
student1.sayGoodBye();
// check inheritance
alert(student1 instanceof Person); // true
alert(student1 instanceof Student); // true
干净易懂,就像我们都想要的那样。请记住,虽然 ES6 很常见,但它是 not supported everywhere:
【讨论】:
如果 Person 构造函数需要参数,例如 Person(name) ... 怎么办? @PhamHuyAnh 做类似function Person(n) this.name = n;
的事情
Student.prototype = new Person();
如果我访问超类中的参数,此行会导致错误。
Student.prototype = Object.create(Person.prototype);
经典的创建方法在这里工作
我正在尝试创建扩展功能,它可以自己完成所有工作。是否有可能以某种方式移动“Person.call(this);”或来自“Monster.apply(this, arguments);”下面的示例到这样的功能?我在这样做时遇到了问题。【参考方案2】:
ES6 现在让您有机会使用 class 和 extends 关键字:
然后,您的代码将是:
你有一个基类:
class Monster
constructor()
this.health = 100;
growl()
console.log("Grr!");
你想扩展和创建另一个类:
class Monkey extends Monster
constructor()
super(); //don't forget "super"
this.bananaCount = 5;
eatBanana()
this.bananaCount--;
this.health++; //Accessing variable from parent class monster
this.growl(); //Accessing function from parent class monster
【讨论】:
这样干净多了,可以确认在 chrome 51.0, Firefox 47 下工作了。 使用trycatch(e)
块来管理它,并告诉最终用户在需要时更新他的浏览器。【参考方案3】:
试试这个:
Function.prototype.extends = function(parent)
this.prototype = Object.create(parent.prototype);
;
Monkey.extends(Monster);
function Monkey()
Monster.apply(this, arguments); // call super
编辑:我在这里放了一个快速演示 http://jsbin.com/anekew/1/edit。注意extends
是JS中的保留字,在linting你的代码时可能会收到警告,你可以简单地把它命名为inherits
,这是我通常做的。
有了这个助手并使用对象props
作为唯一参数,JS中的继承变得更简单了:
Function.prototype.inherits = function(parent)
this.prototype = Object.create(parent.prototype);
;
function Monster(props)
this.health = props.health || 100;
Monster.prototype =
growl: function()
return 'Grrrrr';
;
Monkey.inherits(Monster);
function Monkey()
Monster.apply(this, arguments);
var monkey = new Monkey( health: 200 );
console.log(monkey.health); //=> 200
console.log(monkey.growl()); //=> "Grrrr"
【讨论】:
使用这个,Monkey
不会继承 Monster
的属性 (health
)。如果在调用 Monkey.extends(Monster)
之前未定义 Monkey
,您还将得到 "ReferenceError: Monkey is not defined"
@Phil,这是一个函数声明,它被提升了,它应该可以工作。您将从该代码中得到的唯一“问题”是“扩展是保留字”,但您可以轻松地将其更改为任何其他标识符。
谢谢哥们。这很棒。可以在 Node.js 中应用它来为构造函数等创建基类,因此我不必每次创建构造类时都创建 mongo 连接等【参考方案4】:
如果您不喜欢原型方法,因为它实际上并不能以良好的 OOP 方式运行,您可以试试这个:
var BaseClass = function()
this.some_var = "foobar";
/**
* @return string
*/
this.someMethod = function()
return this.some_var;
;
var MyClass = new Class( extends: BaseClass , function()
/**
* @param string value
*/
this.__construct = function(value)
this.some_var = value;
)
使用轻量级库(2k 缩小):https://github.com/haroldiedema/joii
【讨论】:
感谢您提供此库的链接!它看起来很棒:D 似乎这就是我一直在寻找的东西! 【参考方案5】:我可以提出一种变体,只是在书上看过,似乎最简单:
function Parent()
this.name = 'default name';
;
function Child()
this.address = '11 street';
;
Child.prototype = new Parent(); // child class inherits from Parent
Child.prototype.constructor = Child; // constructor alignment
var a = new Child();
console.log(a.name); // "default name" trying to reach property of inherited class
【讨论】:
【参考方案6】:这是 elclanrs 解决方案的扩展(请原谅双关语),包括实例方法的详细信息,以及对问题的这方面采取可扩展的方法;我完全承认,这要归功于 David Flanagan 的“JavaScript:权威指南”(针对此上下文进行了部分调整)。请注意,这显然比其他解决方案更冗长,但从长远来看可能会受益。
首先我们使用 David 的简单“扩展”函数,它将属性复制到指定对象:
function extend(o,p)
for (var prop in p)
o[prop] = p[prop];
return o;
然后我们实现他的子类定义工具:
function defineSubclass(superclass, // Constructor of our superclass
constructor, // Constructor of our new subclass
methods, // Instance methods
statics) // Class properties
// Set up the prototype object of the subclass
constructor.prototype = Object.create(superclass.prototype);
constructor.prototype.constructor = constructor;
if (methods) extend(constructor.prototype, methods);
if (statics) extend(constructor, statics);
return constructor;
在最后的准备工作中,我们使用 David 的新 jiggery-pokery 来增强我们的 Function 原型:
Function.prototype.extend = function(constructor, methods, statics)
return defineSubclass(this, constructor, methods, statics);
;
在定义了我们的 Monster 类之后,我们执行以下操作(可重复用于我们想要扩展/继承的任何新类):
var Monkey = Monster.extend(
// constructor
function Monkey()
this.bananaCount = 5;
Monster.apply(this, arguments); // Superclass()
,
// methods added to prototype
eatBanana: function ()
this.bananaCount--;
this.health++;
this.growl();
);
【讨论】:
【参考方案7】:对于传统的扩展,您可以简单地将超类编写为构造函数, 然后将此构造函数应用于您继承的类。
function AbstractClass()
this.superclass_method = function(message)
// do something
;
function Child()
AbstractClass.apply(this);
// Now Child will have superclass_method()
angularjs 示例:
http://plnkr.co/edit/eFixlsgF3nJ1LeWUJKsd?p=preview
app.service('noisyThing',
['notify',function(notify)
this._constructor = function()
this.scream = function(message)
message = message + " by " + this.get_mouth();
notify(message);
console.log(message);
;
this.get_mouth = function()
return 'abstract mouth';
])
.service('cat',
['noisyThing', function(noisyThing)
noisyThing._constructor.apply(this)
this.meow = function()
this.scream('meooooow');
this.get_mouth = function()
return 'fluffy mouth';
])
.service('bird',
['noisyThing', function(noisyThing)
noisyThing._constructor.apply(this)
this.twit = function()
this.scream('fuuuuuuck');
])
【讨论】:
【参考方案8】:对于自学者:
function BaseClass(toBePrivate)
var morePrivates;
this.isNotPrivate = 'I know';
// add your stuff
var o = BaseClass.prototype;
// add your prototype stuff
o.stuff_is_never_private = 'whatever_except_getter_and_setter';
// MiddleClass extends BaseClass
function MiddleClass(toBePrivate)
BaseClass.call(this);
// add your stuff
var morePrivates;
this.isNotPrivate = 'I know';
var o = MiddleClass.prototype = Object.create(BaseClass.prototype);
MiddleClass.prototype.constructor = MiddleClass;
// add your prototype stuff
o.stuff_is_never_private = 'whatever_except_getter_and_setter';
// TopClass extends MiddleClass
function TopClass(toBePrivate)
MiddleClass.call(this);
// add your stuff
var morePrivates;
this.isNotPrivate = 'I know';
var o = TopClass.prototype = Object.create(MiddleClass.prototype);
TopClass.prototype.constructor = TopClass;
// add your prototype stuff
o.stuff_is_never_private = 'whatever_except_getter_and_setter';
// to be continued...
使用 getter 和 setter 创建“实例”:
function doNotExtendMe(toBePrivate)
var morePrivates;
return
// add getters, setters and any stuff you want
【讨论】:
【参考方案9】:总结:
有多种方法可以解决在 Javascript 中使用原型扩展构造函数的问题。这些方法中的哪一个是“最佳”解决方案是基于意见的。但是,这里有两种常用的方法来扩展构造函数的函数原型。
ES 2015 课程:
class Monster
constructor(health)
this.health = health
growl ()
console.log("Grr!");
class Monkey extends Monster
constructor (health)
super(health) // call super to execute the constructor function of Monster
this.bananaCount = 5;
const monkey = new Monkey(50);
console.log(typeof Monster);
console.log(monkey);
上述使用ES 2015
类的方法只不过是javascript 中原型继承模式的语法糖。在我们评估typeof Monster
的第一个日志中,我们可以观察到这是函数。这是因为类只是底层的构造函数。尽管如此,您可能喜欢这种实现原型继承的方式,并且绝对应该学习它。在ReactJS
和Angular2+
等主要框架中都有使用。
使用Object.create()
的工厂函数:
function makeMonkey (bananaCount)
// here we define the prototype
const Monster =
health: 100,
growl: function()
console.log("Grr!");
const monkey = Object.create(Monster);
monkey.bananaCount = bananaCount;
return monkey;
const chimp = makeMonkey(30);
chimp.growl();
console.log(chimp.bananaCount);
此方法使用Object.create()
方法,该方法接受一个对象,该对象将是它返回的新创建对象的原型。因此,我们首先在这个函数中创建原型对象,然后调用Object.create()
,它返回一个空对象,其中__proto__
属性设置为Monster 对象。在此之后我们可以初始化对象的所有属性,在这个例子中我们将香蕉计数分配给新创建的对象。
【讨论】:
【参考方案10】:绝对最小(和正确,与上面的许多答案不同)版本是:
function Monkey(param)
this.someProperty = param;
Monkey.prototype = Object.create(Monster.prototype);
Monkey.prototype.eatBanana = function(banana) banana.eat()
就是这样。你可以阅读here the longer explanation
【讨论】:
以上是关于JavaScript 扩展类的主要内容,如果未能解决你的问题,请参考以下文章