JavaScript继承指迷
以下是 廖雪峰老师 JavaScript 教程的笔记 ,这个章节有点大雾,这里记录一下
先硬广 安利一下
小A和小B都是应届毕业生,一起进入了一家互联网公司做前端。半年过去了,小A进步神速,他写的JavaScript代码在组里挑剔的的老员工看来都挺不错:
可读性好,模块化程度高,而且有大量的测试。反观小B,他的代码总是一团糟,还经常搞出莫名其妙的bug。每次做code review时,大家对小B的代码都特别头疼。
年底考核的时候,组长让小A分享一下他入职以来是怎么学习前端并快速成长的,小A不好意思地给大家分享了自己的一点学习心得:
首先,前端工程师必须得熟练掌握HTML、CSS和JavaScript。只懂其中一个或两个不行,必须对这三个都很熟悉,尤其是JS。
很多前端工程师还把对JS的认识停留在用jQuery做网页动画的时代,这个认识早就落后了,现在JS不仅负责前端的所有页面逻辑,还能利用Node开发后端服务。
而JS并没有看上去那么简单,它入门容易精通难,尤其是自己摸索,或者在网上随便搜索一些代码片断,很容易被带进沟里。学JavaScript应该学到精髓,还应该知道JS不好的设计,并且有意识地只用JS优秀的设计。
由于JS最近随着HTML5的兴起有了很大的更新,网上很多教程或者资料都过时了,现在应该用ES 6标准来写代码。即使不做后端,也要对Node非常熟悉,因为前端的自动化也是基于Node和npm实现的。
小A说了这么多,组长觉得很惊讶,说,“我看你平时也没怎么与老员工探讨前端技术问题,怎么懂得这么多?知识体系还挺丰富!”
小A不好意思地说,“其实我刚进公司的时候也是啥也不会,纯小白一个,偶然听一个朋友推荐看到了廖雪峰老师的网站,里面有非常系统的JavaScript全栈教程,讲的重点都是JS的精髓,还特别指出容易出错的坑。廖老师的教程更新很快,我每天都花半个小时上去学一点,几个月下来也积累了不少。”
组长用自己的笔记本搜索了小A说的廖雪峰老师的网站,看了几页,说:“小A啊,这么好的JS教程,你怎么藏着掖着,不早点分享给大家呢?你这个做法不厚道,今晚团队聚餐的费用必须你出。”
小A一脸懵逼……
友情提示:小A提到的神奇的JavaScript全栈教程哪里找?猛戳这里**有惊喜!
创建对象
构造函数
为了区分普通函数和构造函数,按照约定,构造函数首字母应当大写,而普通函数首字母应当小写
function Student(name) {
this.name = name;
this.hello = function () {
alert('Hello, ' + this.name + '!');
}
}
搭配 new 使用默认 自动返回 this
var xiaoming = new Student('小明');
xiaoming.name; // '小明'
xiaoming.hello(); // Hello, 小明!
var xiaohong = new Student('小红');
xiaohong.name; // '小明'
xiaohong.hello(); // Hello, 小明!
比较两个Student
这里new 出来的 Student 的 Hello 方法不是复用! 用其他语言的角度来看同一个类new出来的对象居然各自持有方法,真的是 泥垢了
xiaoming.hello === xiaohong.hello;//false
重写Student的构造函数
function Student(name) {
this.name = name;
}
Student.prototype.bioName = "智人";
Student.prototype.hello = function () {
alert('Hello, ' + this.name + '!');
};
var xiaoming = new Student('小明');
var xiaohong = new Student('小红');
//这里可以看出复用方法了 公用的东西应该放到 prototype 里面
xiaoming.hello === xiaohong.hello;// true
xiaoming.bioName === xiaohong.bioName; //true
xiaoming.bioName; //"智人"
xiaohong.bioName; //"智人"
xiaoming.bioName = "尼安德特人"
xiaoming.bioName; //"尼安德特人"
xiaohong.bioName; //"智人" 震惊了 这个bioName 不是共享的 按照道理来说应该是共用 prototype 的一个属性的那?
xiaoming.bioName === xiaohong.bioName; //false 这里居然显示出来结果
xiaoming.hasOwnProperty("name")// true
xiaoming.hasOwnProperty("bioName")// true !!! 看来是 小名赋值之后给自己天降了个 bioName
xiaoming;
Student.prototype;
//果然可以看出 prototype 里面的 bioName 依然是"智人",而xiaoming是新增bioName属性
原型继承
function Student(props) {
this.name = props.name || 'Unnamed';
}
Student.prototype.hello = function () {
alert('Hello, ' + this.name + '!');
}
//首先在构造函数中相互调用
function PrimaryStudent(props) {
// 调用Student构造函数,绑定this变量:
Student.call(this, props);
this.grade = props.grade || 1;
}
这个时候对于PrimaryStudent 原型链条是,看上去和Student没啥关系
var Reimu = new PrimaryStudent({name :"Reimu"});
QQ20161028-0.png
new PrimaryStudent() ----> PrimaryStudent.prototype ----> Object.prototype ----> null
Student.prototype
QQ20161028-4.png
PrimaryStudent.prototype
QQ20161028-5.png
这个两个家伙各自指向自家
接下来就像是链表插值这类东西的
QQ20161028-6.png来一个空函数F 桥接用 我们最终想实现下面这样的效果
大象塞冰箱分三步 我们狸猫换太子 也走三步 搭四根线
function F() {
}
QQ20161028-1.png
// 把F的原型指向Student.prototype: 这里看上去是一步实际上搭上了两根线
// F -> 原型对象
// F -> 原型对象
F.prototype = Student.prototype;
QQ20161028-2.png
// 把PrimaryStudent的原型指向一个新的F对象,F对象的原型正好指向Student.prototype:
搭一根线 PrimaryStudent -> new F()
PrimaryStudent.prototype = new F();
QQ20161028-3.png
// 把PrimaryStudent原型的构造函数修复为PrimaryStudent:
搭上最后一根线
new F() -> PrimaryStudent
PrimaryStudent.prototype.constructor = PrimaryStudent;
四根线搭上 原来 PrimaryStudent.prototype 指向的 无名原型对象已经被彻底的抛弃了 new F() 已经上位了,取代了无名 原型对象
function inherits(Child, Parent) {
var F = function () {};
F.prototype = Parent.prototype;
Child.prototype = new F();
Child.prototype.constructor = Child;
}
就这样JavaScript终于打肿脸充胖子一样的实现了继承