JS面向对象基础讲解(工厂模式、构造函数模式、原型模式、混合模式、动态原型模式)
什么是面向对象?面向对象是一种思想.
面向对象可以把程序中的关键模块都视为对象, 而模块拥有属性及方法. 这样如果我们把一些属性及方法封装起来,日后使用将非常方便,也可以避免繁琐重复的工作.
工厂模式
工厂模式是软件工程领域中一种广为人知的设计模式,由于ECMAScript无法创建类, 因此用函数封装以特定的接口创建对象.其实现方法非常简单,也就是在函数内创建一个对象,在给对象赋予属性及方法再将对象返回即可.
1 /** 2 * 工厂模式 3 * 优点:解决了创造对象的问题 4 * 缺点:没有解决对象的识别问题 instanceof -> Object 5 * */ 6 function createPerson(name, age, job){ 7 var o = new Object(); 8 o.name = name; 9 o.age = age; 10 o.job = job; 11 o.sayName = function(){ 12 console.log(this.name); 13 }; 14 return o; 15 } 16 17 var person1 = createPerson("xialiu", 24, "shijiazhuang"); 18 var person2 = createPerson("xiaobei", 22, "hainan"); 19 console.log(person1); 20 console.log(person2);
可以看到工厂模式的实现方法非常简单,解决了创建多个相似对象的问题.但是工程模式缺无从识别对象的类型,因为全部都是Object, 不像Date, Array等,因此出现了构造函数模式.
构造函数模式
ECMAScript中构造函数可以创建特定类型的对象, 类似于Array, Date等原生JS的对象.其实现方法如下:
1 /** 2 * 构造函数模式 3 * 优点:解决对象识别的问题 4 * 缺点:不同实例上的同名函数是不相等的.有this对象在,没有必要再执行代码前把函数绑定到特定对象上来. 5 * */ 6 function Blog(name,url){ 7 this.name = name; 8 this.url =url; 9 this.alerturl = function () { 10 console.log(alerturl); 11 } 12 } 13 var blog = new Blog("xiaomeng","HTTP://www.baidu.com/"); 14 console.log(blog instanceof Blog);//true, 判断blog是否是Blog的实例,即解决了工厂模式中不能 15 console.log(blog);
这个例子和工厂模式中除了函数名不同以外,细心的同学应该发现许多不同之处.
函数名手写字母大写---虽然标准没有严格规定手写字母大写,但按照惯例,构造函数的首写字母用大写.
没有先时创建对象
直接将属性和方法赋给this对象
没有return语句
使用new创建对象
能够识别对象, 这是构造函数模式胜于工厂模式的地方
构造函数虽然好用,但也并非没有缺点,使用构造函数的最大问题在于每次创建实例的时候都要重新创建一次方法(理论上每次创建对象的时候对象的属性均不同,而对象的方法是相同的), 然而创建两次完全相同的方法是没有必要的,因此,我们可以将函数移到对象外面.
1 function Blog(name, url) { 2 this.name = name; 3 this.url = url; 4 this.alertUrl = alertUrl; 5 } 6 function alertUrl() { 7 console.log(this.url); 8 } 9 var blog = new Blog("xiaokeai","http://www.xiaokeai.com/"), blog2 = new Blog("xiaomeinv","http://www.123.com/"); 10 blog.alertUrl(); 11 blog2.alertUrl();
我们将alertUrl设置成全局函数,这样一来blog和blog2访问的都是同一个函数了,可是问题又来了,在全局作用域中定义一个实际只想让blog使用的函数,显示让全局作用域有些名副其实,更让人无法接受的是在全局作用域中定义了许多仅供特定对象使用的方法,浪费空间不说,显然失去了面向对象的封装性了,因此可以通过原型来解决这个问题.
原型模式
我们创建的每个函数都有prototype属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法.使用原型对象的好处就是可以让所有对象实例共享它所包含的属性和方法.
1 /** 2 * 原型模式 3 * */ 4 function Blog(){ 5 Blog.prototype.name = "xiaomeiren"; 6 Blog.prototype.url = "http://www.xiaomeiren.com/"; 7 Blog.prototype.friend = ["aaa", "bbb", "ccc", "ddd", "eee"]; 8 Blog.prototype.alertInfo = function () { 9 console.log(this.name + ' ' + this.url + ' ' + this.friend); 10 } 11 } 12 var blog = new Blog(), blog2 = new Blog(); 13 blog.alertInfo(); //"xiaomeiren http://www.xiaomeiren.com/ aaa,bbb,ccc,ddd,eee" 14 blog2.alertInfo(); //"xiaomeiren http://www.xiaomeiren.com/ aaa,bbb,ccc,ddd,eee" 15 16 blog.name = "xiaomeiyun"; 17 blog.url = "http://www.xiaomeiyun.com/"; 18 blog.friend.pop(); 19 20 blog2.name = "xiaomeiyun2"; 21 blog2.url = "http://www.xiaomeiyun2.com/"; 22 23 blog.alertInfo(); //"xiaomeiyun http://www.xiaomeiyun.com/ aaa,bbb,ccc,ddd" 24 blog2.alertInfo(); //"xiaomeiyun2 http://www.xiaomeiyun2.com/ aaa,bbb,ccc,ddd"
原型模式也不是没有缺点,首先,它忽略了构造函数传递初始化参数这一环节,结果所有实例在默认情况下都取得了相同的属性值,这样非常不方便,但这还不是原型的最大问题.原型模式最大问题在于共享的本性所导致的,由于共享,因此一个实例修改了引用,另一个也随之修改了引用.因此我们通常不单独使用原型,而是结合原型模式和构造函数模式.
混合模式
混合模式是原型模式加上构造函数模式
1 /** 2 * 混合模式 = 原型模式 + 构造函数模式 3 * */ 4 function Blog(name, url, friend ) { 5 this.name = name; 6 this.url = url; 7 this.friend = friend; 8 } 9 10 Blog.prototype.alertInfo = function () { 11 console.log('名字:'+this.name + ' 博客:' + this.url + ' 好友:' + this.friend); 12 } 13 14 var blog = new Blog("LiMing","http://www.limingweb.com/",["aaa", "bbb","ccc", "ddd"]), 15 blog2 = new Blog("LiMingming","http://www.limingmingweb.com/",["aaa", "bbb","ccc", "ddd"]); 16 blog.friend.pop();//arrayObject.pop();方法用于删除并返回数组的最后一个元素,并且数组长度减1. 17 blog.alertInfo();//名字:LiMing 博客:http://www.limingweb.com/ 好友:aaa,bbb,ccc 18 blog2.alertInfo();//名字:LiMingming 博客:http://www.limingmingweb.com/ 好友:aaa,bbb,ccc,ddd
混合模式中构造函数模式用于定义实例属性,而原型模式用于定义方法和共享属性. 每个实例都会有自己的一份实例属性,但同时又共享这方法,最大限度的节省了内存.另外这种模式还支持传递初始数据.优点胜多,这种模式使用广泛,认同度最高的一种创建自定义对象的方法.
动态原型模式
动态原型模式将所有信息封装在了构造函数中,而通过构造函数中初始化原型,这个可以通过判断该方法是否有效而选择是否需要初始化原型.
1 function Blog(name, url) { 2 this.name = name; 3 this.url = url; 4 if(typeof this.alertInfo != "function"){ 5 //这段代码只执行一次 6 console.log("haha"); 7 Blog.prototype.alertInfo = function () { 8 console.log('名字:'+this.name + ' 博客:' + this.url); 9 } 10 } 11 } 12 var blog = new Blog("LiMing","http://www.limingweb.com/"), 13 blog2 = new Blog("LiMingming","http://www.limingmingweb.com/");
运行效果;haha
即当blog初始化时,这样做blog2就不再需要初始化原型,对于这种模式创建对象,已经是很完美了.