大家喜欢用 JavaScript 中哪一种继承方式?大家来说说自己的看法 OwO

2015-06-12 21:47:09 +08:00
 bramblex

JavaScript继承主要是两种形式:
1. 原型链继承
2. 拷贝继承

注:ES7标准中的class就暂时不考虑了,因为还没有具体去了解过,希望以后能完善到不需要自己实现那么那么蛋疼的东西了。

我个人倾向于原型链继承,因为原型链继承是JavaScript规范里面推荐的继承,并且最重要的是,可以用原生的 instanceof 判断类型!而拷贝继承不行!


因为以上的一些问题,所以我就自己做了一个简单的JavaScript面的对象库。大概有实现这么几个功能:
1. 封装好的构造函数不管写不写new操作符都不会产生错误了,当然我是倾向于不写new操作符。
2. 返回的构造函数是命名的,方便调试的时候追踪函数栈。
3. 封装了一个parent方法,可以方便访问父原型的构造函数或者父原型的方法。
4. 封装了一个alias方法,可以创建一个方法别名。

具体实现在具体实现在github上 >> 代码传送门

接下来来看一些简单的示例来看看怎么用。

// 首先定义一个Animal类(姑且称为类吧),继承自Objec,有一个构造函数。
var Animal = BlxClass.extend('Animal', Object, function(name, sound){
  this.name = name || null;  
  this.sound = sound || null;
});

Animal.prototype.call = function(){
  console.log(this.sound);
};

// 现在我希望定义一个doge类,继承自Animal。第一个参数 ‘Doge’ 是用来给构造函数命名的。
var Doge = BlxClass.extend('Doge', Animal, function(name){
  var sound = 'wang';
  BlxClass.parent(this, Doge, [name, sound]); // 调用父类的构造函数,因为初始化对象属性在父类构造函数里。
});

Doge.prototype.wang = BlxClass.alias('call');

// 现在实例化一个doge对象
var doge = Doge('Tom'); // 有没有 new 都没关系,结果相同。
doge.call(); // =>'wang'
doge.wang(); // =>'wang'

然后这样就跟其他语言类的继承大致相似了。然后大家更喜欢哪一种继承方式呢?都来说说自己看法吧。

4509 次点击
所在节点    JavaScript
54 条回复
bramblex
2015-06-12 21:48:16 +08:00
@yahoo21cn 展开讨论在这里 OwO
exoticknight
2015-06-12 21:56:19 +08:00
大神 Douglas Crockford 的代码
if ( typeof Object.create !== 'function' ) {
Object.create = function ( o ) {
function F() {}
F.prototype = o;
return new F();
};
}
http://javascript.crockford.com/prototypal.html
bramblex
2015-06-12 22:02:53 +08:00
@exoticknight

这个思路是从一个对象复制出另一个对象,OwO,然后再加工新对象吗?感觉有点坑……
hbkdsm
2015-06-12 22:03:51 +08:00
论茴香豆的茴有几种写法
icymorn
2015-06-12 22:08:11 +08:00
我没用到这种专门搞一个继承的方法,js太灵活了,很多问题在c++首先想到继承解决,而在js中奇技淫巧耍耍就解决了。当然,要我写一个继承,首先从原型链上搞起。
bramblex
2015-06-12 22:08:43 +08:00
@hbkdsm

茴香豆有几种写法都无所谓,能表意即可。但是对于几种不同继承方式,可远远没有那么简单了。如果只是写写jQuery,那当然无所谓。
bramblex
2015-06-12 22:09:59 +08:00
@icymorn

有什么好的实现方案吗?我就是从原形链开始搞起的。OwO
hbkdsm
2015-06-12 22:18:18 +08:00
@bramblex 最简洁的应该是 ES6 的 class-extends-super 这一套语法糖,Nodejs 的 util.inherits 也很好啊。你掌握之后应该就很厉害了,因为你终于知道茴有几种写法了!
yangff
2015-06-12 22:22:37 +08:00
class 的继承方式我记得就是原型继承的语法糖? <求确认
bramblex
2015-06-12 22:26:55 +08:00
@hbkdsm

然而我已经实现了类似你所说的那些东西。
bramblex
2015-06-12 22:27:39 +08:00
@yangff

不知道哎,如果是原型继承的语法糖,那么坑爹的问题还是依旧存在的……
yangmls
2015-06-12 22:35:45 +08:00
@yangff 是的,原型链的语法糖


@bramblex 粗略看了下代码

1. 为何要用 eval,似乎只是为了处理 name 是个字符串,虽然这玩意不一定evil,但是代码可读性就很差。

2. child 的 prototype.constructor 没有处理?直接 new 了 parent

3. Backbone 的那个 extend 不是更好的方案?
bramblex
2015-06-12 22:52:06 +08:00
@yangmls

1. eval 是两个作用:
1.1. 命名。如果没有命名在chrome下输出的东西太难看了,受不了。虽然不命名也可以。
1.2. 没有办法 new func.apply(this, args),所以只能事先把从constructor里面读出来有几个参数,然后用arguments[0], arguments[1] ....这样写进去。

2. childe 的 prototype 直接 new 了 parent。然而是会出点问题,现在马上解决掉。

3. Backbone那么个方案我没看过……但是不能光会用别人的东西嘛,对吧。
pinxue
2015-06-12 22:52:16 +08:00
在 JavaScript 里仿 Class 的都是异端!

ES5之前:
var o = { foo:bar, proto: p };

ES5加了:
var o5 = Object.create(p);
bramblex
2015-06-12 23:04:22 +08:00
@pinxue

然而我就是异端 /w\ 。因为真心比原型继承好用,总感觉原型继承是半残的……其实只要达到目的,无所谓什么异端不异端啦
YuJianrong
2015-06-12 23:11:13 +08:00
我在公司做的class系统是原型链继承,还算好用吧。

对于构造函数没加new这个我以前是赞成不写也new出来的做法,不过现在不赞成了,如果新做一个我一定要抛异常。原因很简单:我不希望做一件事情有多种不一样的做法,如果大家做法一样的话,重构或者静态代码分析都会简单很多,这对于大规模协同开发很重要(jQ是反例)。

我在这个之上还做了destroy方法,这个方法会自动迭代调用父类的destroy方法,并会清空对象成员,保证已销毁对象在大多数情况下使用会崩溃,以第一时间找到问题点。
bramblex
2015-06-12 23:22:11 +08:00
@YuJianrong

很有道理啊!
lrvy
2015-06-12 23:24:14 +08:00
JJ的思念
bramblex
2015-06-12 23:36:53 +08:00
@lrvy 快说,你是哪个基佬?
yangmls
2015-06-12 23:40:45 +08:00
借鉴一下把

var extend = function(protoProps, staticProps) {
var parent = this;
var child;

// The constructor function for the new subclass is either defined by you
// (the "constructor" property in your `extend` definition), or defaulted
// by us to simply call the parent constructor.
if (protoProps && _.has(protoProps, 'constructor')) {
child = protoProps.constructor;
} else {
child = function(){ return parent.apply(this, arguments); };
}

// Add static properties to the constructor function, if supplied.
_.extend(child, parent, staticProps);

// Set the prototype chain to inherit from `parent`, without calling
// `parent` constructor function.
var Surrogate = function(){ this.constructor = child; };
Surrogate.prototype = parent.prototype;
child.prototype = new Surrogate;

// Add prototype properties (instance properties) to the subclass,
// if supplied.
if (protoProps) _.extend(child.prototype, protoProps);

// Set a convenience property in case the parent's prototype is needed
// later.
child.__super__ = parent.prototype;

return child;
};

// 如何用

var BaseObject = function() {};

BaseObject.extend = extend;

var Dog = BaseObject.extend({
call: function() {}...
});

//当然,new是必须的

var dog = new Dog

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/198118

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX