JavaScript高级面向对象

JavaScript得到对象的回顾

在前面我们学习了 javascript 得到对象的几种方式,下面我们首先来回顾下我们学习的几种得到javascript 对象的方式有哪种:

第一种:通过new object得到

//第一种方式
var person=new Object();
person.age=18;
person.name=”孙利媛”;
person.say=function(){
//必须加this,指向person对象所定义的属性
alert(“我的名字是:”+this.name+”,我今年”+this.age+”岁了”);
}
person.say();

使用这种定义的方式,虽然可以定义一个对象,但是因为没有类的约束,所以无法实现对象的重复使用,如存在10个人,就要定义10 个person,太过于麻烦了,在操作过程中存在问题。 

第二种:使用json 得到

我们在编程中发现,当我们需要在网络中传输一个对象数据时,上面的方式无法让我们 去传输,我们知道网络中的数据是以字符串的形式传播的,所以XML 和json  的数据就可以辅助我们完成数据的传输。 

//第二种对象的实现
var person = {
name : “孙利媛”,
age : 18,
say : function() {
alert(“我的名字是:”+this.name+”,我今年”+this.age+”岁了”);
}
}
person.say();

 虽然json的方式也可以定义对象,但是它和new Object 一样存在了不能对象重用的缺陷,所以大家研究出了一种工厂方式定义一个对象,下面我们来使用工厂方式来实现一个对象的定义。 

第三种:使用工厂模式得到

因为上面两种方式定义对象无法让对象重复使用,所以在使用的过程中大家摸索出来一 种基于工厂模式的定义方式,如下所示:

//基于工厂模式的定义方式定义对象
//在一个方法中定义一个对象,将传递进来的
//属性赋给了这个对象
function createPerson(name,age) {
var p = new Object();
p.name = name;
p.age = age;
p.say = function() {
alert(this.name+this.age);
}
return p;
}
//使用工厂模式的定义方法,有效的解决了对象无法重用的问题
var p1 = createPerson(“孙利媛”,”18”);
p1.say();
var p2 = createPerson(“孙利”,”17”);
p2.say();

第四种:使用构造函数来创建一个对象

这种基于构造函数的创建方式,是javascript  模拟其他面向对象语言的方式来实现对象 的创建的。       
我们使用了工厂模式定义了对象,这样就很好的解决了对象无法重用的问题,但是此时 又存在了另一个问题,就是我们无法判断得到的对象的类型了,如typeof或者instanceof 来判断类型,仅仅得到一个Object 类型,所以就推出了基于构造函数的方式。

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

function Person(name,age) {
this.name = name;
this.age = age;
this.say = function() {
alert(“我的名字是:”+this.name+”,我的年龄是”+this.age);
}
}
var p1=new Person(“孙利媛”,”18”);
p1.say();
var p2=new Person(“孙利”,”17”);
p2.say();
alert(p1 instanceof Person);//可以使用instanceof来判断某个对象是否属于某种类

基于构造函数的定义的方式最大的好处除了对象重复使用外,就是让我们还可以判断它 的类型。 
此时我们发现基于构造函数的定义对象的方式看似已经很完美了,我们需要的问题它都 可以解决了,但是如果我们仔细的分析这段代码的话,就会发现这样的代码是存在问题的, 为什么呢? 
我们通过代码分析得知:say 方法在每个对象创建后都存在了一个方法拷贝 (但是我们 发现代码在只有调用时,say 方法才会在堆中创建),这样就增加了内存的消耗了,如果在对 象中有大量的方法时,内存的消耗就会高,这样不行了。解决这个问题的就是我们可以把这个方法放到全局函数,这样就所有的对象指向了一个方法。 

//解决方案就是将方法全部放在外面,成为全局函数
function Person(name,age) {
this.name = name;
this.age = age;

//可以将方法成为全局函数

this.say = say;
}
function say() {
alert(“我的名字是:”+this.name+”,我今年”+this.age+”岁了”);
}
var p1 = new Person(“孙利媛”,15);
p1.say();
var p2 = new Person(“孙利”,30);
p2.say();

这样写的话,会带来另一个问题,就是方法一点定义为全局函数,那么window 对 象就可以调用,这样就破坏了对象的封装性。而且如果有大量的方法,这样写导致整体代码充斥着大量的全局函数,这样将不利于开发。所以我们急需一种可以完美的解决上述问题的方案,javascript 给我们提供了一种解决这些问题的方案,就是基于原型的对象创建方案。 

封装–Javascript 的原型(prototype)

Prototype,原型的初览

以上方式在创建对象都不太理想,所以我们可以使用prototype 的方式来完成对象的创建。 
如何使用原型创建对象呢?首先写段代码让大家看看: 

function Cat() {};//定义了一个对象
//使用原型来给对象赋值
//这样就讲一个对象的属性和方法放在了该对象的原型中
//外界是无法访问到这样数据的
Cat.prototype.name=”小黑”;
Cat.prototype.color=”黑色”;
Cat.prototype.eat=function(){
alert(this.name+”是”+this.color+”的猫正在吃鱼”);
}
var p1 = new Cat();
p1.eat();
var p2 = new Cat();
p2.name=”小白”;
p2.color=”白色”;

// p2.eat();
eat()方法只属于Person 对象独有 的方法。很好的解决了封装破坏的情况。
//可以通过如下的方式检测p1是不是指向Person的原型对象
// alert(Cat.prototype.isPrototypeOf(p1))

什么是原型

上面我们看了基于 prototype 创建对象的方式很好的解决了我们前面遇到的一系列问题,那么到底什么是原型,原型又是如何解决如上的问题的呢?我们下面来研究研究。原型是 js中非常特殊一个对象,当一个函数创建之后,会随之就产生一个原型对象,当通过这个函数的构造函数创建了一个具体的对象之后,在这个具体的对象中就会有一个属性指向原型。这就是原型的概念。鉴于原型的概念比较难以理解,我们就以上面的代码为例,画图为大家讲解: 

原型的内存模型图如下:

常见的原型检测方式

可以通过如下的方式检测p1是不是指向Person的原型对象 

alert(Person.prototype.isPrototypeOf(p1))
//检测p1的构造器是否指向Person对象
alert(p1.constructor == Person)
//检测某个属性是不是自己内存中的
alert(p1.hasOwnProperty(“name”));
alert(p2.hasOwnProperty(“name”))

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

//可以使用delete语句删除对象中自己的属性,那么就会找到原型中的值
delete p2.name;
p2.say();
alert(p2.hasOwnProperty(“name”));

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

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

那么问题来了?如果检测只在原型中,不在自己中的属性呢?(提问)
//我们可以自己写代码来测试属性不在自己,在原型中
function hasPrototypeProperty(obj,prop) {
if (!obj.hasOwnProperty(prop)) {
if (prop in obj) {
return true;
}
}
return false;
}
alert(hasPrototypeProperty(p1,”name”));
alert(hasPrototypeProperty(p2,”name”));

原型重写

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

function Person(){};
Person.prototype = {
“name” : “张三”,
“age” : 18,
“say” : function(){
alert(“名字”+this.name+”年龄是”+this.age);
}
}
var p1=new Person();
p1.say();

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

function Person(){};
Person.prototype = {
“constructor” : “Person”,//手动指向Person
“name” : “张三”,
“age” : 18,
“say” : function(){
alert(“名字”+this.name+”年龄是”+this.age);
}
}
var p1=new Person();
p1.say();
//此时p1的构造器不在指向Person,而是指向了Object
//因为我们覆盖了Person的原型,所以如果constructor比较重要的话,
//我们可以收到指向
alert(p1.constructor == Person)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值