JavaScript面向对象的几种实现方法
工厂模式
1 function createBlog(name, url) { 2 var o = new Object(); 3 o.name = name; 4 o.url = url; 5 o.sayUrl= function() { 6 alert(this.url); 7 } 8 return o; 9 } 10 var blog1 = createBlog('wuyuchang', 'http://www.jb51.net/');
优点:实现方法非常简单,在函数内创建一个对象,给对象赋予属性及方法,再将对象返回即可。解决了创建多个相似对象的问题。
缺点:工厂模式不能识别对象的类型,因为全部都是Object,不像Date、Array等。因此出现了构造函数模式。
构造函数模式
1 function Blog(name, url) { 2 this.name = name; 3 this.url = url; 4 this.alertUrl = function() { 5 alert(this.url); 6 } 7 } 8 9 var blog = new Blog('wuyuchang', 'http://www.jb51.net/'); 10 console.log(blog instanceof Blog); // true, 判断blog是否是Blog的实例
优点:可以创建特定类型的对象,类似于Array、Date等原生JS的对象。
缺点:每次创建实例的时候都要重新创建一次方法(理论上每次创建对象的时候对象的属性均不同,而对象的方法是相同的),然而创建两次完全相同的方法是没有必要的,因此,我们可以将方法函数移到对象外面。
像下面这个例子:
1 function Blog(name, url) { 2 this.name = name; 3 this.url = url; 4 this.alertUrl = alertUrl; 5 } 6 7 function alertUrl() { 8 alert(this.url); 9 } 10 11 var blog = new Blog('scjb51', 'http://sc.jb51.net/'), 12 blog2 = new Blog('jb51', 'http://www.jb51.net/'); 13 14 blog.alertUrl(); // http://sc.jb51.net/ 15 blog2.alertUrl(); // http://www.jb51.net/
我们将alertUrl设置成全局函数,这样一来blog与blog2访问的都是同一个函数。可是问题又来了,在全局作用域中定义了一个实际只想让Blog使用的函数,显示让全局作用域有些名副其实,更让人无法接受的是在全局作用域中定义了许多仅供特定对象使用的方法,浪费空间不说,显然失去了面向对象封装性了,因此可以通过原型模式来解决此问题。
原型模式
1 function Blog() {} 2 3 Blog.prototype.name = 'wuyuchang'; 4 Blog.prototype.url = 'http://tools.jb51.net/'; 5 Blog.prototype.friend = ['fr1', 'fr2', 'fr3', 'fr4']; 6 Blog.prototype.alertInfo = function() { 7 alert(this.name + this.url + this.friend ); 8 } 9 10 // 以下为测试代码 11 var blog = new Blog(), 12 blog2 = new Blog(); 13 14 blog.alertInfo(); //wuyuchanghttp://tools.jb51.net/fr1,fr2,fr3,fr4 15 blog2.alertInfo(); //wuyuchanghttp://tools.jb51.net/fr1,fr2,fr3,fr4 16 17 blog.name = 'wyc1'; 18 blog.url = 'http://***.com'; 19 blog.friend.pop(); 20 blog2.name = 'wyc2'; 21 blog2.url = 'http://+++.com'; 22 blog.alertInfo(); // wyc1http://***.comfr1,fr2,fr3 23 blog2.alertInfo(); // wyc2http://+++.comfr1,fr2,fr3
优点:我们创建的每个函数都有prototype(原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。使用原型对象的好处就是可以让所有对象实例共享它所包含的属性及方法。
缺点:首先,它省略了构造函数传递初始化参数这一环节,结果所有实例在默认情况下都取得了相同的属性值,这样非常不方便。但是,原型模式的最大问题在于共享的本性所导致的,由于共享,一个实例修改了引用,另一个也随之更改了引用。因此我们通常不单独使用原型,而是结合原型模式与构造函数模式一起使用,即混合模式。
混合模式(原型模式 + 构造函数模式)
1 function Blog(name, url, friend) { 2 this.name = name; 3 this.url = url; 4 this.friend = friend; 5 } 6 7 Blog.prototype.alertInfo = function() { 8 alert(this.name + this.url + this.friend); 9 } 10 11 var blog = new Blog('wuyuchang', 'http://tools.jb51.net/', ['fr1', 'fr2', 'fr3']), 12 blog2 = new Blog('wyc', 'http://**.com', ['a', 'b']); 13 14 blog.friend.pop(); 15 blog.alertInfo(); // wuyuchanghttp://tools.jb51.net/fr1,fr2 16 blog2.alertInfo(); // wychttp://**.coma,b
优点:混合模式中构造函数模式用于定义实例属性,而原型模式用于定义方法和共享属性。每个实例都会有自己的一份实例属性,但同时又共享着方法,最大限度的节省了内存。另外这种模式还支持传递初始参数。优点甚多,在ECMAScript中是使用最广泛、认同度最高的一种创建自定义对象的方法。
动态原型模式
1 function Blog(name, url) { 2 this.name = name; 3 this.url = url; 4 5 if (typeof this.alertInfo != 'function') { 6 // 这段代码只执行了一次 7 alert('exe time'); 8 Blog.prototype.alertInfo = function() { 9 alert(thia.name + this.url); 10 } 11 } 12 } 13 14 var blog = new Blog('wuyuchang', 'http://tools.jb51.net'), 15 blog2 = new Blog('wyc', 'http:***.com');
动态原型模式将所有信息封装在了构造函数中,而通过构造函数中初始化原型(仅第一个对象实例化时初始化原型),这个可以通过判断该方法是否有效而选择是否需要初始化原型。可以看到上面的例子中只弹出一次窗,'exe time',即当blog初始化时,这样做blog2就不在需要初始化原型。这种模式创建对象,可以算是对原型模式的一种优化。
文章学习自http://www.jb51.net/article/53823.htm。有删改。
最终参考是来自于《JavaScript高级程序设计》一书。