代码都用关包的方式来显示(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:12 b.setId(1111); b.say(); //-->say:1111 var c=factory.getC(); c.setId(988101); console.log(c.getId());//-->988101这个方法被覆盖了 console.log(c.getId2());//-->988101:调用原来的方法 })();
工厂模式的好处是 把接口都放在了工厂里,类的外部接口, 以及构造函数都表现在了工厂里比较容易管理
如果使用工厂模式 最好使用 seajs 或者requirejs 对整个把代码模块化,不然代码膨胀速度将会不可控制