JavaScript——驰骋网页的脚本执行者(6)
九、高级面向对象技术之继承
1.工厂函数模式创建对象
例如:
function factory(name,age){ var obj = new Object(); obj.name = name; obj.age = age; obj.sayName = function(){ }; return obj; } var obj1 = factory("terry",12); var obj2 = factory("larry",12);
此模式存在的问题有:对象类型无法细分;其中的函数会重复创建,浪费内存空间且破坏封装性。
2.构造函数模式创建对象
通过Object、Array、Date、Number、String、Boolean、RegExp等系统构造函数,或通过自定义构造函数,如function Student(){},创建对象。此模式解决了对象类型无法细分的问题,但仍未解决函数重复创建,破坏封装性的问题。
3.构造函数模式与原型相结合模式
此模式可将对象属性保存在相应实例中,而方法保存在实例的构造函数原型中,如:
function Person(name,age){ this.name = name; this.age = age; } Person.prototype.sayName = function(){ console.log(“Hi, my name is” + this.name); } var p1 = new Person("terry",12); var p2 = new Person("larry",13);
可用obj instanceof Function判断obj是否是Function的实例。
4.原型链继承
每个构造函数都有一个原型对象,原型对象中都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针。当原型对象等于另外一个类型的实例即继承。调用某个方法或者属性的步骤。其核心在于:子构造函数的原型指向(是)父构造函数的实例,如Dog的原型Dog.prototype是Object的实例。
再例如:
//定义父类类型 function Animal(){ this.name = "animal" } Animal.prototype.sayName = function(){ console.log(this.name); } //定义子类类型 function Dog(){ this.color = "灰色" } //通过将子对象的原型对象指向父对象的一个实例来完成继承 Dog.prototype = new Animal(); //区分父类实例的类型 Dog.prototype.constructor = Dog; //子对象的方法其实是定义在父类对象的实例上 Dog.prototype.sayColor = function(){ console.log(this.color); } var dog = new Dog(); console.log(dog); //Dog { color: '灰色' } dog.sayColor(); //灰色 dog.sayName(); //animal
在子类构造函数内部调用超类构造函数可以简化其初始化过程,原理是使用原型链实现对原型属性和方法的继承,而通过借用构造函数实现对实例属性的继承,例如:
function Animal(name,age){ this.name = name; this.age = age; } Animal.prototype.sayName = function(){ console.log("my name is",this.name); } function Person(name,age,gender){ Animal.call(this,name,age); this.gender = gender; } Person.prototype = new Animal(); Person.prototype.constructor = Person; //替换原型 Person.prototype.sayGender = function(){ console.log("my gender is",this.gender); } var p = new Person("terry",12,"male"); console.log(p); //Person { name: 'terry', age: 12, gender: 'male' } p.sayName(); //my name is terry p.sayGender(); //my gender is male
子类型覆盖超类型中的某个方法,或者是需要添加超类中不存在的方法,都需要将给原型添加方法的代码放在继承之后(即替换原型的语句之后),且创建子类对象的操作也应在替换原型之后进行,否则会出现“同母异父”的情况,例如:
function Dog(name,age){ this.name = name; this.age = age; } Dog.prototype.sayName = function(){ console.log("hi, my name is",this.name); } var d1 = new Dog("一休",2); console.log(d1); Dog.prototype = { constructor:Dog, sayName:function(){console.log(this.name)}, sayAge:function(){console.log(this.age);} } var d2 = new Dog("八哥",2); console.log(d2); console.log(d1.constructor === d2.constructor); //true console.log(d1.__proto__ === d2.__proto__); //false