本篇对面向对象做一部分知识补充,其他可以参照以下提供的参考资料
描述
类/继承描述了一种代码的组织结构形式--一种在软件中对真实世界中问题领域的建模方法。 面向对象编程强调的是数据和操作数据的行为本质上是相互关联的,因此好的设计就是把数据以及和它相关的行为打包(或者说是封装)起来.
个人简解:面向对象将某个有共有属性的事物进行抽象,将公有的数据进行封装,并建立某个数据结构,已经在此结构上附加对应的数据操作,力求达到解耦和复用。
- 封装
Java语言有对应的关键字,比如:privated、protected等关键字进行修饰,以限定属性或行为的使用范围,是由语法层面处理的。但是JavaScript中只能通过 变量作用域 实现。
- 继承
常见面向对象语言上,都有类似 extends 的关键字完成抽象类型到具体类型的派生,但至少在ES6的语法糖出现之前,通过 原型委托 实现。
- 多态
由于JavaScript是弱类型的语言,类型的处理只能在运行时体现出来,所以没有类似Java一样,强制对某个对象设计只属于它的可操作类。JavaScript是否有某个操作只取决于它有没有对应的操作,并不取决于它是否是某个类型,我觉得JavaScript本就是多态的。
普通创建形式
function Person( name, age ){
this.name = name;
this.age = age;
this.sayName = function(){
console.info('hello,我是小米');
};
}
var p = new Person('小米', 20);
p.sayName();
原型继承
原型继承也只是利用原型链的查找
function Person(){}
Object.assign(Person.prototype, {
name: '小米',
age: 20,
sayName: function(){
console.info('hello,我是', this.name);
}
});
function Student(){}
Student.prototype = new Person();
var st = new Student;
st.sayName();
某个函数prototype上定义的属性都是共享的,通过对该函数进行构造调用,得的对象都会有一个属性关联到该函数的原型对象上,属性的查找也就是原型链的遍历。
📍 单纯使用这种,导致在创建对象时,无法向超类构造函数调用时传递参数。
借用构造函数
通过call和apply的方式间接的调用函数
function SuperType(name){
this.colors = ['red', 'green'];
this.name = name;
// this.sayHello = function(){};
}
function SubType( name ){
SuperType.call(this, name);
}
var obj = new SubType('subType');
📍 通过在构造函数中,手动调用超类型的构造函数,并绑定this为当前构造实例。但单独使用,无法复用在超类型上定义的各种函数。
组合继承
组合继承就是将二者结合起来
function SuperType(name){
this.name = name;
}
SuperType.prototype.sayName = function(){
console.info(this.name);
}
function SubType(name, age){
SuperType.call(this, name);
this.age = age;
}
SubType.prototype = new SuperType();
SubType.prototype.sayAge = function(){
console.info(this.age);
};
📍 在继承的时候,调用了两次构造函数,如果超类消耗比较大,那么可能会造成额外的浪费。
原型式继承
通过中间对象进行间接引用
function inherit( o ){
function F(){}
F.prototype = o;
return new F;
}
var stu = inherit({
name: '',
colors: []
});
以某个对象为基础,进行原型上的关联,但这也意味着 colors 属性会被共享
寄生组合继承
function inherit(Sub, Super){
function F(){}
F.prototype = Super.prototype;
Sub.prototype = new F;
Sub.prototype.constructor = Sub;
Sub.prototype.Super = Super;
}
function SuperType(name){
this.name = name;
}
SuperType.prototype.sayName = function(){
console.info(this.name);
}
function SubType(name, age){
// SuperType.call(this, name);
this.Super.call(this, name);
this.age = age;
}
inherit(SubType, SuperType);
/*
1. 令新的函数 F 的原型对象指向Super ;F.prototype = Super.prototype;
2. Sub.prototype = new F; 即生成F的实例,但是F实例的原型又关联着Super的原型,故Sub的原型通过 F的实例,和 Super的实例进行关联
Sub.prototye -> F[instance].__proto__ -> F.prototype -> Super.prototype
3. 最后重置Sub原型上的constructor属性为自己
*/
SubType.prototype.sayAge = function(){
console.info(this.age);
};
EXT中Base.js也有类似的操作:
extend: function(parent) {
var parentPrototype = parent.prototype, //
basePrototype, prototype, i, ln, name, statics;
prototype = this.prototype = Ext.Object.chain(parentPrototype); // this.prototype > obj -> parent.prototype
prototype.self = this; // 保留当前的函数
this.superclass = prototype.superclass = parentPrototype; // 当期构造器和原型中都存入父类的原型
if (!parent.$isClass) { // 如果不是Class的类型
basePrototype = Ext.Base.prototype; //
// 将Base中的原型 拷贝到当前的原型里面
for (i in basePrototype) {
if (i in prototype) {
prototype[i] = basePrototype[i];
}
}
}
}
📑可以参考《JavaScript 框架设计》第五章类工厂部分有更多详细的资料
参考资料:
《You Don't Know JS: Types & Grammar》上卷
《JAVASCRIPT 语言精髓与编程实践》
《JavaScript 高级程序设计》
《JavaScript 权威指南》
《JavaScript 面向对象编程指南》