原型鏈:
JavaScript的面向對象編程和大多數其他語言如Java、C#的面向對象編程都不太一樣。如果你熟悉Java或C#,很好,你一定明白麪向對象的兩個基本概念:
-
類:類是對象的類型模板,例如,定義
Student
類來表示學生,類本身是一種類型,Student
表示學生類型,但不表示任何具體的某個學生; -
實例:實例是根據類創建的對象,例如,根據
Student
類可以創建出xiaoming
、xiaohong
、xiaojun
等多個實例,每個實例表示一個具體的學生,他們全都屬於Student
類型。
所以,類和實例是大多數面向對象編程語言的基本概念。
不過,在JavaScript中,這個概念需要改一改。JavaScript不區分類和實例的概念,而是通過原型(prototype)來實現面向對象編程。
JavaScript的原型鏈和Java的Class區別就在,它沒有“Class”的概念,所有對象都是實例,所謂繼承關係不過是把一個對象的原型指向另一個對象而已。
// 原型對象:
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
。
區別構造函數和普通函數:
構造函數首字母最好大寫;在創建調用的使用需要用new關鍵字調用
普通函數首字母小寫,在創建調用的時候不需要用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
對象之間是可以繼承的,在EC6中新增加了class關鍵字,類似於java中的繼承,但是要用super(name)來調用父類的構造函數,否則父類屬性無法正常使用
class PrimaryStudent extends Student {
constructor(name, grade) {
super(name); // 記得用super調用父類的構造方法!
this.grade = grade;
}
myGrade() {
alert('I am at grade ' + this.grade);
}
}
在EC6之前可以通過一個空的構造函數做中介來實現繼承關係