封装——JavaScript的原型(prototype)

来源:互联网 发布:dota2比赛视频软件 编辑:程序博客网 时间:2024/05/16 09:08

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);//qqalert(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中面向对象的封装。将属性和方法封装所对应的对象中,其他对象无法得到和访问。

1 0