def.js是之前在研究js繼承的時候發現的一個很有趣的js,恰巧現在前端團隊中每週進行一次分享,就翻出來整理成一篇小文章
先看一段def.js實現繼承的方式
def ("Person") ({
init: function(name){
this.name = name;
},
speak: function(text){
alert(text || "Hi, my name is " + this.name);
}
});
def ("Ninja") < Person ({
init: function(name){
this._super();
},
kick: function(){
this.speak("I kick u!");
}
});
var ninjy = new Ninja("JDD");
ninjy.speak();
ninjy.kick();
複製程式碼
可以看到是以 < 實現的Ninja繼承於Person
js中,當兩個物件進行算術運算或大小比較時,如果運算子兩邊不是數值,則呼叫valueOf方法,所以def.js一定是重寫了valueOf,在裡面實現了繼承
下面看def.js的原始碼(為了閱讀上下文方便,刪除了中間空行)
(function(global){
// def方法返回就是deferred方法,所以在繼承時執行的valueOf為下文中看到的deferred.valueOf
var deferred;
// extend方法是通過混入的方式把deferred接收到的引數作為物件屬性
function extend(source){
var prop, target = this.prototype;
for(var key in source) if(source.hasOwnProperty(key)){
prop = target[key] = source[key];
if(`function` == typeof prop){
// 這兩個屬性是為了下文中實現._super呼叫超類的同名方法,起到標記作用
prop._name = key;
prop._class = this;
}
}
return this;
}
// 此方法用來呼叫超類的同名方法
function base(){
// callee已經在es5的嚴格模式中被棄用
var caller = arguments.callee.caller;
return caller._class._super.prototype[caller._name]
.apply(this, arguments.length ? arguments : caller.arguments);
}
function def(context, klassName){
klassName || (klassName = context, context = global);
// 在此處把通過def方法建立的類掛載到了global上
var Klass = context[klassName] = function Klass(){
if(context != this){
return this.init && this.init.apply(this, arguments);
}
// 把當前物件記錄到deferred中,方便<左側繼承
deferred._super = Klass;
// 把超類後面的引數暫存在繼承過後再把這些屬性進行混入,類似一開始的例子中Person後面跟的引數
deferred._props = arguments[0] || { };
}
Klass.extend = extend;
deferred = function(props){
return Klass.extend(props);
};
function Subclass(){ }
deferred.valueOf = function(){
var Superclass = deferred._super;
if(!Superclass){
return Klass;
}
Subclass.prototype = Superclass.prototype;
var proto = Klass.prototype = new Subclass;
Klass._class = Klass;
Klass.toString = function(){
return klassName;
};
proto.constructor = Klass;
// 這個地方我把這兩句放到一起的原因,一個_super是在Klass物件上的,一個是在Klass.prototype上的
// 不明白的請複習原型鏈跟new的原理
Klass._super = Superclass;
proto._super = base;
deferred(deferred._props);
};
return deferred;
}
global.def = def;
}(this));
複製程式碼
最後還有一點就是def ("Ninja") < Person()
這句話的執行順序
- def(“Ninja”)返回一個deferred
- Person()設定deferred._super跟deferred._proto
- 左側執行valueOf即deferred.valueOf實現繼承
當然如今es8都要來了,def.js也沒什麼使用場景了