一. 得到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 中面向对象的封装。