JavaScript不区分类和实例的概念,而是通过原型(prototype)来实现面向对象编程。
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(); // 小明 is running...
JavaScript的原型链和Java的Class区别就在,它没有“Class”的概念,所有对象都是实例,所谓继承关系不过是把一个对象的原型指向另一个对象而已。Object.create()方法可以传入一个原型对象,并创建一个基于该原型的新对象,但是新对象什么属性都没有,因此,我们可以编写一个函数来创建xiaoming:
// 原型对象:var Student = {name: 'Robot',height: 1.2,run: function () {console.log(this.name + ' is running...');}};function createStudent(name) {// 基于Student原型创建一个新对象:var s = Object.create(Student);// 初始化新对象:s.name = name;return s;}var xiaoming = createStudent('小明');xiaoming.run(); // 小明 is running...xiaoming.__proto__ === Student; // true
创建对象
JavaScript对每个创建的对象都会设置一个原型,指向它的原型对象。
当我们用obj.xxx访问一个对象的属性时,JavaScript引擎先在当前对象上查找该属性,如果没有找到,就到其原型对象上找,如果还没有找到,就一直上溯到Object.prototype对象,最后,如果还没有找到,就只能返回undefined。
构造函数
除了直接用{ ... }创建一个对象外,JavaScript还可以用一种构造函数的方法来创建对象。它的用法是,先定义一个构造函数:
function Student(name) {this.name = name;this.hello = function () {alert('Hello, ' + this.name + '!');}}//在JavaScript中,可以用关键字new来调用这个函数,并返回一个对象var xiaoming = new Student('小明');xiaoming.name; // '小明'xiaoming.hello(); // Hello, 小明!
用new Student()创建的对象还从原型上获得了一个constructor属性,它指向函数Student本身:
xiaoming.constructor === Student.prototype.constructor; // trueStudent.prototype.constructor === Student; // trueObject.getPrototypeOf(xiaoming) === Student.prototype; // truexiaoming instanceof Student; // true
对new的封装
为了避免忘记写new,我们还可以编写一个createStudent()函数,在内部封装所有的new操作。一个常用的编程模式像这样:
function Student(props) {this.name = props.name || '匿名'; // 默认值为'匿名'this.grade = props.grade || 1; // 默认值为1}Student.prototype.hello = function () {alert('Hello, ' + this.name + '!');};function createStudent(props) {return new Student(props || {})}
这个createStudent()函数有几个巨大的优点:一是不需要new来调用,二是参数非常灵活,可以不传,也可以这么传:
var xiaoming = createStudent({name: '小明'});xiaoming.grade; // 1
原型继承
class继承
新的关键字class从ES6开始正式被引入到JavaScript中。class的目的就是让定义类更简单。
我们先回顾用函数实现Student的方法:
function Student(name) {this.name = name;}Student.prototype.hello = function () {alert('Hello, ' + this.name + '!');}
如果用新的class关键字来编写Student,可以这样写:
class Student {constructor(name) {this.name = name;}hello() {alert('Hello, ' + this.name + '!');}}
最后,创建一个Student对象代码和前面章节完全一样:
var xiaoming = new Student('小明');xiaoming.hello();
用class定义对象的另一个巨大的好处是继承更方便了。现在,原型继承的中间对象,原型对象的构造函数等等都不需要考虑了,直接通过extends来实现:
class PrimaryStudent extends Student {constructor(name, grade) {super(name); // 记得用super调用父类的构造方法!this.grade = grade;}myGrade() {alert('I am at grade ' + this.grade);}}
