JS 面向对象之封装

JS 是面向对象语言,提到面向对象,就不得不说它的三要素之封装。

原始模式

var cat1 = {name:"guaiguai",color:"白毛"}
var cat2 = {name:"huaihuai",color:"黑色"}

存在问题:

  1. 多生成几个实例,代码就会很冗余,写起来也很麻烦
  2. cat1 与 cat2 之间看不出来有啥关联

工厂模式

function createCat(name,color) {
	return {
		name:name,
		color:color
	}
}
var cat1 = createCat("guaiguai","白毛")
var cat2 = createCat("huaihuai","黑毛")

工厂模式存在的问题

  1. cat1 与 cat2 之间仍然看不出有啥联系。
  2. 存在浪费内存,何以见得呢
function createCat(name,color) {
	var obj = {}
	obj.name = name;
	obj.color = color;
	obj.type = "猫科动物"; //不变的属性
	obj.eat = function() {
		alert("吃老鼠")
	}
	return obj
}
var cat1 = createCat("guaiguai","白毛")
var cat2 = createCat("huaihuai","黑毛")
alert(cat1.type) //猫科动物
cat1.eat()
cat2.eat()
console.info(cat1.eat == cat2.eat) // false

功能是正常的,但对于每个实例,type 和 eat 方法其实都是一模一样的内容,但却每次都重新生成,这样多占用了内存,同时影响效率。
那有没办法让 type 和 eat 方法在内存中只生成一次呢?有如下两个方式:

公共属性抽离成全局

function eat() {
	alert("吃老鼠")
}
var typeName = "猫科动物"
function createCat(name,color) {
	var obj = {}
	obj.name = name;
	obj.color = color;
	obj.type = typeName; //不变的属性
	obj.eat = eat
	return obj
}
var cat1 = createCat("guaiguai","白毛")
var cat2 = createCat("huaihuai","黑毛")
alert(cat1.type) //猫科动物
cat1.eat()
cat2.eat()
console.info(cat1.eat == cat2.eat) // true

这样是可以实现 type 与 eat 方法的复用,但这样将变量与方法定义成全局,一来这个全局的变量或方法只是被某个对象调用,有点名不副实;二来如果要在对象中定义多个共享的属性,就得定义多个全局函数或变量

采用 prototype(原型模式)

createCat.prototype = {
	type:"猫科动物",
	eat:function() {
		alert("吃老鼠")
	}
}
function createCat(name,color) {
	var obj = {}
	obj.name = name;
	obj.color = color;
	// __proto__非标准用法
	obj.__proto__ = createCat.prototype 
	return obj
}
var cat1 = createCat("guaiguai","白毛")
var cat2 = createCat("huaihuai","黑毛")
alert(cat1.type) //猫科动物
cat1.eat()
cat2.eat()
console.info(cat1.eat == cat2.eat) // true
console.info(cat1 instanceof createCat) // true
console.info(cat2 instanceof createCat)// true

通过在函数的原型上设置属性的方式,来达到复用 type 和 eat 方法的效果。当访问 type
时,会先在实例对象自己身上查找,如果查找不到,就会到它的原型对象查找(即 createCat.prototype
),如果还是没有,就会继续往上找,直到找到 null 为止,如果仍然没有就会报 undefined,这就是原型链

构造函数模式

Cat.prototype = {
	type:"猫科动物",
	eat:function() {
		alert("吃老鼠")
	}
}
function Cat(name,color) {
	this.name = name;
	this.color = color;
}
var cat1 = new Cat("guaiguai","白毛")
var cat2 = new Cat("huaihuai","黑毛")
alert(cat1.type) //猫科动物
cat1.eat()
cat2.eat()
console.info(cat1.eat == cat2.eat) // true
console.info(cat1 instanceof Cat) // true
console.info(cat2 instanceof Cat) // true

使用 Cat 作为函数名,与跟使用 createCat 作为函数名作用是一样的,只是为了区分构造函数跟普通函数,构造函数始终都应该以一个大写字母开头

如上,只要在函数前面使用 new 关键字,那么就可以少做四件事情:

  1. 不用创建新对象(即var obj = {}),因为 new 会帮你做(在函数中你使用 this 就可以访问到该新对象obj)
  2. 不用绑定原型,因为 new 会帮你做
  3. 不用 return 新对象,因为 new 会帮你做

即 var cat1 = new Cat(“guaiguai”,“白毛”) 主要做了如下事情:

  1. 创建一个新对象 cat1
  2. 将构造函数的作用域赋给新对象cat1(因此 this 就指向了这个新对象)
  3. 执行构造函数中的代码(为这个新对象添加属性);
  4. 返回新对象。

说明 new 关键字也只是个语法糖

自己实现个 new 操作符

function create(Con,...args){
	var obj = {}
	// 将 obj 实例与 Con.prototype 关联起来,达到可以访问构造函数 Con 对应的原型链上的属性和方法
	Object.setPrototypeOf(obj,Con.prototype) //obj.__proto__ = Con.prototype
	// 执行 Con 构造函数中的代码,将 this 绑定为当前实例对象 obj ,并传入剩余的参数
	var result = Con.apply(obj,args) // var result = Con.call(obj,...args)
	// 如果返回的是引用类型的对象,则返回;否则默认返回当前实例对象obj
	return result instanceof Object ? result : obj
}

验证:

function Person(name,age){
	this.name = name;
	this.age = age;
}
Person.prototype.speak = function() {
	alert(this.name + "speak!")
}
var p = create(Person,"张三",3)
p.speak() //张三speak!
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值