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 也没什么使用场景了
来源: https://juejin.im/post/5aa8c67e51882510fd3f51dc