如下内容均基于本人对《JavaScript高级程序设计》第三版6.3小节的理解app
function Animal(name) { var name = name; //'私有(受保护)'成员,只容许在父类的构造函数中赋值 this.food = undefined; //'公有'成员 //引用类型的成员 this.birthday = { year: undefined }; //构造函数中的方法,打印一些基本信息 this.greeting = function() { console.log('Hi, my name is {' + name + '} I like eat {' + this.food + '} and my birth year is {' + this.birthday.year + '}'); }; //原型中的方法,将构造函数中的非函数成员以JSON格式打印 Animal.prototype.briefInfo = function() { var brief = { name: name, food: this.food, birthday: this.birthday }; console.log(JSON.stringify(brief)); }; }
实现方式:子类的原型指向父类的实例函数
子类:测试
function Dog() {} Dog.prototype = new Animal(); //原型指向父类的实例
测试:this
var dog1 = new Dog(); dog1.food = 'shit'; dog1.birthday.year = 2015; var dog2 = new Dog(); dog2.food = 'bones'; dog2.birthday.year = 2016; dog1.greeting(); //console: Hi, my name is {undefined} I like eat {shit} and my birth year is {2016} dog2.greeting(); //console: Hi, my name is {undefined} I like eat {bones} and my birth year is {2016} dog1.briefInfo(); //console: {"food":"shit","birthday":{"year":2016}} dog2.briefInfo(); //console: {"food":"bones","birthday":{"year":2016}} //以上, //birthday是引用类型的属性,因此dog1的birthday.year被dog2覆盖了; //没法给dog1和dog2的name赋值
存在的问题:prototype
引用类型的对象会被子类的全部实例共享(1.1)
设计
没法在建立子类的实例时,给父类的构造函数传递参数(1.2)
code
实现方式:在子类的构造函数中利用call(或者apply)方法执行父类构造函数(问题1.2解决)
,将执行对象设为子类的this,至关于把父类构造函数中的成员拷贝了一份到子类(问题1.1解决)
对象
子类继承
function Dog(name) { Animal.call(this, name); }
测试ip
var dog1 = new Dog('tom'); dog1.food = 'shit'; dog1.birthday.year = 2015; var dog2 = new Dog('mike'); dog2.food = 'bones'; dog2.birthday.year = 2016; dog1.greeting(); //console: Hi, my name is {tom} I like eat {shit} and my birth year is {2015} dog2.greeting(); //console: Hi, my name is {mike} I like eat {bones} and my birth year is {2016} //briefInfo是父类原型中的属性,并无被继承,如下语句会报错 dog1.briefInfo(); //error: dog1.briefInfo is not a function dog2.briefInfo(); //error: dog2.briefInfo is not a function
存在的问题:
父类原型中定义的属性没法被继承
综合方式一方式二,一种很明显的方式呼之欲出了:
实现方式:结合原型链继承和经典继承
子类
function Dog(name) { Animal.call(this, name); //经典继承 } Dog.prototype = new Animal(); //原型链继承
测试
var dog1 = new Dog('tom'); dog1.food = 'shit'; dog1.birthday.year = 2015; var dog2 = new Dog('mike'); dog2.food = 'bones'; dog2.birthday.year = 2016; dog1.greeting(); //console: Hi, my name is {tom} I like eat {shit} and my birth year is {2015} dog2.greeting(); //console: Hi, my name is {mike} I like eat {bones} and my birth year is {2016} dog1.briefInfo(); //console: {"name":"tom","food":"shit","birthday":{"year":2015}} dog2.briefInfo(); //console: {"name":"mike","food":"bones","birthday":{"year":2016}} //终于获得了预期的结果,算是较好的实现了继承。 //可是,并不完美
存在的问题
父类的构造函数被调用了两次
为了引出下一个继承方式,先将函数的继承放在一边,看一下js中对象的继承
(摘抄原文)
1. 原型式继承
var person = { name: 'martin' firend: ['bob', 'steven'] }; function object(o) { function F() {}; F.prototype = o; return new F(); } var anotherPerson = object(person); //antherPerson继承了person2. 寄生式继承
function createAnother(original) { var clone = object(original); //原型式继承定义的方法 //扩展对象 clone.sayHi = function() { console.log('hi'); } return clone; }
回到正题,接下来介绍一颗 “银弹”
实现方式:
利用经典继承拷贝父类构造中的属性到子类
利用原型式继承建立一个继承父类原型的对象
将该对象的constructor属性指向子类的构造函数(寄生式继承:扩展对象)
将子类的prototype指向该对象
子类
function Dog(name) { Animal.call(this, name); } function F() {} var supProto = Animal.prototype; F.prototype = supProto; var subProto = new F(); subProto.constructor = Dog; Dog.prototype = subProto;
测试
var dog1 = new Dog('tom'); dog1.food = 'shit'; dog1.birthday.year = 2015; var dog2 = new Dog('mike'); dog2.food = 'bones'; dog2.birthday.year = 2016; dog1.greeting(); //console: Hi, my name is {tom} I like eat {shit} and my birth year is {2015} dog2.greeting(); //console: Hi, my name is {mike} I like eat {bones} and my birth year is {2016} dog1.briefInfo(); //console: {"name":"tom","food":"shit","birthday":{"year":2015}} dog2.briefInfo(); //console: {"name":"mike","food":"bones","birthday":{"year":2016}}