首页 > 编程语言 > 详细

web前端学习(二) javascript对象和原型继承

时间:2015-12-04 22:41:26      阅读:254      评论:0      收藏:0      [点我收藏+]
(1)javascript对象
    在JS中,对象是已命名的数据的集合。这些已命名的数据作为对象的属性来引用,如果一个函数值存储在某个对象的属性中,那么那个函数通常被称为方法。一般来说,对象是可以通过直接量方式或者构造函数方式创建的。
   从文字理解起来有点困难,那么下面我用代码的方式更直观的展示下。
  直接量方式:
var someone = {                //对象
    name:"Tom",                //属性
    age : 18,
    work:"student",
    showInfo:function(){       //方法
        document.write(this.name+‘ ‘+this.age+‘ ‘+this.work+‘<br />‘);
    }
}
someone.showInfo();            //Tom 18 student

 通过输出很直接的看出someone拥有name,age和work三种属性。

   构造函数方式:

function CreatPerson(name, age, work)                  //构造函数
{
    this.name = name;
    this.age  = age;
    this.work = work;

    this.showInfo = function(){
          document.write(this.name+‘ ‘+this.age+‘ ‘+this.work+‘<br />‘);
    }
} 
var someone = new CreatPerson("Tom", 18, "student"); //实例对象
someone.showInfo();                                  //Tom 18 student

  综合两种输出结果,可以确定两种方式都创建一个相同对象someone, 他们 都包含相同的属性name, age, work, 和方法showInfo。不过从功能上来说,构造函数方式可以重复利用,对于多变量创建时比直接量方式更为方便快捷,但是这种方式有个弊病,所有由构造函数实例成的对象都会继承所有的属性(即使实例对象不需要该属性), 影响效率,为了解决这一问题,js引入了对象继承,允许一个对象继承原型对象的属性,下面就开始进入本篇的核心。

(2)原型继承
   javaScript中每个对象都有原型对象,原型对象的所有属性都是以它为原型的对象的属性,也就是说,每个对象都继承原型对象的所有属性。讲到这里,你可能认为那和上面直接构造有什么区别?JS中所有函数都有Prototype属性,它引用一个对象,你在其中定义的任何属性都会被该构造函数创建的对象继承,特别注意一点:被创建对象的继承是在查询时自动发生的,而并非直接复制,这样就大量减少了内存占有。如上showInfo 不一定每个对象都需要,就可以提取出来,直到被创建对象调用到该属性才生成该属性。
function CreatPerson(name, age, work)
{
    this.name = name;
    this.age  = age;
    this.work = work;
}
CreatPerson.prototype.showInfo = function(){    //将函数添加为对象的原型属性
    document.write(this.name+‘ ‘+this.age+‘ ‘+this.work+‘<br />‘);
}
var someone = new CreatPerson("Tom", 18, "student");
someone.showInfo();                        //Tom 18 student
  当然,模仿上面的代码,我们也可以给对象添加新的属性和方法:
function CreatPerson(name, age, work){
    this.name = name;
    this.age  = age;
    this.work = work;
}
 
CreatPerson.prototype.showInfo = function(){
    document.write(this.name+‘ ‘+this.age+‘ ‘+this.work+‘<br />‘);
}
//额外的属性和方法
CreatPerson.prototype.Extra = 0;
CreatPerson.prototype.showExtra = function(){
    document.write(this.Extra+‘<br />‘);
}
var someone = new CreatPerson("Tom", 18, "student");
someone.showInfo();                 //Tom 18 student   
someone.showExtra();                //0

   通过上面的输出结果,someone对象继承了CreatPerson的方法showInfo和showExtra,既满足了实例化属性的要求,又降低了内存占用,提高了效率。基础的原型继承就是这么简单,通过Prototype, 实例对象继承原构造函数的Prototype引用对象的全部属性。但是当我们需要添加新的方法和属性,又不想不改变原有属性和接口,该怎么处理呢?这里就要提到子类化继承模式。

   下面设计一个新的构造函数ExtraCreatPerson, 它继承了原有属性和方法,又在原有构造函数的基础上增加了habbit属性和showExtraInfo方法,从而实现对原有接口的扩展。

function CreatPerson(name, age, work){
    this.name = name;
    this.age  = age;
    this.work = work;
}
 
CreatPerson.prototype.showInfo = function(){
    document.write(this.name+‘ ‘+this.age+‘ ‘+this.work+‘<br />‘);
}
 
function ExtraCreatPerson(name, age, work, habit){
    CreatPerson.apply(this, arguments);
    this.habit = habit;
}
ExtraCreatPerson.prototype = new CreatPerson();
ExtraCreatPerson.prototype.constructor = ExtraCreatPerson;
ExtraCreatPerson.prototype.showExtraInfo = function(){
    document.write(this.habit+‘<br />‘);
}
 
var someone = new ExtraCreatPerson("Bob", 18, "student", "Sing");
someone.showInfo();        //Bob 18 student
someone.showExtraInfo();    //Sing

 

 

  通过子类继承我们实现了预期,在原有基础上扩展了属性,不过子类继承创建时调用了CreatPerson.applynew CreatPerson(), 也就是创建了两次父对象,这又是浪费时间的,怎么解决呢,我们可以让子类共享父类中的prototype属性,这样就不需两处创建了,实现如下:

function CreatPerson(name, age, work){
    this.name = name;
    this.age  = age;
    this.work = work;
}
 
CreatPerson.prototype.showInfo = function(){
    document.write(this.name+‘ ‘+this.age+‘ ‘+this.work+‘<br />‘);
}
 
function ExtraCreatPerson(name, age, work, habit){
    CreatPerson.apply(this, arguments);
    this.habit = habit;
}
ExtraCreatPerson.prototype = CreatePerson.prototype;
ExtraCreatPerson.prototype.constructor = ExtraCreatPerson;
ExtraCreatPerson.prototype.showExtraInfo = function(){
    document.write(this.habit+‘<br />‘);
}
 
var someone = new ExtraCreatPerson("Bob", 18, "student", "Sing");
someone.showInfo();               //Bob 18 student         
someone.showExtraInfo();          //Sing

  理解到这里,应该对原型继承有比较清晰的认识,实例对象继承构造函数的prototype属性,子构造函数继承父对象的prototype属性,不过在原有代码基础上添加:

ExtraCreatPerson.prototype.showExtraInfo = function(){
    document.write(this.name+‘<br />‘);
}
var other = new CreatPerson("Tom", 22, "engineer");
other.showExtraInfo();        //Tom
someone.showExtraInfo();      //Bob
  看到这个输出,你是不是已经发现问题了? Javascript的每个类都有一个原型对象,这个原型对象都具有一套属性,每个对象都能继承原型的所有属性,这是上面提到的继承的实现过程,但是按照直接共享原型属性的方法,子类原型属性的添加会影响到父类的原型属性(如本例中由父构造函数创建的对象someone也获得了showExtraInfo方法,这是违背原型继承不改变原构造函数属性和方法的初衷,不过利用空的临时构造函数搭建桥梁,用来实现继承的转移,即没有改变原有属性,也没有增加额外的空间,实现如下:
function CreatPerson(name, age, work){
    this.name = name;
    this.age  = age;
    this.work = work;
}
 
CreatPerson.prototype.showInfo = function(){
    document.write(this.name+‘ ‘+this.age+‘ ‘+this.work+‘<br />‘);
}
 
function ExtraCreatPerson(name, age, work, habit){
    CreatPerson.apply(this, arguments);
    this.habit = habit;
}
 
function F(){
}
F.prototype = CreatPerson.prototype;
 
ExtraCreatPerson.prototype = new F();
ExtraCreatPerson.prototype.constructor = ExtraCreatPerson;
ExtraCreatPerson.prototype.showExtraInfo = function(){
    document.write(this.habit+‘<br />‘);
}
var someone = new ExtraCreatPerson("Bob", 18, "student", "Sing");
someone.showInfo();                   //Bob 18 student
someone.showExtraInfo();              //Sing
 
ExtraCreatPerson.prototype.showExtraInfo = function(){
    document.write(this.name+‘<br />‘);
}
var other = new CreatPerson("Tom", 22, "engineer");
someone.showExtraInfo();                    //Bob
other.showExtraInfo();                      //error: other.showExtraInfo is not a function
  隔离后,other就不具备子继承添加的方法,符合预期,是不是松了一口气,记的在本小节第一段代码中的CreatPerson.prototype.Extra = 0;属性吗,看下面的代码:
function CreatPerson(name, age, work){
    this.name = name;
    this.age  = age;
    this.work = work;
}
 
CreatPerson.prototype.Extra = {num:0};
CreatPerson.prototype.Extradata = 0;
CreatPerson.prototype.showExtra = function(){
    document.write(this.Extradata+ " " + this.Extra.num+‘<br />‘);
}
 
CreatPerson.prototype.showInfo = function(){
    document.write(this.name+‘ ‘+this.age+‘ ‘+this.work+‘<br />‘);
}
 
function ExtraCreatPerson(name, age, work, habit){
    CreatPerson.apply(this, arguments);
    this.habit = habit;
}
 
function F(){
 
}
F.prototype = CreatPerson.prototype;
 
ExtraCreatPerson.prototype = new F();
ExtraCreatPerson.prototype.constructor = ExtraCreatPerson;
ExtraCreatPerson.prototype.showExtraInfo = function(){
    document.write(this.habit+‘<br />‘);
}
 
var someone = new ExtraCreatPerson("Bob", 18, "student", "Sing");
var other = new CreatPerson("Tom", 22, "engineer");
 
other.showExtra();                  // 0 0
someone.showExtra();                // 0 0
someone.Extradata =      1;
someone.Extra.num =     2;
other.showExtra();                  // 0 2
someone.showExtra();                // 1 2

  通过观察输出,我们发现当原型继承的是属性是一个值的时候,隔离是成功的。这是由读写属性的不对称性决定的,在读对象的某属性时,JS首先检查是否具有该属性,如果没有就从原型读取,而写入属性时,并不使用原型对象,也就是说属性的继承只发生在读属性值时,写入属性时不会发生, 因此修改someone.Extradata的值并不会影响other.Extradata的值; 看到这你一定会说,如果按照这个原则,为什么上面的someone.Extra.num = 2;修改了other.Extra.num的值,我换个角度给你讲吧,other继承的实际上是other.Extra这个对象,也就是设置num的值理论上是先读取other.Extra的引用对象,在设置对象的属性num值,也就是从原型继承理论中读取的过程,修改了num其实就是修改了原型中指定的对象,又违背了初衷,不过随着一步步深入了解,是不是发现原型继承其实就是对原有构造函数的拷贝过程,不过我建议去理解下javascript浅拷贝和深拷贝,那么问题就解决了,代码如下:

//判断是对象还是数组,避免克隆后导致结构变化(数组->对象 对象<-数组)
function isClass(obj){
    if(obj == null) return "NULL";
    if(obj == undefined) return "Undefined";
    return Object.prototype.toString.call(obj).slice(8, -1);
}
 
function deepClone(obj){
    var result;
    var objClass = isClass(obj);
    //确定result的类型
    if(objClass==="Object"){
        result={};
    }else if(objClass==="Array"){
        result=[];
    }else{
        return obj;
    }
    for(key in obj){
        var copy=obj[key];
        if(isClass(copy)=="Object"){
            result[key]=arguments.callee(copy); //递归调用
        }else if(isClass(copy)=="Array"){
            result[key]=arguments.callee(copy);
        }else{
            result[key]=obj[key];
        }
    }
    return result;
}
 
function CreatPerson(name, age, work){
    this.name = name;
    this.age  = age;
    this.work = work;
}
 
CreatPerson.prototype.Extra = {num:0};
CreatPerson.prototype.Extradata = 0;
CreatPerson.prototype.showExtra = function(){
    document.write(this.Extradata+ " " + this.Extra.num+‘<br />‘);
}
 
CreatPerson.prototype.showInfo = function(){
    document.write(this.name+‘ ‘+this.age+‘ ‘+this.work+‘<br />‘);
}
 
function ExtraCreatPerson(name, age, work, habit){
    CreatPerson.apply(this, arguments);
    this.habit = habit;
}
 
ExtraCreatPerson.prototype = deepClone(CreatPerson.prototype);
ExtraCreatPerson.prototype.constructor = ExtraCreatPerson;
ExtraCreatPerson.prototype.showExtraInfo = function(){
    document.write(this.habit+‘<br />‘);
}
 
var someone = new ExtraCreatPerson("Bob", 18, "student", "Sing");
var other = new CreatPerson("Tom", 22, "engineer");
 
other.showExtra();             // 0 0
someone.showExtra();           // 0 0
someone.Extra.num = 2;
someone.Extradata = 1;
other.showExtra();              // 0 0
someone.showExtra();            // 1 2
other.showExtraInfo();          //error: other.showExtraInfo is not a function

  到了这,子类和父类实现了完全的隔离,可喜可贺!

   js原型继承的所有知识都讲完了,本文的编写参考<JavaScript权威指南>第三章和第八章,当然本文也可以作为了解这两个章节的实例,希望对大家有帮助。
    

web前端学习(二) javascript对象和原型继承

原文:http://www.cnblogs.com/zc110747/p/5020292.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!