原文地址:https://github.com/catchonme/blog/issues/3javascript
咱們知道Class
最大的做用就是可以實現繼承,這樣子類經過繼承父類,從而儘量的對代碼進行復用。java
那麼問題來了git
咱們經過分析prototype.js 來看看ES5
是如何實現Class
的github
prototype.js
書寫Class
的方法app
var Animal = Class.create({
initialize: function(name) {
this.name = name;
}
})
var myAnimal = new Animal('jack');
console.log(myAnimal.name);// jack
var Cat = Class.create(Animal, {
initialize: function($super, name, age) {
$super(name);
this.age = age;
}
})
var myCat = new Cat('mia', 13);
console.log(myCat.name); // mia
console.log(myCat.age); // 13
複製代碼
建立類:經過Class.create
建立Class
, initialize
函數初始化Class
的屬性函數
繼承:子類在Class.create
的第一個參數傳入父類名稱,在函數中第一個參數傳入$super
,函數內使用$super(args)
來調用父類的同名函數工具
而後咱們來看prototype.js
內部實現Class
,如下是大體的結構,主體是三個函數,包含一個空函數ui
var Class = (function(){
var subclass() {}; // 空函數
function create() { // code... }
function addMethods() { // code... }
return {
create: create,
Methods: {
addMethods: addMethods
}
}
})();
複製代碼
咱們挨個解析,create
函數作了什麼呢?this
function create() {
var parent = null, properties = [].slice.call(arguments);
// 傳入的第一個參數是不是函數,若是是,就說明是當前類的父類
if (isFunction(properties[0]))
parent = properties.shift();
// 新建 klass 函數,並執行initialize 函數
function klass() {
this.initialize.apply(this, arguments);
}
// extend 函數將 Class.Methods的屬性賦給 klass
extend(klass, Class.Methods);
// 設定當前類爲 parent
klass.superclass = parent;
klass.subclasses = [];
// 若是當前類有父類,就須要把父類的屬性和函數賦給當前類
if (parent) {
// 父類的 prototype 賦給 空函數 subclass.prototype
subclass.prototype = parent.prototype;
// 經過實例化 subclass 再賦給 klass.prototype,這樣 klass 就可以擁有父類的屬性和函數了
klass.prototype = new subclass;
parent.subclasses.push(klass)
}
// addMethods 爲將全部傳入的參數賦給 klass 的 prototype 中,該函數做用後面講解
for (var i=0, length=properties.length; i<length; i++) {
klass.addMethods(properties[i]);
}
if (!klass.prototype.initialize) {
klass.prototype.initialize = emptyFunction
}
klass.prototype.constructor = klass;
return klass
}
複製代碼
create
函數作了哪些事呢
klass
函數,執行initialize
函數addMethods
方法,將用戶的函數賦給klass
的prototype
中繼承父類的做用已經實現了,用戶建立類時的方法須要經過addMethods
賦給當前類的prototype
中,接下來的問題就是,父類和子類的同名函數,該如何執行呢,父類的同名函數已經賦給子類的prototype
中了,子類增長同名函數,爲何不會覆蓋掉呢?spa
咱們看看addMethods
函數
function addMethods(source) {
var ancestor = this.superclass && this.superclass.prototype,
properties = Object.keys(source);
// 遍歷傳遞過來的函數,分別賦給當前的 prototype 中
for (var i=0, length=properties.length; i<length; i++) {
var property = properties[i], value = source[property];
// 判斷當前類中的函數,第一個參數否是是 $super
if (ancestor && isFunction(value)
&& value.argumentNames()[0] == "$super") {
var method = value;
// 這裏是爲何子類可以執行父類的同名函數的關鍵地方
value = (function (m) {
return function () {
// 經過將函數名傳遞給父類,父類執行一次該同名函數,然
return ancestor[m].apply(this, arguments);
}
})(property).wrap(method); // wrap(method) 又執行一次當前類的同名函數
// 重寫當前函數的 valueOf
value.valueOf = (function (method) {
return function () {
return method.valueOf.call(method);
}
})(method);
// 重寫當前函數的 toString
value.toString = (function (method) {
return function () {
return method.toString.call(method);
}
})(method);
}
// 將函數賦給當前類的 prototype
this.prototype[property] = value;
}
}
複製代碼
addMethods
又作了哪些事呢?
$super
,就調用父類的同名函數執行一次,在調用當前類的同名函數執行一次,這樣就可以實現使用$super
調用父類的函數了$super
,就直接賦給當前類的prototype
中上述函數中使用的其餘工具函數,如isFunction
, extend
等,由於我已經把prototype,js
中的Class
單獨剝離出來了,經過點擊 這裏 查看,裏面會有更詳細的註釋。
因此咱們回到最初的問題
prototype
中,這樣子函數就擁有父函數的屬性和函數了superclass
爲父函數,在當前類中全部自定義的函數賦給當前類的prototype
時,會經過superclass
找到父類的同名函數,這樣執行子類的同名函數時,即爲執行父類的同名函數一次,子類的同名函數一次。