子类的全部实例都共享着原型上的全部属性和方法。经过子类实例,能够访问原型上的属性,可是,不能重写原型上的属性。javascript
//定义一个学生类 function Student(stuID, schoolName) { this.stuID = stuID; this.schoolName = schoolName; } //全部学生都有这样一个特征 Student.prototype.characteristic = '年轻有朝气'; var stu1 = new Student(1001,'第一小学'); console.log(stu1.stuID); //1001 console.log(stu1.characteristic); //'年轻有朝气' //重写characteristic stu1.characteristic = '活泼可爱'; console.log(stu1.characteristic); //'活泼可爱' var stu2 = new Student(1002,'第一小学'); console.log(stu2.characteristic); //'年轻有朝气' console.log(Student.prototype); //{characteristic: "年轻有朝气"}
上面这段代码代表:
经过stu1.characteristic = '活泼可爱';并无改变原型上的属性值。java
当实例中,存在和原型中同名的属性时,会自动屏蔽原型上的同名属性。stu1.characteristic = = '活泼可爱' 其实是给实例stu1添加了一个本地属性 characteristic,因此当咱们再次访问stu1.characteristic 时,访问的是实例的本地属性,而不是原型上的characteristic属性(它因和本地属性名同名已经被屏蔽了)。app
原型上的 characteristic 值是一个基本类型的值,若是是一个引用类型呢?这其中又会有一堆小九九。函数
其实原型上任何类型的值,都不会被实例所重写。在实例上设置与原型上同名属性的值,只会在实例上建立一个同名的本地属性。可是,原型上引用类型的值能够经过实例进行修改,并且全部的实例访问到的该引用类型的值也会随之改变。(不是很明白,既然引用类型的值都能被修改了,那么为何还说不会被实例重写??难道修改!= 重写)this
//定义一个学生类 function Student(stuID, schoolName) { this.stuID = stuID; this.schoolName = schoolName; } //全部学生都有这样一个特征 Student.prototype.characteristic = '年轻有朝气'; Student.prototype.examItems = ['语文','数学','英语']; //考试项目 var stu1 = new Student(1001, '第一小学'); console.log(stu1.examItems); //['语文','数学','英语'] //修改examItems stu1.examItems.push('科学'); console.log(stu1.examItems); //['语文','数学','英语','科学'] var stu2 = new Student(1002, '第一小学'); console.log(stu2.examItems); //['语文','数学','英语','科学']
原型上任何类型的属性值都不会经过实例被重写,可是引用类型的属性值会受到实例的影响而修改。prototype
在子类的构造函数中,经过 apply( ) 或 call( )的形式,调用父类构造函数,以实现继承。code
//定义一个超类/父类: 人 function Person (name, age) { //人都有姓名,年龄,会吃饭,会睡觉 //传入出生年份 year,自动计算年龄 this.name = name; this.age = age; this.eat = function () { alert('吃饭'); } this.sleep = function () { alert('睡觉'); } } //定义一个子类: 学生 //学生Student也是人,天然要继承超类 Person 的全部属性和方法 //学生都应当有姓名、年龄、会吃饭、会睡觉 //固然学生也有本身的一些属性:学号,学校名称等,和方法,好比都要去作一件事:写做业 function Student (stuID, schoolName, name, age) { this.stuID = stuID; this.schoolName = schoolName; //用call调用 Person,以实现继承 Person.call(this, name, age); } Student.prototype.doHomework = function () { alert('作做业'); } //实例化一个学生 var stu1 = new Student(1001, '第一小学', '王宝宝',20); console.log(stu1.stuID); //1001 console.log(stu1.schoolName); //'第一小学' console.log(stu1.name); //'王宝宝' console.log(stu1.age); //20 stu1.eat(); //'吃饭' stu1.sleep(); //'睡觉' stu1.doHomework(); //'作做业'
在子类构造函数中,咱们经过 call 的方式调用了父类构造函数 Person实现了继承。别忘了,函数只不过是一段能够在特定做用域执行代码的特殊对象,咱们能够经过 call 方法指定函数的做用域。对象
在 stu1 = new Student() 构造函数时,Student 内部 this 的值指向的是 stu1, 因此 this.stuID =stu1.stuID, 因此 Person.call(this, name, age) 就至关于Person.call(stu1, '王宝宝', 20),就至关于 stu1.Person('王宝宝',20)。最后,stu1 去调用 Person 方法时,Person 内部的 this 指向就指向了 stu1。那么Person 内部this 上的全部属性和方法,都被拷贝到了stu1上。继承
总之,在子类函数中,经过call() 方法调用父类函数后,子类实例 stu1, 能够访问到 Student 构造函数和 Person 构造函数里的全部属性和方法。这样就实现了子类向父类的继承。ip
这种形式的继承,每一个子类实例都会拷贝一份父类构造函数中的方法,做为实例本身的方法,好比 eat()。这样作,有几个缺点:
1. 每一个实例都拷贝一份,占用内存大,尤为是方法过多的时候。
2. 方法都做为了实例本身的方法,当需求改变,要改动其中的一个方法时,以前全部的实例,他们的该方法都不能及时做出更新。只有后面的实例才能访问到新方法。
因此,其实单独使用原型链继承或者借用构造函数继承都有本身很大的缺点,最好的办法是,将二者结合一块儿使用,发挥各自的优点。
例如:
function OSTAccountDC() { OSTBaseDC.call(this); } OSTAccountDC.prototype = new OSTBaseDC(); //经过OSTBaseDC建立一个实例,避免改变父类构造函数的属性,而只是在本地建立一个属性。 OSTAccountDC.prototype.constructor = OSTAccountDC; //使this指向本身OSTAccountDC,而不是指向构造函数OSTBaseDC OSTAccountDC.prototype.accountRequest = function(callback){ // do something }