动态范例言语
编程言语按数据范例大致可以分为两类:静态范例言语与动态范例言语。
静态范例言语在编译时已肯定变量范例,
动态范例言语的变量范例要到递次运行时,待变量被赋值后,才具有某种范例。
而JavaScript是一门典范的动态范例言语。
动态范例言语对变量范例的宽大使得编程变得很天真。因为没必要举行范例检测,我们可以挪用任何对象的恣意要领,而无需去斟酌它底本是不是被设想为具有该要领。而这是建立在鸭子范例的概念上。
鸭子范例
鸭子范例浅显的说法是:
假如它走起路来像鸭子,叫起来也是鸭子,那末它就是鸭子。
鸭子范例指点我们只关注对象的行动,而不关注对象自身。
在动态范例言语的面向对象设想中,应用鸭子范例的头脑,我们没必要借助超范例的协助,就可以轻松地在动态范例言语中完成一个准绳:“面向接口编程,而不是面向完成编程”。比方:
一个对象如有push和pop要领,而且这些要领供应了准确的完成,它就可以够被看成栈来运用;
一个对象如有length属性,且可以遵照下标来存取属性,这个对象就可以够被看成数组来运用。
多态
多态的寄义
统一操纵作用于差别的对象上面,可以发生差别的解释和差别的实行结果。
对象的多态性
我们说的多态性,实在就是对象的多态性,那末,对象的多态性是怎样的?怎样让对象表现出多态性?
对象多态性的一个简朴的例子:
// 让动物发声
var makeSound = function(animal){
animal.sound();
}
// 鸭子的啼声
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 Dog = function(){};
Dog.prototype.sound = function(){
console.log('汪汪汪');
}
makeSound(new Dog());
范例搜检
静态范例言语(比方Java)在编译时会举行范例婚配搜检,这类搜检在带来安全性的同时,让代码显得生硬。因而,静态范例言语通常被设想为可以向上转型:
当给一个类变量赋值时,这个变量的范例既可以运用这个类自身,也可以运用这个类的超类。
就像我们在形貌“一只麻雀在飞”,“一只喜鹊在飞”时,假如想疏忽他们的详细范例,可以说成“一只鸟在飞”,这时刻“鸟”就是“麻雀”和“喜鹊”的超类。
而JavaScript是一门没必要举行范例搜检的动态范例言语。
多态的作用
多态是面向对象编程言语中最主要的手艺。
多态最基础的作用就是经由过程把历程化的前提分支语句转化为对象的多态性,从而消弭这些前提分支语句。有一个例子可以很好地解释:
在影戏的拍摄现场,当导演喊出“anciton”时,主角最先背台词,照明师担任打灯光,背面的群众演员伪装中枪倒地,道具师往镜头里撒上雪花。在获得统一个音讯时,每一个对象都晓得本身应当做什么。假如不应用对象的多态性,而是用面向历程的体式格局来编写这一段代码,那末相当于在影戏最先拍摄后,导演每次都要走到每一个人的眼前,确认他们的职业分工(范例),然后通知他们要做什么。假如映射到递次中,那末递次中将充溢着前提分支语句。
将行动散布在各个对象中,并让这些对象各自担任本身的行动,这正是面向对象设想的长处。
多态与设想形式
从面向对象设想的角度动身,经由过程对封装、继续、多态、组合等手艺的重复运用,提炼出一些可重复运用的面向对象设想技能,我们将其归结为设想形式。而多态在个中是重中之重,绝大多部份设想形式的完成都离不开多态性的头脑。比方:
敕令形式
组合形式
战略形式
…
Javascript将函数作为一等对象,所以函数自身也是对象,函数用来封装行动而且可以四周通报。当我们对一些函数发出“挪用”的音讯时,这些函数会返回差别的实行结果,这是多态性的一种表现。
封装
封装的目的是将信息隐蔽。封装包含:
封装数据
封装完成
封装范例
封装变化
封装数据
在很多言语的对象体系中,封装数据是由语法剖析来完成的,这些言语能够供应了private、public、protected等关键字来供应差别的接见权限。但JavaScript并没有供应对这些关键字的支撑,只能依靠变量的作用域来完成封装特征,而且只能模拟出public和private这两种封装性。
平常我们经由过程函数来建立作用域:
var myObject = (function(){
var __name = 'sven'; //私有(private)变量
return {
getName:function(){ //公然(public)要领
return __name;
}
}
})();
console.log(myObject.getName()); //输出:sven
console.log(myObject.__name); //输出:undefined
封装完成
从封装完成细节来讲,封装使得对象内部的变化对其他对象而言是通明的(即不可见)。对象对它本身的行动担任。其他对象或许用户都不体贴它的内部完成。对象使得对象之间的耦合变得松懈,对象之间只经由过程暴露的API接口来通讯。
封装完成细节的例子异常多,比方迭代器。迭代器的作用是在不暴露一个聚合对象的内部示意的前提下,供应一种体式格局来递次接见这个聚合对象。如一个each函数,它的作用就是遍历一个聚合对象,运用这个each函数的人没必要体贴它的内部代码是怎样完成的,只需它供应的功用准确便可以了。
封装范例
封装范例是静态范例言语的一种主要封装体式格局。封装范例是经由过程抽象类和接口来举行的。
在JavaScript中,并没有对抽象类和接口的支撑。JavaScript自身也是一门范例隐约的言语。在封装范例方面,JavaScript没有才能,也没有必要做得更多。
封装变化
从设想形式的角度动身,封装在更主要的层面表现为封装变化。
经由过程封装变化的体式格局,把体系中稳固稳定的部份和轻易变化的部份断绝开来,在体系的演化历程当中,我们只须要替代掉那些轻易变化的部份,假如这些部份是已封装好的,替代起来也想对轻易。这可以最大水平地保证递次的稳固性和可扩展性。
原型形式
原型形式
原型形式是用于建立对象的一种形式。
原型形式没必要体贴对象的详细范例,只需找到一个对象,然后经由过程克隆来制造一个如出一辙的对象。
原型形式的完成关键是言语自身是不是供应了clone要领,ECMAScript5供应了Object.create要领,可以用来克隆对象。
原型形式的真正目的不在于须要获得如出一辙的对象,而是供应了一种便利的体式格局去建立某个范例的对象,克隆只是建立这个对象的历程和手腕。
在JavaScript这类范例隐约的言语中,建立对象异常轻易,也不存在范例耦合的题目。从设想形式的角度来看,原型形式的意义并不算大。但JavaScript自身是一门基于原型的面向对象言语,它的对象体系就是运用原型形式来搭建的,在这里称为原型编程范型或许更适宜。
原型编程范型
原型编程中有一个主要特征,即当对象没法相应某个要求时,会把该要求托付给它本身的原型。
而原型编程范型最少包含以下基础准绳:
一切的数据都是对象
要获得一个对象,不是经由过程实例化类,而是找到一个对象作为原型并克隆它
对象会记着它的原型
假如对象没法相应某个要求,它会把这个要求托付给它本身的原型
JavaScript中的原型继续
JavaScript在原型编程范型的划定规矩的基础上来构建它的对象体系。
一切的数据都是对象
JavaScript在设想的时刻,模拟Java引入了两套范例机制:基础范例和对象范例。
根据JavaScript设想者的本意,除了undefined以外,一切都应是对象。为了完成这一目的,number、boolean等几种基础范例数据可以经由过程“包装类”的体式格局变成对象范例数据。
JavaScript绝大部份数据都是对象。事实上,JavaScript中的根对象是Object.prototype对象。Object.prototype对象是一个空对象。JavaScript的每一个对象,都是从Object.prototype对象克隆而来。
要获得一个对象,不是经由过程实例化类,而是找到一个对象作为原型并克隆它
JavaScript经由过程显式地挪用 var obj1 = new Object()
, 或许 var obj2 = {}
。此时,引擎内部会从Object.prototype上面克隆一个对象出来。
这里用了new运算符从组织器中获得了一个对象。在JavaScript里,函数既可以作为一般的函数被挪用,也可以作为组织器被挪用。用new运算符来建立对象的历程,实际上也只是先克隆Object.prototype对象,再举行一些其他分外操纵的历程。
对象会记着它的原型
就JavaScript的真正完成来讲,实在并不能说对象有原型,而只能说对象的组织器有原型。“对象把要求托付给它本身的原型”就是对象把要求托付给它的组织器的原型。
JavaScript给对象供应了一个名为__proto__的隐蔽属性,某个对象的__proto__属性默许会指向它的组织器的原型对象,即{Constructor}.prototype。在一些浏览器中,__proto__被公然出来。
假如对象没法相应某个要求,它会把这个要求托付给它本身的原型
这条划定规矩是原型继续的精华地点。当一个对象没法相应某个要求时,它会顺着原型链把要求通报下去,直到碰到一个可以处置惩罚要求的对象为止。
虽然JavaScript的对象最初都是由Object.prototype对象克隆而来,但对象组织器的原型并不仅限于Object.prototype上,而是可以动态地指向其他对象。比方,当对象A须要对象B的才能时,可以有挑选地把对象A的组织器的原型指向对象B,从而到达继续的结果。
PS:本节内容为《JavaScript设想形式与开辟实践》第一章 笔记。