JavaScript基础④基于原型的面向对象编程
前言
“世间万物皆对象 ”, 何为对象? 对象可以是风 , 可以是雨 , 可以是万物星辰 , 可以是你我 。在Java编程中 , 将世间万物归为类 , 创建对象必有类 , 类为对象的模版 , 对象为类的实例 。 在JavaScript早期是没有纯正的OOP,只有基于原型的伪OOP,今天这篇主要介绍JavaScript的基于原型的面向对象,下篇介绍最新支持的OOP编程。
简介
JavaScript早期没有传统面向对象语言的中有类的概念,而是把一堆无序属性的集合,其属性包含有基本值,函数,对象,将这些组合起来,产生一个对象。每个对象都是基于一个引用类型创建的。
基于引用的对象
// 创建一个Object对象引用
var person = new Object();
// 动态创建一个对象属性
person.name = "zeno";
person.age = 20 ;
person.job = "Software Engineer";
// 动态创建对象方法
person.sayName = function(){
alert(this.name);
};
person.sayName(); // out : zeno
调用类构造函数
// 构造方法创建原型对象
function Person(name,age,job) {
var obj = new Object();
obj.name = name ;
obj.age = age;
obj.job = job;
obj.sayName = function() {
alert(this.name);
};
return obj;
}
var p1 = Person("zeno",20,"Software Engineer");
var p2 = Person("lily",23,"Teacher");
p1.sayName(); // out : zeno
p2.sayName(); // out : lily
使用字面量来创建对象
// 使用字面的方式创建对象
var person = {
// 属性及方法以 : 的形式,形成键值对模式 , 左边为属性名称 , 右边为属性值
name:"zeno",
age:20,
job:"Software Engineer",
sayName:function(){
alert(this.name);
}
}
person.sayName(); // out : zeno
使用字面量创建的对象的方式比较普遍 , 因其书写方便,类似JSON格式,而广为使用 。
原型对象
使用原型创建对象,将Function
视为一个类标识 。
创建原型对象
// 基于原型 , 创建一个Person类
function Person() {
// 类的属性
Person.prototype.name = "zeno";
Person.prototype.age = 20;
Person.prototype.job = "Software Engineer";
// 类的方法
Person.prototype.sayHello = function(){
alert("Hello "+this.name);
};
}
// 创建Person对象
var person = new Person();
alert(person.name); // out : zeno
person.sayHello(); // out : Hello zeno
在原型时代,将function
视为class , 用以创建类, 在function
中创建属性与方法 。
简便的创建方式
// 更简便的原型对象模式
function Person1(){}
Person1.prototype = {
name:"xiaojiu",
age:20,
job:"Teacher",
sayName:function(){
alert(this.name);
}
};
我们创建的每个函数都有一个 prototype(原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。
原型对象
无论什么时候,只要创建了一个新函数,就会根据一组特定的规则为该函数创建一个 prototype属性,这个属性指向函数的原型对象。在默认情况下,所有原型对象都会自动获得一个 constructor(构造函数)属性,这个属性包含一个指向 prototype 属性所在函数的指针。
继承
JavaScript继承,通过原型链来实现继承 , JavaScript继承实现的本质是:重写原型对象,代之以新的类型实例 。JavaScript只能实现单继承 , 子类只能继承一个父类 , 多重继承时后继承的父类属性和方法会覆盖之前继承的父类属性和方法。
单继承
// 父类
function SuperType() {
this.property = true;
this.name = "zeno"
}
// 子类
function SubType(){
this.subProperty = false;
}
// 使用原型继承父类
SubType.prototype = new SuperType();
var sub = new SubType();
alert(sub.name); // out : zeno;
多重继承
// 父类
function SuperType() {
this.property = true;
this.name = "zeno"
}
// 父类2
function SuperType2() {
this.name = "lily";
}
// 子类
function SubType(){
this.subProperty = false;
}
// 使用原型继承父类
SubType.prototype = new SuperType();
SubType.prototype = new SuperType2();
var sub = new SubType();
alert(sub.name); // out : lily
由上述代码可以看出 , JavaScript多重继承的时候 , 后续继承的父类会覆盖前面继承的父类的属性。
使用call函数实现简便继承
在子类中无法使用父类原型中定义的方法,并且父类会调用两次构造方法,一次是创建子类原型的时候,另一次是在子类型构造函数的内部。
function Person(name,age) {
this.name = name ;
this.age = age ;
}
Person.prototype = {
showInfo : function() {
alert("name = "+this.name+" age = "+this.age);
}
};
function Teacher() {
Person.call(this,"zeno",20);
}
var teacher = new Teacher();
alert(teacher.showInfo()); // 报错
alert(teacher.name); // out : name
使用call函数实现继承的时候 , 只能使用function
里面的属性和方法 , 对象的原型方法却调用不了 , 我们在调用属性方法的时候会报找不到方法的错误 。使用call函数实现继承 , 少了一点灵活 , 如果将原型继承与call函数想结合 , 就可以比较完美的实现继承了 。
组合继承
function Person(name,age) {
this.name = name ;
this.age = age ;
}
Person.prototype = {
showInfo : function() {
alert("name = "+this.name+" age = "+this.age);
}
};
function Teacher() {
Person.call(this,"zeno",20);
}
Teacher.prototype = new Person();
var teacher = new Teacher();
alert(teacher.showInfo()); // name = zeno age = 20 // undefined
运行之后我们会发现 , alert了两次 , 第一次 name = zeno age = 20 , 第二次 undefined , 为什么会出现两次弹窗呢 ?因为我们实例化了两次Person , 在teacher对象中就会存在两个showInfo()方法, 一个有值一个没有值。解决方案是 : call继承function
内部的属性和方法 , 那么可以使用子类的Protorype来接收父类的prototype属性 。
组合增强
// 属性继承
function inheritPrototype(subType,superType) {
var prototype = superType.prototype;
prototype.constructor = subType;
subType.prototype = prototype;
}
// 父类
function SuperType(name) {
this.name = name;
this.books=["Java","JavaScript"];
}
// 父类原型属性
SuperType.prototype = {
age:20,
sayAge:function(){
alert(this.age);
}
};
// 子类继承父类
function SubType(){
SuperType.call(this,"sub");
}
// 原型继承
inheritPrototype(SubType,SuperType);
var sub = new SubType();
sub.sayAge(); // out : 20
alert(sub.books); // out : Java , JavaScript
将父类属性赋值给子类属性 ,将父类的构造函数也交由子类实现 , 这样可以避免多次创建父类对象。
结语
使用JavaScript原型来实现面向对象的基本继承 , 比较麻烦 , 完全没有Java那种比较清爽的感觉, 需要比较复杂的来实现子类继承父类 。在最新标准(ES6)的JavaScript已经可以使用class
,extends
这些关键字来实现OOP,基本上比较像Java的样子了 。
最后用一首歌来为我们的梦想加油 《追梦赤子心》
充满鲜花的世界到底在哪里
如果它真的存在那么我一定会去
我想在那里最高的山峰矗立
不在乎它是不是悬崖峭壁
用力活着用力爱哪怕肝脑涂地
不求任何人满意只要对得起自己
关于理想我从来没选择放弃
即使在灰头土脸的日子里
也许我没有天分
但我有梦的天真
我将会去证明用我的一生
....