js包含三部分:1,ECMAScript语法 2,DOM 3,BOM. 应该说ECMAScript这种东西,语法和许多概念都是源于那些高级语言(C/C++, Java),单就面向对象和继承来讲,js又属于比较特殊的,它的类是一个构造函数,函数又属于一种对象,而它又不支持直接的继承,故而就须要使用prototype或者是冒充对象来实现继承和多态。有人说js是属于一种基于对象的,而不是面向对象,由于面向对象要有三要素: 1, 封装 2,继承 3,多态。 而js的多态是经过给不一样的对象添加不一样的属性而实现的。其实本质上仍是用子类的方法覆盖父类的方法。
javascript
js的类是用函数包裹的,里面定义了属性和方法,也就是闭包,google一下什么是闭包:
css
在计算机科学中,闭包(Closure)是词法闭包(Lexical Closure)的简称,是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即便已经离开了创造它的环境也不例外。因此,有另外一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体。
html
也就是说数据与函数创建了联系并共同组成一个实体,且运行离开该函数后,这些数据依然存在。coolshell有一篇文章专门介绍 javascript的闭包。那么看看代码是怎么写的吧:java
典型的工厂模式:
python
function sayName(){ alert("hi, I am "+ this.name); } // 工厂模式 function createUser(name,age){ var user = new Object; // 进行装配 user.name = name; user.age = age; user.sayName = sayName; return user; } var bibo = createUser("bibo", 20); bibo.sayName();
整个过程就是新建一个对象,而后动态地为对象添加各类属性方法,就连方法也能够预先定义好,到时候予以赋值。这就很像工厂里面进行装配,装配完就返回——出厂,因而也叫“工厂模式”。web
构造函数方式:shell
就是在一个函数内将全部东西都生产出来,而不是从外面拿。故而函数什么的要在里面定义,这就造成了一个很奇特的现象——函数里面包函数。通常C++里面不会出现这样的东西,可是咱们要有一种观念,对象既然是构造函数造出来的,那么这个类和造这个类的函数就能够划等号了。而C++的方式叫作“对象模板”会至关贴切。编程
// class User function User(name, age){ this.name = name; this.age = age; // 定义方法 this.sayName = function(){ alert("hi, I am "+ this.name); } } var bibo = new User("bibo", 20); bibo.sayName();
有几个比较明显不一样的地方,如在构造函数内使用this引用,指代该对象。而后在新建的时候用了new操做符,相比工厂模式的new Object,显得更加精简了一些。其余方面大致相同,这有点像妈妈怀孕同样,在“肚子”里把你整个给造成了(今天刚好是母亲节~)。闭包
原型方式:app
原型方式是使用prototype来定义类所包含的属性和方法,它使用的是一个空构造函数,而后把整个构造函数的过程实现,使得它可以造出一个对象。用代码说话吧,由于暂时找不到恰当的隐喻来讲明:
function User(){ } User.prototype.name = "bibo"; User.prototype.age = 20; User.prototype.sayName = function(){ alert("hi, I am "+ this.name); } var bibo = new User(); bibo.sayName();
能够发现,这在写好构造函数后,依然能够修改构造函数实现的方法,故而可以达到定制的目的。js的多态是使用这个实现的,由于prototype只有一个,新的会覆盖旧的。
以上三种很自由的对象构造方法,各有优缺点,能够混合使用,所以会很是灵活。另外极可能会引出如动态原型方法(动态检测是否含有原型方法,不然添加)等更加复杂的方式,其实都是这三种演变而来。
js的继承可使用假冒对象和原型继承的方式(一个类的原型是另一个类)
对象冒充:
// class A function User(name, age){ this.name = name; this.age = age; // 定义方法 this.sayName = function(){ alert("hi, I am "+ this.name); } } // class B function VIP_User(name, age, money){ this.newMethod = User; // 拷贝一份构造函数 this.newMethod(name, age); // 用构造函数运行 delete this.newMethod; // 继承完毕 // 新的属性/方法 this.money = money; this.pay = function(){ alert("pay"); } } var bibo = new VIP_User("bibo", 20, 10000); bibo.sayName(); // 继承的 bibo.pay(); // 本身的
核心部分就在13-15行,咋一看,是拷贝一份构造函数,而后运行该方法,而后删除该方法,玄妙就在于14行,在该函数内运行了一遍class A的构造函数后,class 产生的对象中就带有了A的血液。由于等于把代码贴过来,运行一遍。
还有一种使用call和apply来运行父类的构造函数的方法,可是要传入一个this对象。是这样写的,把核心的13-15行换成:
User.call(this, name, age);
下面讨论另一种不一样的方式
原型链继承:
// Class A function User(){ } User.prototype.name = "bibo"; User.prototype.age = 20; User.prototype.sayName = function(){ alert("hi, I am "+ this.name); } // Class B function VIP_User(){ } VIP_User.prototype = new User(); // 用A的实例来做为B的原型 VIP_User.prototype.pay = function(){ alert("pay"); } var bibo = new VIP_User(); bibo.sayName(); bibo.pay();
继承的在前,而后添加子类的新的方法或属性。VIP_User以User的实例为原型,而后才予以扩展的。
以上两种继承的方法也能够混合使用,只要理解了这两种继承的思想,就不难在复杂的继承中混合使用这两种方法。
刚刚接触js,就面向对象这两件东西比较难理解,其余语法不少都和C++类似,又和动态脚本语言如python有点类似。有函数式编程的味道。以上观点若有不正确,欢迎评论指正。
by bibodeng 2013-05-12