封装——JavaScript的原型(prototype)

prototype,原型的初览

function Person(){
Person.prototype.name="小花";
Person.prototype.age=18;
Person.prototype.say=function(){alert("姓名:"+this.name+",年龄:"+this.age);
}
}
var p1=new Person();
p1.say();
say();//报错了

这样我们发现window就无法访问到say方法了,此时say方法只属于person对象独有的方法。很好的解决了封装破坏的情况。

什么是原型

上面我们烂了基于prototype创建对象的方式很好的解决了我们前面遇到的一系列问题,那么到底什么是原型,原型又是如何解决以上的问题的呢?我们下面来研究研究。

原型是js中非常特殊一个对象,当一个函数创建之后,会随之产生一个原型对象,当通过这个函数的构造函数创建了一个具体的对象之后,在这个具体的对象中就会有一个属性指向原型。这就是原型的概念。

鉴于原型的概念比较难以理解,我们就以上面的代码为例,画图为大家讲解。

第一步:function Person(){}之后,内存中创建了一个Person对象,有一个prototype属相,指向了Person对象的原型对象,而原型对象中存在了一个constructor的属性,指向了Person对象。



第三种状态:当根据Person构造函数创建一个对象后,该对象中存在一个_prop_的属性,也指向了Person对象的原型对象,当我们调用对象的属性或者方法时,首先在自己里面找,找不到的话,就会去Person对象的原型对象中找


原型的基本知识到这里也就差不多了,只有对上面的图和代码能够很好的理解,那么原型的理解就没问题,下面介绍几种原型的检测方式。

alert(Person.prototype.isprototypeof(p1))
//检测p1的构造函数是否指向Person对象
alert(p1.constructor==Person)
//检测某个属性是不是自己内存中的
alert(p1.hasOwnProperty("name"));




可以使用delete语句来删除我们赋予对象的自己属性(注意:原型中的是无法删除的)如:

delete p1.name;
p1.say();
alert(p1.hasOwnProperty("name"));

检测在某个对象自己或者对应的原型中是否存在某个属性。

alert("name" in p1);//true 
delete p2.name;//虽然删除了自己的name属性,但是原型中有
alert("name" in p2);//true 
//原型和自己中都有sex属性
alert("sex" in Person)//false

那么问题来了。如果检测只在原型中,不在自己的属性呢?

function hasPP(obj,prop){
	if(!obj.hasOwnProperty(prop)){
		if(prop in obj){
		return true;
	}
}
	return false;
}
alert(hasPP(p1,"name"));
alert(hasPP(p2,"name"));

原型重写

在上面的写法中,我们已经解决了大量的问题,使用原型。但是如果我们的对象中存在大量的属性或者方法的时候,使用上面的方式,感觉要写大量的【对象.prototype.属性名】这样的代码,感觉不是很好,那么我们可以使用JSON的方式来写:

function Person(){}
	Person.prototype={
	name:"刘帅哥";
	age:18;
	say:function(){
		alert("姓名:"+this.name+",年龄:"+this.age);
	}
}
var p1=new Person();
p1.say();
var p2=new Person();
p2.age=20;
p2.name="小明";
p2.say();

但是这种写法,我们是将该对象的原型覆盖(注意:这两种写法不一样的,第一种是扩充,第二种是覆盖),就会出现如下的问题:

function Person(){}
	Person.prototype={
	name:"刘帅哥";
	age:18;
	say:function(){
		alert("姓名:"+this.name+",年龄:"+this.age);
	}
}
var p1=new Person();
p1.say();
var p2=new Person();
p2.age=20;
p2.name="小明";
p2.say();
//此时p1的构造器不再指向Person,而是指向了Object
//因为我们覆盖了Person的原型,所以如果constructor比较重要的话,
//我们可以收到指向
alert(p1.constructor==Person)


此时就没有问题了。但是原型重写会给我们带来非常有趣的现象。

function Person(){}
var p1=new Person();
Person.prototype.sayHello=function(){
	alert("名字:"+this.name+",年龄:"+this.age);
}
Person.prototype={
constructor:Person,
name:"huahua",
age:18,
friends:["haha","xxx"],
say:function(){
	alert("名字:"+this.name+",年龄:"+this.age);
}
}
var p2=new Person();
p2.say();//正确
p1.sayHello();//此时找不到name和age,但是代码正确
p1.say();//错误,因为原型重写
p2.sayHello();//错误

这些代码要研究明白,必须配合之前原型的图来看,下面我画图来说明问题:


因为原型重写,需要大家根据原型的原理图来理解,原型的知识也就这些了。

封装——原型创建对象

因为原型存在,我们事先了对象的封装,但是这种封装也同样可能存在问题的。

1.我们无法像使用构造函数的那样将属性传递用于设置值

2.当属性中引用类型,可能存在变量值的重复

function Person(){};
Person.prototype={
constructor:Person,
name:"huahua",
age:18,
friends:["haha","xxx"],
say:function(){
alert(this.name+this.age+this.friends);
}
}
var p1=new Person();
p1.say();
var p2=new Person;
p2.name="xiaocao";
p2.say();
p1.friends.push("qq");
alert(p1.friends);//qq
alert(p2.friends);//qq  
//因为p1和p2对象都指向了同一个原型链,所以当p1属性值发生变化时,p2也变化,p2的输出也和p1一样

终极方案——基于组合的对象定义

为了解决原型所带来的问题,需要通过组合构造函数和原型来实现对象的创建将:属相在构造函数中定义,将方法在原型中定义。这种有效集合了两者的优点,是目前最为常用的一种方式。

//所以需要通过组合的封装构造函数和原型来实现对象的创建
//基于组合的对象定义
//即属性在构造方法中定义,方法在原型中定义
function Person(name,age,friends){
this.name=name;
this.age=age;
this.friends=friends;
}
//此时所有的属性都保存在自己的内存中
Person.prototype={
constructor:Person,
say:function(){
alert(this.name+this.age+this.friends);
}
}
//方法定义在原型中
var p1=new Person("花猫",19,["小花","小妹"]);
p1.friends.push("豆豆");
alert(p1.friends);
var p2=new Person("黑猫",19,["小草","小妹"]);
p2.friends.push("南瓜");
alert(p2.friends);


//基于动态原型的对象定义
function Person(name,age,friends){
this.name=name;
this.age=age;
this.friends=friends;
//判断不存在的时候写
//如果存在就不写,减少内存消耗
	if(!Person.prototype.say){
		Person.prototype.say=function(){
			alert(this.name+this.age+this.friends);
		}
	}
}
var p1=new Person("花猫",19,["小花","小妹"]);
p1.friends.push("豆豆");
alert(p1.friends);
var p2=new Person("黑猫",19,["小草","小妹"]);
p2.friends.push("南瓜");
alert(p2.friends);

JavaScript面向对象对应一个对象的方式,上述两种都行,根据个人习惯而定。这也是JavaScript中面向对象的封装。将属性和方法封装所对应的对象中,其他对象无法得到和访问。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值