这两天真的一直在看原型以及继承之间的千丝万缕,哇,收获颇多,不过所谓温故知新,还是要多复习复习知识点,才能察觉那些之前不易发现的小小sparkle
真心推荐MDN文档——对象原型,JavaScript 中的继承,文档指出了很多原理性的东西,所谓饮水思源,大家千万不要轻易就相信别人说什么好哇就像挖到宝藏一样,要多实践,多探索原理性的东西你才能真正把它掌握,它才真的是你的东西。
期间我还参考了阮一峰老师的Javascript 面向对象编程(一):封装,Javascript面向对象编程(二):构造函数的继承
Javascript面向对象编程(三):非构造函数的继承
廖雪峰老师的原型继承
在MDN文档以及廖雪峰老师那里都提到了构造函数继承的一种方法:(这里主要说明继承的一个办法,原理性的说明请看以上推荐链接)
function Person(first, last, age, gender, interests) {this.name = {first,last};this.age = age;this.gender = gender;this.interests = interests;
};Person.prototype.greeting = function() {alert('Hi! I\'m ' + this.name.first + '.');
};
这里先说明一下,在构造函数中,属性最好都写在构造函数里,而方法最后都写在prototype(原型对象)里,这样层次比较分明,但是如果是常属性(值不变),那么他可以写进prototype里,这样可以减少内存的占据
我们现在要创建一个Teacher类,需要继承Person的所有属性和方法,
同时也包括:
一个新的属性subject
——这个属性包含了教师教授的学科。
一个被更新的greeting()
方法,这个方法打招呼听起来比一般的greeting()
方法更正式一点——对于一个教授一些学生的老师来说。
MDN文档的做法是:
function Teacher(first, last, age, gender, interests, subject) {// 调用person构造函数,绑定this变量:Person.call(this, first, last, age, gender, interests); this.subject = subject;
}Teacher.prototype = Object.create(Person.prototype);
Teacher.prototype.constructor = Teacher;
而这里廖雪峰老师的做法的第一步也是(与廖老师文章里例子不同但原理相似)
Person.call(this, first, last, age, gender, interests); this.subject = subject;
之后他指出,调用了Person构造函数不等于继承了Person,Teacher
创建的对象的原型是:
new Teacher() ----> Teacher.prototype ----> Object.prototype ----> null
必须想办法把原型链修改为:
new Teacher() ----> Teacher.prototype ----> Person.prototype ----> Object.prototype ----> null
我们必须借助一个中间对象来实现正确的原型链,这个中间对象的原型要指向Person.prototype
。为了实现这一点,参考道爷(就是发明JSON的那个道格拉斯)的代码,中间对象可以用一个空函数F
来实现:
// 空函数F:
function F() {
}// 把F的原型指向Student.prototype:
F.prototype = Person.prototype;// 把Teacher的原型指向一个新的F对象,F对象的原型正好指向Person.prototype:
Teacher.prototype = new F();// 把PrimaryStudent原型的构造函数修复为Teacher:
Teacher.prototype.constructor = Teacher;
重写Teacher的greeting函数:
Teacher.prototype.greeting = function() {var prefix;if(this.gender === 'male' || this.gender === 'Male' || this.gender === 'm' || this.gender === 'M') {prefix = 'Mr.';} else if(this.gender === 'female' || this.gender === 'Female' || this.gender === 'f' || this.gender === 'F') {prefix = 'Mrs.';} else {prefix = 'Mx.';}alert('Hello. My name is ' + prefix + ' ' + this.name.last + ', and I teach ' + this.subject + '.');
};
验证:
var teacher1 = new Teacher('Dave', 'Griffiths', 31, 'male', ['football', 'cookery'], 'mathematics');teacher1.name.first;
teacher1.interests[0];
teacher1.bio();
teacher1.subject;
teacher1.greeting();
廖老师用空函数的方法实现了间接继承又不占用多少空间,Teacher.prototype的改写也不会影响上一级的prototype,妙也!
但是为什么MDN文档用
Teacher.prototype = Object.create(Person.prototype);
就基本全搞定继承了呢?
带着好奇我查了这个函数的原理
Object.create = function (o) {var F = function () {};F.prototype = o;return new F();};
哈哈哈哈,这不是跟廖老师的方法一样嘛,也是造一个空函数,然后间接实现函数的继承!
而非构造函数中,阮老师总结的第一个方法 object()方法,其原理类似于Object.create()
function object(o) {function F() {}F.prototype = o;return new F();}
var Chinese = {nation:'China',greeting:function (name) {alert('Hi! I\'m ' + name+' from '+this.nation+ '.');}};var Doctor =Object.create(Chinese);Doctor.career='doctor';Doctor.greeting=function(name){alert('Hi! I\'m a ' + this.career+' from '+this.nation+ ',my name is '+name+ ' .');};alert(Doctor.nation);//ChinaDoctor.greeting('Emma');//Hi! I'm a doctor from China,my name is Emma .
大家可以试试看。
好的,总结到此啦,阮一峰老师提到了很多构造函数的继承方法,但是各有利弊吧,原型以及继承这块‘深海‘’还是需要我们多去翻滚才行,这里顺便抛一个new方法原理,自己好好瞎捉摸吧哈哈哈哈