继承是根据原型链实现的,一层层进行引用,当查找某个实例中的属性时,先在实例中进行查找,如果没有,再到原型中进行查找,如果原型还没有,在到prototype指向的父类模型中进行查找,像链条一样依次向上查找,直到找到该属性。
function Person() { this.age = 12; } Person.prototype.Say = function () { console.log("hello"); } function Students() { this.name = "aa"; } Students.prototype.Learn = function () { console.log("study"); } Students.prototype = new Person(); var student = new Students(); student.Say(); // hello console.log(student.age); //12 console.log(Students.prototype.constructor); //function Person 实际上不是Students的原型的constructor被重写了,而是Students的原型指向了另一个对象(Person)的原型,而这个原型对象的constructor属性指向的是Person console.log(Students.prototype); //Person {age: 12}
我们知道,所有引用类型都继承了object,而这个继承也是通过原型链实现的,我们要记住,所有函数的默认原型都是object的实例,因此默认原型都会包含一个内部指针,指向object.prototype,这也正是所有自定义类型都会继承tostring()等默认方法的原因。
继承中使用引用类型变量的问题
function Person() { this.age = 12; this.color = ["red", "black"]; } Person.prototype.Say = function () { console.log("hello"); } function Students() { this.name = "aa"; } Students.prototype.Learn = function () { console.log("study"); } Students.prototype = new Person(); var student = new Students(); student.color[0] = "white"; student.age = 55; console.log(student.color[0]); //white console.log(student.age); //55 var student1 = new Students(); console.log(student1.color[0]);////white console.log(student1.age); //12
可以看到,上面使用值类型定义的变量没有共享,而引用类型的变量产生了共享,某个实例在修改引用类型的变量时,会直接影响所有实例的值,因这显然是不科学的
解决方法:
function Person() { this.age = 12; this.color = ["red", "black"]; } Person.prototype.Say = function () { console.log("hello"); } function Students() { Person.call(this); this.name = "aa"; } Students.prototype.Learn = function () { console.log("study"); } Students.prototype = new Person(); var student = new Students(); student.color[0] = "white"; student.age = 55; console.log(student.color[0]); //white console.log(student.age); //55 var student1 = new Students(); console.log(student1.color[0]);////red console.log(student1.age); //12
解决方法是,在子类型构造函数的内部调用超类型构造函数,这样使用apply和call方法,可以在以后新创建的对象上执行构造函数。
最理想的继承方法:
function Person() { this.age = 12; this.color = ["red", "black"]; } Person.prototype.Say = function () { console.log("hello"); } function Students() { Person.call(this); this.name = "aa"; } Students.prototype.Learn = function () { console.log("study"); } function inheritPrototype(subType, superType) { var prototype = Object.create(superType.prototype); prototype.constructor = subType; subType.prototype = prototype; } inheritPrototype(Students, Person); var student = new Students(); var student1 = new Students(); student.color[0] = "white"; console.log(student.color[0]); console.log(student1.color[0]);
这中法只调用了一次父类的构造函数,并且避免子类的prototype上创建不必要的属性,还能保持原型链的不变,所以这是最理想的方式。