1,面向对象的 JavaScript
1.1 理解鸭子类型
鸭子类型的通俗说法是:“如果它走起路来像鸭子,叫起来也是鸭子,那么它就是鸭子。”
国王要听的只是鸭子的叫声,这个声音的主人到底是鸡还是鸭并不重要。鸭子类型指导我们只关注对象的行为,而不关注对象本身。这里我个人的理解是把谁来做和做什么事给抽离开。在做什么事里面,我不需要关心是谁来做,我只需要关心怎么去做这件事。而至于谁来做,谁来做都可以,只要可以能做这件事就行。
var duck = {
duckSinging: function(){
console.log( '嘎嘎嘎' );
}
};
var chicken = {
duckSinging: function(){
console.log( '嘎嘎嘎' );
}
};
var choir = []; // 合唱团
var joinChoir = function( animal ){
if ( animal && typeof animal.duckSinging === 'function' ){
choir.push( animal );
console.log( '恭喜加入合唱团' );
console.log( '合唱团已有成员数量:' + choir.length );
}
};
joinChoir( duck ); // 恭喜加入合唱团
joinChoir( chicken ); // 恭喜加入合唱团
上面的代码就可以很好的解释了,在怎么做中,无需检查它们的类型,而是只需要保证它们拥有 duckSinging 方法。如果下次期望加入合唱团的是一只小狗,而这只小狗刚好也会鸭子叫,那么这只小狗也能顺利加入。
所以在静态语言中,大家都遵循着面向接口(面向对象)编程,而不是面向过程,通过一个抽象类来约束怎么做一件事,然而Javascript是动态语言并且没有接口的概念,这里我们就可以用鸭子模型,只要这个对象有了某种能力,那么它就可以去做某件事,而做的这件事不必像静态语言一样受谁来做的约束。
1.2 多态
多态用一句话来概括就是:同一操作作用于不同的对象上面,可以产生不同的解释和不同的执行结果。
从字面上来理解多态不太容易,下面我们来举例说明一下。
主人家里养了两只动物,分别是一只鸭和一只鸡,当主人向它们发出“叫”的命令时,鸭会“嘎嘎嘎”地叫,而鸡会“咯咯咯”地叫。这两只动物都会以自己的方式来发出叫声。它们同样“都是动物,并且可以发出叫声”,但根据主人的指令,它们会各自发出不同的叫声。
接下来我们用代码来实现:
var makeSound = function( animal ){
if ( animal instanceof Duck ){
console.log( '嘎嘎嘎' );
}else if ( animal instanceof Chicken ){
console.log( '咯咯咯' );
}
};
var Duck = function(){};
var Chicken = function(){};
makeSound( new Duck() ); // 嘎嘎嘎
makeSound( new Chicken() ); // 咯咯咯
上述代码就蕴含着多态的思想,都是发出声音的动作,但只因为做这件事的动物不同,所以得到的结果也不同。注意,这里还是在培养我们一种思想,就是把做一件事和谁来做给分开。但上面的代码是有问题的,他在做什么事的时候,并没有把谁来做给解耦开,还是和谁来做耦合在一起,这样就会导致,如果后期又来了一条狗,那么在代码内又多了一条分支,这样后期维护起来会很困难,而且也违反了设计模式的开放—封闭原则。
要想对上面的代码进行优化,在Java中需要用到多态的语法,定义一个动物抽象类,他具有发出声音的动作,然后让谁来做去继承该抽象方法,去实现发出动作的具体内容,利用多态的语言,即左父右子创建多态子类(说白了,其实就是一个向上转型,逃避类型检查的作用),在做什么事内,做的内容需要接受一个动物类,这样经过多态创建的子类就可以逃避类型检查成功的作为参数传入。以上是个人的理解,下面是具体的Java实现多态的代码。
public abstract class Animal {
abstract void makeSound(); // 抽象方法
}
public class Chicken extends Animal{
public void makeSound(){
System.out.println( "咯咯咯" );
}
}
public class Duck extends Animal{
public void makeSound(){
System.out.println( "嘎嘎嘎" );
}
}
Animal duck = new Duck(); // 多态语法
Animal chicken = new Chicken(); // 多态语法
现在剩下的就是让 AnimalSound 类的 makeSound 方法接受 Animal 类型的参数,而不是具体的Duck 类型或者 Chicken 类型:
public class AnimalSound{
public void makeSound( Animal animal ){ // 接受 Animal 类型的参数
animal.makeSound();
}
}
public class Test {
public static void main( String args[] ){
AnimalSound animalSound= new AnimalSound ();
Animal duck = new Duck();
Animal chicken = new Chicken();
animalSound.makeSound( duck ); // 输出嘎嘎嘎
animalSound.makeSound( chicken ); // 输出咯咯咯
}
}
但在JavaScript中就完全没有这么复杂,这都是归功于JavaScript属于静态语言,它不需要像Java一样进行类型检查,所以JavaScript实现上面案例的代码如下:
var Duck = function(){}
Duck.prototype.sound = function(){
console.log( '嘎嘎嘎' );
};
var Chicken = function(){}
Chicken.prototype.sound = function(){
console.log( '咯咯咯' );
};
makeSound( new Duck() ); // 嘎嘎嘎
makeSound( new Chicken() ); // 咯咯咯
var makeSound = function( animal ){
animal.sound();
};
由此可见,某一种动物能否发出叫声,只取决于它有没有 makeSound 方法,而不取决于它是否是某种类型的对象,这里不存在任何程度上的“类型耦合”。这正是我们从上一节的鸭子类型中领悟的道理。在 JavaScript 中,并不需要诸如向上转型之类的技术来取得多态的效果。
1.3 封装
略
1.4 继承
略(在之前红宝书相关内容中有更为详细的笔记)