JS面向对象之封装
面向对象编程思想:
以事物为重心, 考虑的是一件事情的完成有哪些事物参与了, 重点在于完善每种事物. 然后各种事物项目配合完成这件事儿.
面向对象的编程语言:
Java, C#, Object-C, JavaScript...但是, JavaScript不是严格意义上面向对象的语言, 因为没有类的概念.
面向过程编程思想:
以事件为重心, 考虑的是一件事情的完成需要怎样的步骤, 每个步骤都需要怎么处理.
JS是基于对象的若类型语言
在基于类的面向对象方式中,对象(object)依靠类(class)来产生。 在基于原型的面向对象方式中,对象(object)则是依靠构造函数(constructor)和原型(prototype)构造出来的。
JS封装
面向对象语言的第一个特性毫无疑问是封装,在 JS 中,封装的过程就是把一些属性和方法放到对象中“包裹”起来,那么我们要怎么去封装属性和方法
对象字面量 --> object对象 --> 工厂模式 --> 构造函数 --> 原型模式 --> 构造函数+原型模
对象字面量
var dog = { // 属性 - 变量 color:'white', weight:'20kg', height:'0.6m', // 方法 - 函数 eat: function(){ return '我要eat饭'; }, sleep: function(){ console.log('我要睡觉'); } } // 调用对象 // 调属性 console.log(dog.color); // 调方法 var res = dog.eat(); console.log(res);
Object方式创建
var obj1 = new Object(); // 属性 obj1.color = 'white-2'; obj1.height = '0.6m'; // 方法 obj1.eat = function(){ console.log('我要eat饭-2'); } // 调用对象 // 调属性 console.log(obj1.color); var str = 'color'; console.log(obj1[str]); // 注意:对象中的调用,.后面不能跟变量,变量需要使用 []
- 优点:代码简单
- 缺点: 创建多个对象会产生大量的代码,编写麻烦,且并没有实例与原型的概念。
- 解决办法:工厂模式。
工厂模式
工厂模式是编程领域一种广为人知的设计模式,它抽象了创建具体对象的过程。JS 中创建一个函数,把创建新对象、添加对象属性、返回对象的过程放到这个函数中,用户只需调用函数来生成对象而无需关注对象创建细节,这叫工厂模式:
function createPerson(name='jack',age=18){ var person = new Object(); person.name = name; person.age = age; person.showName = function(){ console.log('我的名字是:' + this.name); } person.showAge = function(){ console.log('我的年龄是:' + this.age); } return person; // 对象 } var per = createPerson('xiaoming',33); // person
- 优点:工厂模式解决了对象字面量创建对象代码重复问题,创建相似对象可以使用同一API。
- 缺点:因为是调用函创建对象,无法识别对象的类型。
- 解决办法:构造函数
构造函数
function createPer(name='jack',age=18){ // var obj = new Object(); // 系统会自动创建 // var this = new Object(); this.name = name; this.age = age; this.showName = function(){ console.log('我的名字是:' + this.name); } this.showAge = function(){ console.log('我的年龄是:' + this.age); } // 省略:系统帮你自己返回 // return obj } // 创建对象 var person = new createPer('zhangsan',19);
通过构造函数new
一个实例经历了四步:
- 创建一个新对象;
- 将构造函数内的
this
绑定到新对象上; - 为新对象添加属性和方法;
- 返回新对象(JS 引擎会默认添加
return this;
)
而通过构造函数创建的对象都有一个constructor
属性,它是一个指向构造函数本身的指针,因此就可以检测对象的类型啦。:
alert(person.constructor === Person) //true alert(person instanceof Person) // true
但是仍然存在问题
alert(person.showName == person2.showName) //false
- 优点:解决了类似对象创建问题,且可以检测对象类型。
- 缺点:构造函数方法要在每个实例上新建一次。
- 解决办法:原型模式。
原型对象 + 构造对象
function newPerson(name='jack',age=18){ this.name = name; this.age = age; } // 原型方法 // newPerson.prototype. newPerson.prototype.showName = function(){ console.log('我的名字是:' + this.name); } newPerson.prototype.showAge = function(){ console.log('我的年龄是:' + this.age); } // 创建对象 var zhang = new newPerson('zhangsan',19); var peiqi = new newPerson('佩奇',2); zhang.showName(); peiqi.showName(); console.log(zhang.showName == peiqi.showName); // true // 构造函数 console.log(newPerson.prototype.constructor);
- 优点:与单纯使用构造函数不一样,原型对象中的方法不会在实例中重新创建一次,节约内存。
- 缺点:使用空构造函数,实例 person1 和 person2 的
name
都一样了,我们显然不希望所有实例属性方法都一样,它们还是要有自己独有的属性方法。并且如果原型中对象中有引用类型值,实例中获得的都是该值的引用,意味着一个实例修改了这个值,其他实例中的值都会相应改变。 - 解决办法:构造函数+原型模式组合使用。