对象(类)的封装方式(面向对象的js基本知识)
上一个月一直忙于项目,没写过笔记,今天稍微空下来了一点
前几天在写项目的时候关于怎么去封装每一个组件的时候思考到几种方式,这里总结一下:
1、构造函数方式(类似java写类的方式):把所有的属性和方法全部挂在构造函数内部的this上:
1 function Textarea(opts) { 2 this.defaults = { 3 //...这是一些默认属性 4 }; 5 this.options = $.extend(true, {}, this.defaults, opts); 6 this.creatDom = function() { 7 //...这里面很大一堆代码 8 }; 9 this.setVal = function() { 10 //...这是很大一堆代码 11 }; 12 this.getVal = function() { 13 //...这是很大一堆代码 14 }; 15 }
这种方法的优点是代码比较紧凑,比较好理解和代码的管理,但是这样我们每次实例化一个对象的时候都会新开一段内存来存储这个对象(包括方法和属性)
我们改改:
1 function Textarea(opts) { 2 this.defaults = { 3 //...这是一些默认属性 4 }; 5 this.options = $.extend(true, {}, this.defaults, opts);//这是覆盖以后的属性 6 this.creatDom = creatDom; 7 this.setVal = setVal; 8 this.getVal = getVal; 9 } 10 function creatDom() { 11 //...这是很大一堆代码 12 } 13 function setVal() { 14 //...这是很大一堆代码 15 } 16 function getVal() { 17 //...这是很大一堆代码 18 }
这样会节约一些内存:但是牺牲了代码的紧凑度;
我们可以知道,每次实例化对象的时候其实三个方法其实是公用的,所以出现了第二种方式去定义对象
2、原型式写法:把属性和方法都挂在到对象的原型上:
1 function Textarea() {} 2 Textarea.prototype.defaults = { 3 //... 4 }; 5 Textarea.prototype.options = $.extend(true, {}, this.defaults); 6 Textarea.prototype.creatDom = function() { 7 //... 8 }; 9 Textarea.prototype.setVal = function() { 10 //... 11 }; 12 Textarea.prototype.getVal = function() { 13 //... 14 }; 15 //当然也可以把所有属性和方法放在对象上面然后再挂在prototype上: 16 function Textarea() {} 17 Textarea.prototype = { 18 defaults : {}, 19 options : $.extend(true, {}, this.defaults), 20 creatDom : function() {}, 21 setVal : function() {}, 22 getVal : function() {} 23 };
但是这种方法是不可取的,因为这样不能通过参数来构造对象实例(一般每个对象的属性是不相同的);就像fe里面每个人名字不一致,但都能吃能笑能写代码;
举个简单的例子:
1 function Person(){} 2 Person.prototype.name = "winter"; 3 Person.prototype.getName = function() { return this.name;} 4 new Person("summer").getName();//得到的还是winter的大名;
3、构造函数+原型结合的方式:
1 function Textarea(opts) { 2 this.defaults = {}; 3 this.option = $.extend(true, {}, this.defaults, opts); 4 } 5 Textarea.prototype = { 6 creatDom : function() {}, 7 setVal : function() {}, 8 getVal : function() {} 9 };
看看这种方式是不是很完美...
貌似还不完美,这样Textarea.prototype还是在外面,
我们把它也拿到对象里面去:如下
1 function Textarea(opts) { 2 this.defaults = {}; 3 this.option = $.extend(true, {}, this.defaults, opts); 4 Textarea.prototype = { 5 creatDom : function() {}, 6 setVal : function() {}, 7 getVal : function() {} 8 }; 9 }
现在看似好了,但是我们每次实例化的时候会发生什么?
都会去执行这对代码:
1 Textarea.prototype = { 2 creatDom : function() {}, 3 setVal : function() {}, 4 getVal : function() {} 5 };
所以我们再做一个步骤,就是让它只执行一次:
1 function Textarea(opts) { 2 this.defaults = {}; 3 this.option = $.extend(true, {}, this.defaults, opts); 4 if(!!this.init){ 5 Textarea.prototype = { 6 creatDom : function() {}, 7 setVal : function() {}, 8 getVal : function() {} 9 }; 10 this.init = true; 11 } 12 }
OK!一般说来公共属性写在prototype下,需要在实例化的时候改变的属性写在this下
另外我们在实例化对象的时候(new)的时候需要在new的过程中就执行初始化函数:
比如上面的例子我们在var textarea = new Textarea(opts);的时候就想把creatDom函数执行了而不是new Textarea(opts).creatDom();
这时候我们就需要这样:
1 function Textarea(opts) { 2 this.defaults = {}; 3 this.option = $.extend(true, {}, this.defaults, opts); 4 this.creatDom(this.options); 5 } 6 Textarea.prototype = { 7 creatDom : function() {}, 8 setVal : function() {}, 9 getVal : function() {} 10 };
号外:构造函数下面的this
1 var die = BlackBerry; 2 function Phone(){ 3 console.log(this.die); 4 } 5 Phone()//BlackBerry 6 var die = BlackBerry; 7 function Phone(){ 8 this.die = "Nokia"; 9 console.log(this.die); 10 } 11 Phone()//Nokia
构造函数直接调用会把this当作全局window
所以以原型方式写出的对象中没有挂在this下面,直接调用构造函数将不会有这些属性和方法
如果以构造函数的方式生成对象在构造函数直接调用的时候将在window下生成相应的对象和方法
原型下面的this
1 function Phone() {} 2 Phone.prototype = { 3 name : "Nokia", 4 init : function() { 5 this.name = "BlackBerry"; 6 } 7 }; 8 var myPhone = new Phone(); 9 myPhone.init(); 10 console.log(myPhone.name);//BlackBerry 11 console.log(Phone.prototype.name);//Nokia
init里面的this指的是什么?Phone.prototype?
这里的this指的是调用的实例化对象myPhone;而不是原型对象,看看下面:
console.log(myPhone.hasOwnProperty("name"));//true
这里为true的原因是在调用myPhone.init()是给实例化对象添加乐一个name属性
console.log(myPhone.hasOwnProperty("init"));//false
这里为false很明显,因为start是原型上面的属性而非实例化对象的属性
我差点被骗了:我以为在代码执行的时候myPhone.init();this.name = "BlackBerry";会覆盖Phone.prototype下面的那么属性