javascript实现继承的几种方式?
基本概念
实现继承的几种方式
继承的概念分两个层面:
一、子类可以拥有父类的一部分或全部属性或方法
二、子类还可以有自己的一些属性和方法
在javascript中实现继承的核心机制是 ——原型链
一个构造函数由构造函数本身和构造函数的原型对象两个部分组成(双对象法则) 。
也就是构造函数有一个隐藏的属性__proto__,指向构造函数的原型对象。
当构造函数被实例化时,把构造函数中的所有属性和方法拷贝到实例当中,同时,这个__proto__属性也会被拷贝。
当访问实例对象当中的属性或方法时,会先从实例本身找,如果找到了,就会使用实例中的这个属性或方法;
如果没有找到,就会通过__proto__属性中保存的地址找到原型对象,再从原型对象里找这个属性或方法。
/** * 对象冒充 * 构造函数使用 this 关键字给所有属性和方法赋值(即采用类声明的构造函数方式)。 * 因为构造函数只是一个函数,所以可使 ClassA 构造函数成为 ClassB 的方法,然后调用它。 * ClassB 就会收到 ClassA 的构造函数中定义的属性和方法。 * 弊端:必须使用构造函数方式。 */ function ClassA(sColor) { this.color = sColor; this.yy = "yy1234yy"; this.sayColor = function () { alert(this.color); }; } /** * 在这段代码中,为 ClassA 赋予了方法 newMethod(请记住,函数名只是指向它的指针)。 * 然后调用该方法,传递给它的是 ClassB 构造函数的参数 sColor。 * 最后一行代码删除了对 ClassA 的引用,这样以后就不能再调用它。 * 所有新属性和新方法都必须在删除了新方法的代码行后定义。 * 否则,可能会覆盖超类的相关属性和方法。 */ function ClassB(sColor, sName) { this.newMethod = ClassA; this.newMethod(sColor); delete this.newMethod; this.name = sName; this.sayName = function () { // alert(this.name); alert(this.name+"___"+this.yy); }; } var objA = new ClassA("blue"); var objB = new ClassB("red", "John"); objA.sayColor(); //输出 "blue" objB.sayColor(); //输出 "red" objB.sayName(); //输出 "John" //输出John___yy1234yy
/** * call()方法 * 它的第一个参数用作 this 的对象。其他参数都直接传递给函数自身。 */ function sayColor(sPrefix,sSuffix) { alert(sPrefix + this.color + sSuffix); }; var obj = new Object(); obj.color = "blue"; sayColor.call(obj, "The color is ", ",a very nice color indeed."); /** * 要与继承机制的对象冒充方法一起使用该方法,只需将前三行的赋值、调用和删除代码替换即可 */ function ClassB(sColor, sName) { //this.newMethod = ClassA; //this.newMethod(color); //delete this.newMethod; ClassA.call(this, sColor); this.name = sName; this.sayName = function () { alert(this.name); }; }
/** * apply()方法 * apply() 方法有两个参数,用作 this 的对象和要传递给函数的参数的数组。 * 只有超类中的参数顺序与子类中的参数顺序完全一致时才可以传递参数对象。 * 如果不是,就必须创建一个单独的数组,按照正确的顺序放置参数。 */ function sayColor(sPrefix,sSuffix) { alert(sPrefix + this.color + sSuffix); }; var obj = new Object(); obj.color = "blue"; sayColor.apply(obj, new Array("The color is ", ",a nice color.")); /** * 该方法也用于替换前三行的赋值、调用和删除新方法的代码 */ function ClassB(sColor, sName) { //this.newMethod = ClassA; //this.newMethod(color); //delete this.newMethod; ClassA.apply(this, new Array(sColor)); this.name = sName; this.sayName = function () { alert(this.name); }; }
/** * 原型链 * 继承这种形式在 ECMAScript 中原本是用于原型链的。 * 调用 ClassA 的构造函数,没有给它传递参数。这在原型链中是标准做法。要确保构造函数没有任何参数。 * 子类的所有属性和方法都必须出现在prototype属性被赋值后,因为在它之前赋值的所有方法都会被删除。 * 弊端:不支持多重继承。记住,原型链会用另一类型的对象重写类的 prototype 属性。 * 弊端:无法使用带参数的构造函数。 * 在原型链中,instanceof 运算符的运行方式也很独特。 * 对 ClassB 的所有实例,instanceof 为 ClassA 和 ClassB 都返回 true。 */ function ClassA() { } ClassA.prototype.color = "blue"; ClassA.prototype.sayColor = function () { alert(this.color); }; function ClassB() { } ClassB.prototype = new ClassA(); ClassB.prototype.name = ""; ClassB.prototype.sayName = function () { alert(this.name); };
/** * 混合方式 * 这种继承方式使用构造函数定义类,并非使用任何原型。 * 用对象冒充继承构造函数的属性,用原型链继承 prototype 对象的方法。 * 由于这种混合方式使用了原型链,所以 instanceof 运算符仍能正确运行。 */ function ClassA(sColor) { this.color = sColor; } ClassA.prototype.sayColor = function () { alert(this.color); }; function ClassB(sColor, sName) { ClassA.call(this, sColor); this.name = sName; } ClassB.prototype = new ClassA(); ClassB.prototype.sayName = function () { alert(this.name); };
/** * Animal类,有个Cat类继承它 */ function Animal(name){ this.name=name; } Animal.prototype.sayName=function(){ alert(this.name); }; function Cat(name){ Animal.call(this,name); } Cat.prototype=new Animal(); var cat = new Cat("大白的猫"); cat.sayName();