浅析JavaScript的原型(prototype)(一)

一. 得到javaScript对象的四种方式的比较

第一种方式:通过new Object得到:没有类的约束,无法实现对象的重复使用,过于繁琐

var person=new Object();
person.name="花花";
person.age=17;
person.say=function(){ 
alert("姓名:"+this.name+",年龄:"+this.age);  //必须加this,指向person对象所定义的属性
};
person.say();
alert(typeof person); 

第二种方式:使用json得到:xml和json的数据可以辅助我们在网络中传输对象数据,但依旧无法实现对对象的重用

var person={
name:"花花",
age:16,
say:function(){
alert(this.name+this.age)
}
};
person.say();

第三种方式:使用工厂模式得到:解决了对象无法重用的问题,但是无法判断的到的对象的类型

function creatPerson(name,age){ //在方法中定义一个对象,将传递进来的属性赋给这个对象
var p=new Object();
p.name=name;
p.age=age;
p.say=function(){
alert(this.name+this.age);
} 
return p;
}
//使用工厂模式的定义方法,有效的解决了对象无法重用的问题
var p1=creatPerson("花花",16);
p1.say();
var p2=creatPerson("小草",16);
p2.say();

第四种方式:使用构造函数来创建一个对象:这种方式是JavaScript模拟其他面向对象语言的方式来实现对象的创建  对象重复使用,可判断类型

//构造构造函数的创建对象的方式和基于工厂的方式类似
//最大区别:函数的名称就是类的名称,按面向对象潜规则,首字母大写,表示这是
 一个构造函数

function Person(name,age){
this.name=name;  //this.name(变量的属性)=name(传递的形参)
this.age=age;
this.say=function(){
alert("我的名字:"+this.name+",我的年龄"+this.age);
}
}
var p1=new Person("花花",18);
p1.say();
alert(p1 instanceof Person); //true 使用构造函数的好处就是可以使用instanceof来判断这个对象的类型 

基于构造函数的定义对象的方式存在问题,通过分析:say方法在每个对象创建后都存在一个方法拷贝,(但代码在只有调用时,say方法才会在堆中创建),增加内存损耗

解决这个问题的方法:将方法放到全局函数,这样所有的对象都指向了一个方法

function Person(name,age){
this.name=name;  //this.name(变量的属性)=name(传递的形参)
this.age=age;
this.say=say; //让方法成为全局函数
} 
function say(){
alert("我的名字:"+this.name+",我的年龄"+this.age);
}
var p1=new Person("花花",18);
p1.say();

问题:定义为全局函数,window对象就可调用,破坏了对象的封装性,导致全局变量污染,JavaScript提供了一种解决方案,就是基于原型的对象创建方案

二.javaScript的原型(prototype)

1.浅析原型

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

2.用原型创建对象

//定义一个对象
function Person(){};
//使用原型来给对象赋值,将一个对象的属性和方法放在该对象的原型中,外界无法访问
Person.prototype.name="xiaoming";
Person.prototype.age=20;
Person.prototype.say=function(){
alert(this.name+this.age);
}
var p1=new Person();
alert(p1.constructor==Person);//true
p1.say();//正常访问
say();//报错  window无法访问,say方法只属于Person对象独有的方法,很好的解决封装破坏的情况

原型的内存模型图如下:


3.常见的原型检测方式:

function Cat(){};
Cat.prototype.name="花猫";
Cat.prototype.say=function(){
console.log(this.name+"爱吃鱼");
}
var p1=new Cat();
var p2=new Cat();
p2.name="黑猫";
p2.say();
alert(Cat.prototype.isPrototypeOf(p1));//检测p1是不是指向Cat的原型对象
alert(p1.constructor==Cat);//检测p1的构造器是不是指向Cat对象
alert(p2.hasOwnProperty("name"));//检测p2中有没有自己的属性
delete p2.name;
alert("name" in p2);
//虽然删除了自己的name属性,但是原型中有
//若检测只在原型中,不在自己种的属性
function isPrototypeProperty(property,obj){
if(!obj.hasOwnProperty(property)){
if(property in obj){
return true;
}
}
return false;
}
alert(isPrototypeProperty("name",p1));

三.原型重写

//定义一个对象
function Person(){};
Person.prototype.name="xiaoming";
Person.prototype.age=20;
Person.prototype.say=function(){
alert(this.name+this.age);
}
var p1=new Person();
alert(p1.constructor==Person);//true
p1.say();//正常访问
Person.prototype={
name:"huahua",
age:18,
say:function(){
alert(this.name+this.age);
}
}
//此时p1的构造器不再指向p1,而是指向Object,原型重写导致Person原型被覆盖
var p1=new Person();
p1.say();//
alert(p1.constructor==Person);//false

//因此我们需要手动指向  在JSON中添加constructor:Person
//原型重写  JSON
//注意原型重写时,要紧跟在构造函数之后,因为当有对象指向原先的原型对象时,原先的原型对象不会被释放

四.封装--原型创建对象

因为原型存在,我们实现了对象的封装,但是这种封装也同样可能存在问题的。
1、 我们无法像使用构造函数的那样将属性传递用于设置值
2、 当属性中有引用类型,可能存在变量值的重复

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

3.终极方案--基于组合的对象定义:需要通过组合的封装构造函数和原型来实现对象的创建

是目前最常用的一种方式

//基于组合的对象定义
//即属性在构造方法中定义,方法在原型中定义

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);

4.终极方案--基于动态原型的对象定义

因为一些面向对象的程序员(如:java、c#)等开发人员他们认为将方法放在外面不像面向对象的写法,所以提供了另一种写法,在
这里说说,
供参考:

//基于动态原型的对象定义
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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值