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; // true
Student.prototype.constructor === Student; // true
Object.getPrototypeOf(xiaoming) === Student.prototype; // true
xiaoming 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);
}
}