什么是面向对象?面向对象是一种思想!(废话)。设计模式
面向对象能够把程序中的关键模块都视为对象,而模块拥有属性及方法。这样咱们若是把一些属性及方法封装起来,往后使用将很是方便,也能够避免繁琐重复的工做。接下来将为你们讲解在JS中面向对象的实现。函数
工厂模式测试
工厂模式是软件工程领域一种广为人知的设计模式,而因为在ECMAScript中没法建立类,所以用函数封装以特定接口建立对象。其实现方法很是简单,也就是在函数内建立一个对象,给对象赋予属性及方法再将对象返回便可。this
function createBlog(name, url) { var o = new Object(); o.name = name; o.url = url; o.sayUrl= function() { alert(this.url); } return o; } var blog1 = createBlog('wuyuchang', 'http://www.cnblogs.com/wuyuchang/');
能够看到工厂模式的实现方法很是简单,解决了建立多个类似对象的问题,可是工厂模式却无从识别对象的类型,由于所有都是Object,不像Date、Array等,所以出现了构造函数模式。url
构造函数模式spa
ECMAScript中构造函数能够建立特定类型的对象,相似于Array、Date等原生JS的对象。其实现方法以下:prototype
function Blog(name, url) { this.name = name; this.url = url; this.alertUrl = function() { alert(this.url); } } var blog = new Blog('wuyuchang', 'http://www.cnblogs.com/wuyuchang/'); console.log(blog instanceof Blog); // true, 判断blog是不是Blog的实例,即解决了工厂模式中不能
这个例子与工厂模式中除了函数名不一样之外,细心的童鞋应该发现许多不一样之处:设计
构造函数虽然好用,但也并不是没有缺点,使用构造函数的最大的问题在于每次建立实例的时候都要从新建立一次方法(理论上每次建立对象的时候对象的属性均不一样,而对象的方法是相同的),然而建立两次彻底相同的方法是没有必要的,所以,咱们能够将函数移到对象外面(也许有些童鞋已经看出缺点,嘘!)。指针
function Blog(name, url) { this.name = name; this.url = url; this.alertUrl = alertUrl; } function alertUrl() { alert(this.url); } var blog = new Blog('wuyuchang', 'http://www.cnblogs.com/wuyuchang/'), blog2 = new Blog('cnblogs', 'http://www.cnblogs.com/'); blog.alertUrl(); // http://www.cnblogs.com/wuyuchang/ blog2.alertUrl(); // http://www.cnblogs.com/
咱们将alertUrl设置成全局函数,这样一来blog与blog2访问的都是同一个函数,但是问题又来了,在全局做用域中定义了一个实际只想让Blog使用的函数,显示让全局做用域有些名副其实,更让人没法接受的是在全局做用域中定义了许多仅供特定对象使用的方法,浪费空间不说,显然失去了面向对象封装性了,所以能够经过原型来解决此问题。code
原型模式
咱们建立的每一个函数都有prototype(原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含能够由特定类型的全部实例共享的属性和方法。使用原型对象的好处就是可让全部对象实例共享它所包含的属性及方法。
function Blog() { } Blog.prototype.name = 'wuyuchang'; Blog.prototype.url = 'http://www.cnblogs.com/wuyuchang/'; Blog.prototype.friend = ['fr1', 'fr2', 'fr3', 'fr4']; Blog.prototype.alertInfo = function() { alert(this.name + this.url + this.friend ); } // 如下为测试代码 var blog = new Blog(), blog2 = new Blog(); blog.alertInfo(); // wuyuchanghttp://www.cnblogs.com/wuyuchang/fr1,fr2,fr3,fr4 blog2.alertInfo(); // wuyuchanghttp://www.cnblogs.com/wuyuchang/fr1,fr2,fr3,fr4 blog.name = 'wyc1'; blog.url = 'http://***.com'; blog.friend.pop(); blog2.name = 'wyc2'; blog2.url = 'http://+++.com'; blog.alertInfo(); // wyc1http://***.comfr1,fr2,fr3 blog2.alertInfo(); // wyc2http://+++.comfr1,fr2,fr3
原型模式也不是没有缺点,首先,它省略了构造函数传递初始化参数这一环节,结果全部实例在默认状况下都取得了相同的属性值,这样很是不方便,但这仍是不是原型的最大问题,原型模式的最大问题在于共享的本性所致使的,因为共享,所以所以一个实例修改了引用,另外一个也随之更改了引用。所以咱们一般不单独使用原型,而是结合原型模式与构造函数模式。
混合模式(原型模式 + 构造函数模式)
function Blog(name, url, friend) { this.name = name; this.url = url; this.friend = friend; } Blog.prototype.alertInfo = function() { alert(this.name + this.url + this.friend); } var blog = new Blog('wuyuchang', 'http://www.cnblogs.com/wuyuchang/', ['fr1', 'fr2', 'fr3']), blog2 = new Blog('wyc', 'http://**.com', ['a', 'b']); blog.friend.pop(); blog.alertInfo(); // wuyuchanghttp://www.cnblogs.com/wuyuchang/fr1,fr2 blog2.alertInfo(); // wychttp://**.coma,b
混合模式中构造函数模式用于定义实例属性,而原型模式用于定义方法和共享属性。每一个实例都会有本身的一份实例属性,但同时又共享着方法,最大限度的节省了内存。另外这种模式还支持传递初始参数。优势甚多。这种模式在ECMAScript中是使用最普遍、认同度最高的一种建立自定义对象的方法。
动态原型模式
动态原型模式将全部信息封装在了构造函数中,而经过构造函数中初始化原型(仅第一个对象实例化时初始化原型),这个能够经过判断该方法是否有效而选择是否须要初始化原型。
function Blog(name, url) { this.name = name; this.url = url; if (typeof this.alertInfo != 'function') { // 这段代码只执行了一次 alert('exe time'); Blog.prototype.alertInfo = function() { alert(thia.name + this.url); } } } var blog = new Blog('wuyuchang', 'http://www.cnblogs.com/wuyuchang'), blog2 = new Blog('wyc', 'http:***.com');
能够看到上面的例子中只弹出一次窗,'exe time',即当blog初始化时,这样作blog2就不在须要初始化原型,对于使用这种模式建立对象,能够算是perfect了。
此博文参考《JavaScript高级程序设计》第3版,但语言都通过简化,例子也重写过,若是有什么不懂的地方请留言回复,做者将更新博客。