首先来首歌曲来放松一下吧!
有人这样说:这种高大上的东西都是造火箭才用得上的,平时干的都是拧螺丝的活当然用不上咯!
但还是得知道并掌握。。。。!
class继承需要掌握,原型继承知道就好了…
一、面向对象编程概述
JavaScript不区分类和实例的概念,而是通过原型(prototype)来实现面向对象编程。
1、通过__proto__ 来指向Student,并且继承Student的所有属性!
JavaScript它没有“Class”的概念,所有对象都是实例,所谓继承关系不过是把一个对象的原型指向另一个对象而已。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| var Student = { name: 'Robot', height: 1.2, run: function () { console.log(this.name + ' is running...'); } };
var xiaoming = { name: '小明' };
xiaoming.__proto__ = Student;
xiaoming.name; xiaoming.run();
|
2、指向(原型)是可以改变的!
在指向一个类后,可以直接修改他的指向(原型)!
1 2 3 4 5 6 7 8 9 10 11 12
| var Bird = { fly: function () { console.log(this.name + ' is flying...'); } };
var xiaoming = { name: '小明' };
xiaoming.__proto__ = Bird; xiaoming.fly();
|
注意:不建议使用`__proto__`来改变指向
3、要使用Object.create()方法
该方法可以传入一个原型对象,并创建一个基于该原型的新对象,但是新对象什么属性都没有
为了方便,可以创建一个函数来接收姓名参数,在函数内部做一下赋值的过程即可!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| var Student = { name: 'Robot', height: 1.2, run: function () { console.log(this.name + ' is running...'); } };
function createStudent(name) { var s = Object.create(Student); s.name = name; return s; }
let xiaoming = Object.create(Student);
var xiaoming = createStudent('小明'); xiaoming.run(); xiaoming.__proto__ === Student;
|
二、关于proto和prototype
prototype是函数的一个属性(每个函数都有一个prototype属性),这个属性是一个指针,指向一个对象。它是显示修改对象的原型的属性。
__proto__是一个对象拥有的内置属性(请注意:prototype是函数的内置属性,__proto__是对象的内置属性),是JS内部使用寻找原型链的属性。
区别参考链接:https://www.cnblogs.com/yangjinjin/archive/2013/02/01/2889103.html
三、创建对象
为了区分普通函数和构造函数,按照约定,构造函数首字母应当大写,而普通函数首字母应当小写.
这样,一些语法检查工具如jslint将可以帮你检测到漏写的new。
本节参考:https://www.liaoxuefeng.com/wiki/1022910821149312/1023022043494624
1、原型链
当我们用obj.xxx访问一个对象的属性时,JavaScript引擎先在当前对象上查找该属性,如果没有找到,就到其原型对象上找,如果还没有找到,就一直上溯到Object.prototype对象,最后,如果还没有找到,就只能返回undefined。
数组原型链
Array.prototype定义了indexOf()、shift()等方法,因此你可以在所有的Array对象上直接调用这些方法。
1 2
| var arr = [1, 2, 3]; arr ----> Array.prototype ----> Object.prototype ----> null
|
函数原型链
Function.prototype定义了apply()等方法,因此,所有函数都可以调用apply()方法。
1 2 3 4
| function foo() { return 0; } foo ----> Function.prototype ----> Object.prototype ----> null
|
2、构造函数
用new来调用函数,返回一个对象!
函数内部的this指向新创建的对象!并默认返回this!不需要写return了!
必须写new,不写new,函数返回的是undefined!
1 2 3 4 5 6 7 8 9 10
| function Student(name) { this.name = name; this.hello = function () { alert('Hello, ' + this.name + '!'); } }
var xiaoming = new Student('小明'); xiaoming.name; xiaoming.hello();
|
此时小明的原型链:
1
| xiaoming ----> Student.prototype ----> Object.prototype ----> null
|
3、constructor
用new Student()创建的对象还从原型上获得了一个constructor属性,它指向函数Student本身:
1 2 3 4 5 6
| xiaoming.constructor === Student.prototype.constructor; Student.prototype.constructor === Student;
Object.getPrototypeOf(xiaoming) === Student.prototype;
xiaoming instanceof Student;
|
4、公用函数(共享方法)
如果我们通过new Student()创建了很多对象,这些对象的hello函数实际上只需要共享同一个函数就可以了,这样可以节省很多内存。
通过够着函数.prototype.函数名来创建公用函数,节省内存!
1 2 3 4 5 6 7
| function Student(name) { this.name = name; }
Student.prototype.hello = function () { alert('Hello, ' + this.name + '!'); };
|
5、解决忘写new的方法
我们还可以编写一个createStudent()函数,在内部封装所有的new操作。
|| :用或符号来实现默认值,毕竟是就近原则的!
将new封装后,即可不用写new了,以防忘写!
1 2 3 4 5 6 7 8 9 10 11 12
| function Student(props) { this.name = props.name || '匿名'; this.grade = props.grade || 1; }
Student.prototype.hello = function () { alert('Hello, ' + this.name + '!'); };
function createStudent(props) { return new Student(props || {}) }
|
这样传参传的是一个对象,是不需要顺序的!
1 2 3 4 5
| var xiaoming = createStudent({ name: '小明' });
xiaoming.grade;
|
四、原型继承
本节参考:https://www.liaoxuefeng.com/wiki/1022910821149312/1023021997355072
原型链:
1
| new PrimaryStudent() ----> PrimaryStudent.prototype ----> Student.prototype ----> Object.prototype ----> null
|
我们必须借助一个中间对象来实现正确的原型链,这个中间对象的原型要指向Student.prototype。为了实现这一点,参考道爷(就是发明JSON的那个道格拉斯)的代码,中间对象可以用一个空函数F来实现:
F空函数起到一个桥接的作用!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| function PrimaryStudent(props) { Student.call(this, props); this.grade = props.grade || 1; }
function F() { }
F.prototype = Student.prototype;
PrimaryStudent.prototype = new F();
PrimaryStudent.prototype.constructor = PrimaryStudent;
PrimaryStudent.prototype.getGrade = function () { return this.grade; };
var xiaoming = new PrimaryStudent({ name: '小明', grade: 2 }); xiaoming.name; xiaoming.grade;
xiaoming.__proto__ === PrimaryStudent.prototype; xiaoming.__proto__.__proto__ === Student.prototype;
xiaoming instanceof PrimaryStudent; xiaoming instanceof Student;
|
关于call()函数:https://www.runoob.com/w3cnote/js-call-apply-bind.html
是用来重定义 this 这个对象的!
五、class继承
在上面的章节中我们看到了JavaScript的对象模型是基于原型实现的,特点是简单,缺点是理解起来比传统的类-实例模型要困难,最大的缺点是继承的实现需要编写大量代码,并且需要正确实现原型链。
有没有更简单的写法?有!
新的关键字class从ES6开始正式被引入到JavaScript中。class的目的就是让定义类更简单。
用class来写,上面章节中的继承和对象编程就简单多了!
constructor很明显是够着函数!
注意没有function关键字!
1 2 3 4 5 6 7 8 9 10 11
| class Student { constructor(name) { this.name = name; }
hello() { alert('Hello, ' + this.name + '!'); } } var xiaoming = new Student('小明'); xiaoming.hello();
|
class继承
用class定义对象的另一个巨大的好处是继承更方便了。想一想我们从Student派生一个PrimaryStudent需要编写的代码量。现在,原型继承的中间对象,原型对象的构造函数等等都不需要考虑了,直接通过extends来实现:
extends表示原型链对象来自Student!
使用super()函数来调用父类的(即Student)的构造方法!
class继承和原型继承没有任何区别,class的作用就是让JavaScript引擎去实现原来需要我们自己编写的原型链代码。简而言之,用class的好处就是极大地简化了原型链代码。
1 2 3 4 5 6 7 8 9 10
| class PrimaryStudent extends Student { constructor(name, grade) { super(name); this.grade = grade; }
myGrade() { alert('I am at grade ' + this.grade); } }
|
本章节到此结束,敬请期待后续章节!