js 的原型链 以及伪继承

来源:互联网 发布:知乎注册要钱吗 编辑:程序博客网 时间:2024/06/10 05:46

代码都用关包的方式来显示(function(){//....code.....})();这是一个良好的习惯

1.名词解释

(function(){    function A(){        this.createId2=function(){            this.id2=2;        }    }    A.prototype.id=1;//通过静态方式创建静态成员    A.prototype.createId3=function(){        this.id3=3;    }    var a=new A();    a.createId2();//通过调用动态成员函数创建 动态成员    a.createId3();//通过静态函数创建 动态成员    a.id4=4;//直接创建动态成员})()

1.1构造函数:

在new 的时候被执行的函数;如:整个A就是一个构造函数

1.2 动态成员:

在new 之后创建的成员,包括构造函数中创建的,元素new之后赋值通过函数 如构造器中的var id0=0,this.id=5,执行a.createId4()时创建的id4,以及直接赋值创建的:a.id3=7;

1.3 静态成员:

使用prototype方式声明的成员,如 A.prototype.id_2=1;也就是保存在prototype中的成员

2.动态成员,静态成员的特性

2.1 删除成员

(function(){  funciton A(){    this.id=1;//创建一个动态id成员    delete this.id//删除动态成员  }  A.prototype.id_2=1;//创建一个静态成员  delete A.prototype.id_2;//删除一个静态成员})();

2.2 访问成员

2.2.1 优先级 动态成员>静态成员

(function(){    function A(){        console.log(this.id);//访问到静态成员        //--->2        this.id=1;        console.log(this.id);//访问到动态成员        delete this.id;        console.log(this.id);//动态属性被删除,则访问到静态成员        //-->2    }    A.prototype.id=2;    var a=new A();    a.id=1;    console.log(a.id);//获取到的是动态成员    //--->1    delete a.id;//删除动态属性id    console.log(a.id);//因为动态成员被删除 获取到的是静态成员    //--->2    delete a.id;//删除动态属性id,因为a.id已经被删除了所以这个操作无效    console.log(a.id);    //--->1    delete A.prototype.id;删除静态成员id    console.log(a.id);    //--->undefined;})();

2.2.2 外部创建

(function(){    function A(){        this.createId2=function(){            this.id2=2;        }    }    A.prototype.id=1;//通过静态方式创建静态成员    A.prototype.createId3=function(){        this.id3=3;    }    var a=new A();    a.createId2();//通过调用动态成员函数创建 动态成员    a.createId3();//通过静态函数创建 动态成员    a.id4=4;//直接创建动态成员})()

一个有趣的例子

(function(){   function A(){        this.say=function(){            console.log(this.id);        }   }   var a=new A();   a.say();//调用动态成员方法   //--->undefined   //因为没有名为id成员   a.id=1;//外部创建动态成员   a.say();   //--->1;})();


2.2.3 共享性

例子1:

(function(){    function A(){        this.func1=function(){};    }    A.prototype.func2=function(){};    console.log(new A().func1===new A().func1);//动态成员 func1之间的比较    //--->false    console.log(new A().func2===new A().func2);//静态成员 func2之间的比较    //--->true})();


例子2

(function(){    function A(){        this.id=1;    }    A.prototype.id2=2;    var a=new A();    var b=new A();    a.id=7;    console.log(b.id);//因为id是动态成员所b不受应影响    //-->2    console.log(a.id2);//公共属性    //-->2    a.id2=3;    ////创建了一个动态属性id2    console.log(b.id2);//因为a.id2为动态的所以无法影响到b    //-->2    A.prototype.id2=777;    delete a.id2;//删除动态成员a.id2    console.log(a.id2);//获取a.静态成员 id2    //--->7    console.log(b.id2);//获取b.静态成员 id2    //--->7})();

总结:

1.动态成员,实例化对象之间完全独立

2.静态成员,实例化对象之间完全共享

3.原型链继承说明

3.1 new关键字

例子
(function(){   function A(){     console.log(this.id);   }   A.prototype.id=1;   A();//没有使用new 是无法访问到静态成员的   //-->undefined;   new A();   //-->1;})();


在使用new关键字的时候,先找到A的原型链,也就是A.prototype并吧这个链的引用复制给构造函数 构造函数中也就可以调用到静态成员了

3.2 原型链继承的实质

3.2.1 继承,重写的实质

使用 new 关键字产生一个静态成员的引用集合,在构造函数以及之后的行为中创建动态成员,如果动态成员的名字和静态成员的名字相同,因为访问优先级的问题,this关键字就只能调用到动态成员了,这就实现了类似重写的功能

3.2.2在重写了成员以后,如何调用原来的成员

在java中,可以使用this.super关键字调用到父类的成员,同样 在js中则使用 [函数名].prototype.[成员名] 来调用父类成员

(function(){    function A(){        this.id=0;        console.log(this.id);        //-->0        console.log(A.prototype.id);        //-->1    }    A.prototype.id=1;    new A();})();

4.伪继承:曲线救国

4.1 静态继承

4.1.1 基本方法 

(function(){    function A(){        this.id=1;    }    A.prototype.id2=2;    function B(){}    B.prototype=new A();///B继承A    console.log(B.prototype.id);    //-->1    console.log(B.prototype.id2);    //-->2})();

这个代码的目的是让B继承A的成员,

方法就是:B.prototype=new A();

4.1.2 继承特性

把所有的父类成员,不管是动态还是静态,都转化为子类的静态成员

4.1.3 私有变量的继承关系

(function(){    function A(){        var _id=0;        this.setId=function(id){             _id=id;        }        this.getId=function(){             return _id;        }    }    function B(){}    B.prototype=new A();    new B().setId(1000);    console.log(new B().getId());    //--->1000})();

私有成员也是可以被继承的!!!!!!!! 只不过子类无法访问,但确实存在!!!!!!!!!!

4.1.4 继承原理

4.1.4.1 prototype赋值的方式原理

(function(){    function B(){}    B.prototype={id:1};//初始化B的原型链上的成员    B.prototype.name='oyb';    console.log(B.prototype.id);    //--->1    console.log(new B().id);    //--->1    console.log(new B().name); //-->oyb})();



4.1.4.1 静态继承的实质

(function(){    function A(){        this.id=1;    }    A.prototype.id2=2;    function B(){}    var a=new A();    console.log(a);    //-->A {id: 1, id2: 2}     B.prototype=a;///初始话B的静态成员    console.log(B.prototype.id);    //-->1    console.log(B.prototype.id2);    //-->2})();

如果A的构造方法带有参数 就B.prototype=new A(arg1,arg2,arg3);//把参数带进去就可以了

4.1.3 私有变量的继承关系

(function(){    function A(){        var _id=0;        this.setId=function(id){             _id=id;        }        this.getId=function(){             return _id;        }    }    function B(){}    B.prototype=new A();    new B().setId(1000);    console.log(new B().getId());    //--->1000})();

私有成员也是可以被继承的!!!!!!!! 只不过子类无法访问,但确实存在!!!!!!!!!!

4.2 动态继承

4.1.1 基本方法

(function(){    function A(){        this.id=1;    }    function B(){        A.call(this);//动态继承的代码    }    var b=new B();    console.log(b.id);    //-->1})();
核心方法 [父类].call(this);
如果父类有构造函数的话 就 ["父类"].call(this,arg1,arg2,arg3);

4.1.2 特性

(function(){    function A(){        this.id=1;    }    A.prototype.id2=2;    function B(){        A.call(this);    }    console.log(B.prototype.id);    //-->undefined    console.log(B.prototype.id2);    //-->undefined    var b=new B();    console.log(b.id);    //-->1    console.log(b.id2);    //undefined})();
只能继承动态成员,并且继承之后的成员仍旧是动态的!!!!!! 这样就可以避免属性重复问题

4.1.2 原理

使用了js的反射,实质上就是把A当作一个函数在B的构造函数中运行了一遍4

4.1.3私有属性的继承

和静态继承相似,一样会被继承下来,外部不能访问,但是是作为动态成员继承下来,所以不会被共享

4.3 混合式继承

因为静态继承,所有成员都是互相共享,方法适合用静态继承,属性因为是独立的所以适合用动态继承方式,或者干脆不继承,直接在子函数中创建一个
(function(){    function A(){        this.id=1;    }    A.prototype.setId=function(id){         this.id=id; }    A.prototype.getId=function(){        return this.id; } function B(){        A.call(this);//动态继承属性    }    B.prototype=new A();//静态集成方法 var b=new B(),c=new B(); b.setId(1010101); console.log(c.getId());    //-->1})()(function(){    function A(){}    A.prototype.setId=function(id){         this.id=id; }    A.prototype.getId=function(){        return this.id; }    function B(){        this.id=1;    }    B.prototype=new A();//静态集成方法 var b=new B(),c=new B();    b.setId(1010101);    console.log(c.getId());    //-->1})()

5 引入工厂模式

无论是使用静态还是动态的声明方式 那就是静态方法只能访问无法访问动态的私有变量,如上一个例子, var b=new B();b.id 是完全暴露出来的,外部可以随意修改id属性,如果想要封装怎么办?使用工厂模式的话可以很好的解决这个问题

(function(){  var factory=new function(){    function A(){}       A.prototype.setId=function(id){       this.id=id;    }    A.prototype.getId=function(){      return this.id;    }    function B(){}    B.prototype=new A();    B.prototype.say=function(){      console.log("say:"+this.getId());    }    function C(){      this.id=null;      this.getId=function(){                   return this.id+"这个方法被覆盖了" ;      }      this.getId2=function(){        return C.prototype.getId.call(this)+":调用原来的方法";      }    }    C.prototype=new A();    return {     getB:function(id){       var obj=new B();       obj.id=id;       return {         getId:function(){                        return obj.getId();         },         setId:function(id){           obj.setId(id);         },         say:function(){           obj.say();         }       }     },     getC:function(){      var obj=new C();      return {        getId:function(){          return obj.getId();        },        setId:function (id){          obj.setId(id);        },        getId2:function(){          return obj.getId2()        }      }    }  }};var b=factory.getB(12);b.say();//-->say:12b.setId(1111);b.say();//-->say:1111var c=factory.getC();c.setId(988101);console.log(c.getId());//-->988101这个方法被覆盖了console.log(c.getId2());//-->988101:调用原来的方法})();



工厂模式的好处是 把接口都放在了工厂里,类的外部接口, 以及构造函数都表现在了工厂里比较容易管理
如果使用工厂模式 最好使用 seajs 或者requirejs 对整个把代码模块化,不然代码膨胀速度将会不可控制










0 0
原创粉丝点击