基于对象
描述
面向对象的三大特征继承,封装和多态,完全实现就是面向对象(Object-Oriented),没有完全实现就是基于对象(Object-Based)
度娘解释
基于对象的编程语言没有提供象抽象、继承、重载等有关面向对象语言的许多功能。而是把其它语言所创建的复杂对象统一起来,从而形成一个非常强大的对象系统,以供使用。
即阉割版面向对象,比如VB,JavaScript
区别
1.面向对象的技巧,尤其是设计模式,很多都不能直接使用,用了更蛋疼
2.很多时候js下描述的继承与java描述的继承不一样
基于原型的继承
软件 = 结构 + 算法 ,针对面向对象,这里的结构指是对象(什么是对象),继承用于描述两个对象(结构)的逻辑关系
面向对象继承的逻辑问题
而这个逻辑在具体描述时,又有各种逻辑细节
1.封装
比如可继承与不可继承(即封装性)
2.抽象
继承是结构,指的是逻辑关系,具体的业务并不重要
继承绝不会以来一个具体的对象,这是与js最大的区别
3.其他关键词
interface,abstract,override,overload,super...
均为对继承或者三大特性具体逻辑的处理,再具体一点就是jvm规范
js的继承的逻辑问题
js通过构造器,实现如何描述两个结构的关系,针对面向对象的逻辑问题,会进行选择性实现(无视),逻辑上只有两点
1.对象(实例)只有构造自某个原型
即实例不存在拥有原型的问题
而构造器有原型,指向的就是原型,这是基础逻辑观点
2.原型也(必须)是一种实例
对于基于类实现体系来讲,类是一个抽象,继承的也可以是抽象结构,甚至可能都不算一个结构对象
js继承必须是一个实例
js的继承的实现
没有固定语法,只有对原型对象的理解,提出的解决方案
所以有人说基于对象没有继承,第一原因就是没有语法
针对使用,处理掉js的继承的两项逻辑问题即可
第一种有结构器型
//1.原型是一种实例
Son.prototype = new Father();
// 2.构造器有原型,且指向原型
Son.prototype.constructor = Son;
第二种无结构器型
function object(o) {
function F() {}
F.prototype = o;
return new F();
}
var b = obj({
a:1
})
这里最大的槽点在于,手动修改指向,以及没有完善的验证系统
js继承关系,更多的是规范一对多的引用关系
继承用于描述两个对象(结构)的逻辑关系,实现时,更多的是解决内存的复用,如果单说描述对象的关系,不用原型也可以实现,如
function Son{
this.father = new Father();
}
或者使用内置的father属性(随便写的),并在构造器中,自行进行如上处理
Son.father = Father
又或者加上内存复用,进行如下复用
//对象定义
Father(){
}
Son(){
}
//父级实例的应用
var father = new Father();
//子实例的应哟
var son1 = new Son();
//手动创建关联
son1.father = father;
var son2 = new Son();
son2.father = father;
在这里描述原型链的作用,大致就是减少手动创建关联的次数,以及规范化
js的继承实现与java继承的逻辑区别
最后,js没有继承的一个原因是,手动出来实例对象,本身就是组合的一种方式,因为基于类的继承的父类实例是不能(很难)拿出来使用的,如果有反复使用的需求,组合就是替代方案
比如在java中
public class Father {
static int count = 0;
Father(){
count++;
System.out.println("父类实例初始化"+count);
}
}
public class Son extends Father {
Son(){
System.out.println("子类实例初始化");
}
}
public static void main(String[] args) {
Son son1 = new Son();
Son son2 = new Son();
}
运行结果是
父类实例初始化1
子类实例初始化
父类实例初始化2
子类实例初始化
继承的是抽象,逻辑和对象的关系,父类的构造器创建了两次,但父类的具体实例是不存在的,描述一下就是son1与son2都继承至父亲(父类),他们的父亲(父类)有通用的特点,但他们是谁,不知道
如果业务上一定要描述出父类,那一定会使用组合,实例化父类后,进行业务上的连接,无论是代理,装饰或者其他类似的模式,也使用相似的方式进行处理,依赖于手动创建父类
public class Son {
Son(Father father){
this.father = father;
}
}
对应js继承实现的语义
//father 是一名父亲(父类的实例)
var father = new Father();
//他有很多儿子
Son.prototype = new Father();
Son.prototype.constructor = Son;
//大儿子
var son1 = new Son();
//小儿子
var son2 = new Son();
这就是为什么说
js没有继承,只有组合,原型链代码复用的意义大于描述对象结构关系
官方定义
ECMA-262-3
http://dmitrysoshnikov.com/ecmascript/chapter-7-1-oop-general-theory/
另一个原因是官方对继承的定义,ECMA-262-3,针对继承一块的描述
For improvement of a code reuse, classes can extend other classes, bringing necessary additions. This mechanism is called as (hierarchical) inheritance.
中文翻译
为了提高代码重用,类可以从一个扩展为另一个,在加上额外的信息。 这种机制被称为(分层)继承 。
这就是为什么我会说js的继承,强调的是复用,无论代码上还是内存上,而对其逻辑上的设计关系并没有描述
后续有描述到继承链,关于继承链,我带入的理解就是原型链
java规范有一节是对所有概念的定义,关于继承这一块的描述不同于js,很遗憾,没有找到
self
再然后就是js参考的语法基础,self,这里一直强调的是动态继承以及traits(类似于mixin)
这块文档基本都没了,只是凭映像描述
使用
最后是使用上,除了最开始写组件会很OO外,其他时候能用上,真的是看在组合的意义上,比如Vue,VueComponent继承Vue,假设他是基于类的描述,这里是不可能让VueComponent继承Vue的,只会组合,比如
function VueComponent(vue){
this.vue = vue;
}
//使用
<script>
{
methods;{
back(){
this.vue.$router.back(-1);
}
}
}
</script>
这是引擎和解析对象,工厂与产品的关系,逻辑上不可能继承出来的
使用场景
只要不是多对多的关系都可以实现js的继承,主要看是否需要,比如强行VueRouter继承Vue(当然,一对一的继承除了少些一个指向,整体意义不大,有点通过bind修改this的意思)
唯一的问题是改变原型需要获取到vue的实例,两种处理
1.代理/工厂,即由VueRouter提供Vue创建的功能
显然是越权了
2.主动提供vue的实例
那这个就是组合了
这也是为什么会说,不是自己写的,或者有框架级方案,不要主动去继承
没有框架级语法约束,继承的基类的升级,对被继承来讲很容易是毁灭级的(主要指属性或方法重名)
总结
js通过原型链实现的动态继承,主要目的是代码复用,而不是用OO的思想设计和编程,这是我个人经验的描述