function A(x){ this.x = x; this.say = function() { console.log(this.x + ' say'); } } function B(x,y) { this.m = A; // 把构造函数A作为一个普通函数引给临时方法m() this.m(x); // 把当前参数作为值传给构造函数A,并且执行 delete this.m; // 清除临时方法 this.y = y; this.call = function(){ console.log(this.y); } } // 测试类继承 var a = new A(1); var b = new B(2,3); a.say(); // 1 say b.say(); // 2 say b.call(); // 3上面的实现方式很巧妙对吧,但是这种设计方式太随意,缺乏严密性。严禁的设计模式应该考虑到各种可能存在的情况和类继承关系中的互相耦合性。所以一般我们尽可能把子类自身的方法放在原型里去实现。下面这种创建方式会更好:
function A(x){ this.x = x; this.say = function() { console.log(this.x + ' say'); } } function B(x,y){ this.y = y; A.call(this,x); // 在构造函数B内调用超类A,实现绑定,用call来实现借用 } B.prototype = new A(); // 设置原型链,建立继承关系 B.prototype.constructor = B; // 恢复B的原型对象的构造函数为B,如果不操作就会指向A B.prototype.gety = function(){ // 给类B添加新的方法 return this.y; } // 测试 类继承 var a = new A('A'); var b = new B('Bx','By'); a.say(); // A say b.say(); // Bx say console.log(b.gety()); // By console.log(B.prototype.__proto__ === A.prototype); // true; console.log(b.constructor === B); // true 这里也可以把B中的 B.prototype.constructor = B; 去除,结果为false在js中实现类继承,需要设置3点:
// 定义一个继承函数 function extend(Sub,Sup){ // 有两个入口参数,Sub是子类,Sup是父类 var F = function(){}; // 建立一个临时的构造函数 F.prototype = Sup.prototype; // 把临时构造函数的原型指向父类的原型 Sub.prototype = new F(); // 实例化临时类,此处相当于把子类的原型指向父类的实例 Sub.prototype.constructor = Sub; // 恢复子类的构造函数 Sub.sup = Sup.prototype; // 在子类中定义一个本地属性存储超类原型,可避免子类和超类耦合 if(Sup.prototype.constructor === Object.prototype.constructor) { // 检测超类构造器是否为Object构造器 Sup.prototype.constructor = Sup; } } // 下面定义两个类用来测试上面的继承函数 function A(x,y) { this.x = x; this.y = y; } A.prototype.add = function() { return (this.x-0) + (this.y-0); // 此处-0 的目的是确保字符串类型可转成数值型 } A.prototype.minus = function() { return this.x - this.y; } function B(x,y) { A.apply(this,[x,y]); } // 开始实现类继承中的原型成员继承 extend(B,A); // 为了不与A类中的代码耦合可以单独为B定义一个同名的add B.prototype.add = function() { return B.sup.add.call(this); // 避免代码耦合 } // 测试继承 var b = new B(1,2); console.log(b.minus()); // -1 console.log(b.add()); // 3
function A(){ this.color = 'red'; } function B(){} function C(){} B.prototype = new A(); C.prototype = new B(); // 测试原型继承 var c = new C(); console.log(c.color); // red原型继承显得很简单,不需要每次构造都调用父类的构造函数,也不需要通过复制属性的方式就能快速实现继承。但它也存在一些缺点:
function Arr() { var a = new Array(); return a; } var arr = new Arr(); arr[0] = 1; arr[1] = 2; console.log(arr); // [1,2] console.log(Array.isArray(arr)); // true console.log(arr instanceof Array); // true console.log(arr instanceof Arr); // false通过构造函数中完成对类的实例化操作,然后返回实例对象,这就是实例继承的由来。实例继承可实现对所有对象的继承,包括自定义类,核心对象和DOM对象。但是也有一些缺点
function A(){ this.color = 'red'; } A.prototype.say = function() { console.log(this.color); } var a = new A(); var b = {}; // 开始拷贝 for(var item in a) { b[item] = a[item]; } // 开始测试 console.log(b.color); // red b.say(); // red我们把它封装一下:
Function.prototype.extend = function(obj){ for(item in obj){ this.constructor.prototype[item] = obj[item]; } } function A(){ this.color = 'green'; } A.prototype.say = function(){ console.log(this.color); } // 测试 var b = function(){}; b.extend(new A()); b.say(); // green复制继承实际上是通过反射机制复制类对象中的可枚举属性和方法来模拟继承。这种可以实现多继承。但也有缺点:
Function.prototype.clone = function(obj){ function Temp(){}; Temp.prototype = obj; return new Temp(); } function A(){ this.color = 'purple'; } var o = Function.clone(new A()); console.log(o.color); // purple
function A(x,y){ this.x = x; this.y = y; } A.prototype.add = function(){ return (this.x-0) + (this.y-0); } function B(x,y){ A.call(this,x,y); } B.prototype = new A(); // 测试 var b = new B(2,1); console.log(b.x); // 2 console.log(b.add()); // 3
function A(x){ this.x = x; } A.prototype.hi = function(){ console.log('hi'); } function B(y){ this.y = y; } B.prototype.hello = function(){ console.log('hello'); } // 给Function增加extend方法 Function.prototype.extend = function(obj) { for(var item in obj) { this.constructor.prototype[item] = obj[item]; } } // 在类C内部实现继承 function C(x,y){ A.call(this,x); B.call(this,y); }; C.extend(new A(1)); C.extend(new B(2)); // 通过复制继承后,C变成了一个对象,不再是构造函数了,可以直接调用 C.hi(); // hi C.hello(); // hello console.log(C.x); // 1 console.log(C.y); // 2一个类继承另一个类会导致他们之间产生耦合,在js中提供多种途径来避免耦合的发生如 掺元类
// 定义一个掺元类 function F(x,y){ this.x = x; this.y = y; } F.prototype = { getx:function(){ return this.x; }, gety:function(){ return this.y; } } // 原型拷贝函数 function extend(Sub,Sup){ // Sub 子类 , Sup 掺元类 for(var item in Sup.prototype){ if(!Sub.prototype[item]){ // 如果子类没有存在同名成员 Sub.prototype[item] = Sup.prototype[item]; // 那么复制掺元类成员到子类原型对象中 } } } // 定义两个子类 A,B function A(x,y){ F.call(this,x,y); } function B(x,y){ F.call(this,x,y); } // 调用extend函数实现原型属性,方法的拷贝 extend(A,F); extend(B,F); console.log(A.prototype); // 测试继承结果 var a = new A(2,3); console.log(a.x); // 2 console.log(a.getx()); // 2 console.log(a.gety()); // 3 var b = new B(1,2); console.log(b.x); // 1 console.log(b.getx()); // 1 console.log(b.gety()); // 2通过此种方式把多个子类合并到一个子类中,就实现了多重继承。