PS:
本文参考了司徒正美的《JavaScript框架设计》,以及许多其它的书籍和网络文章,感谢作者们分享了这么好的学习资源!
关于JS的类和继承的原理,见:JavaScript里的类和继承
文中提到的库和测试文件戳这里:https://github.com/hellobugme/jsclass/

 

1、基于拷贝继承


Prototypehttp://prototypejs.org/)和 jQueryhttps://jquery.com/)都是风靡一时的库,功能大而全,并不是纯粹的类工厂,这里只是简单说下里面的继承。
它们的 extend 都是基于拷贝继承,其中 jQuery 支持 深度拷贝(deep copy)
拷贝继承的缺点在上篇文章中有提过:子类实例无法通过父类的 instanceof 验证

Prototype 中类和继承相关的代码如下:

var Class = {
    create: function() {
        return function() {
            this.initialize.apply(this, arguments);
        }
    }
};
Object.extend = function(destination, source) {
    for (var property in source) {
        destination[property] = source[property];
    }
    return destination;
};

是吧,够简单的… 只不过是使用 Class.create() 创建出来的类,在实例化时会主动帮你执行下初始化函数 initialize 而已。使用方法大致如下:

var Person = Class.create();
Object.extend(Person.prototype, {
    initialize: function(name) {
        this.name = name;
    },
    getName: function() {
        return this.name;
    }
});
var User = Class.create();
User.prototype = Object.extend(new Person(), {
    initialize: function(name, password) {
        this.name = name;
        this.password = password;
    },
    getPassword: function() {
        return this.password;
    }
});

jQuery 在 extend 的实现中加入递归,对数组和对象等引用类型的属性值进行深度拷贝:

... 表示一大波的代码,下同

jQuery.extend = jQuery.fn.extend = function() {
    //...
    for (name in options) {
        src = target[name];
        copy = options[name];
        //...
        if (deep && copy && (jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)))) {
            if (copyIsArray) {
                copyIsArray = false;
                clone = src && jQuery.isArray(src) ? src : [];
            } else {
                clone = src && jQuery.isPlainObject(src) ? src : {};
            }
            // 通过递归对数组和对象进行深度拷贝
            target[name] = jQuery.extend(deep, clone, copy);
        } else if (copy !== undefined) {
            target[name] = copy;
        }
    }
    //...
};

deep 是可选参数,指定是否进行深度拷贝。

通过 jQuery.isPlainObject() 和 jQuery.isArray() 判断属性值是否为纯粹对象或数组,然后决定是递归操作还是直接赋值。

 

2、Sugar


道爷(Douglas Crockford)在自己的网站上提出了一套简单的方法来模拟类式继承—— Sugarhttp://javascript.crockford.com/inheritance.html)。

 1 // 辅助函数,可以将新函数绑定到对象的 prototype 上
 2 Function.prototype.method = function(name, func) {
 3     this.prototype[name] = func;
 4     return this;
 5 };
 6 
 7 // 从其它对象继承函数,同时仍然可以调用数据父对象的那些函数
 8 Function.method('inherits', function(parent) {
 9     // 继承父对象的方法
10     this.prototype = new parent();
11     this.prototype.constructor = parent;
12 
13     var d = {},
14         p = this.prototype;
15     // 创建一个新的特权函数'uber',调用它时会执行所有在继承时被重写的函数
16     this.method('uber', function uber(name) {
17         if (!(name in d)) {
18             d[name] = 0;
19         }
20         var f, // 要执行的函数
21             r, // 函数的返回值
22             t = d[name], // 记录当前所在的父层次的级数
23             v = parent.prototype; // 父对象的prototype
24 
25         // 如果已经在某个'uber'函数之内
26         if (t) {
27             // 上溯必要的t,找到原始的prototype
28             while (t) {
29                 v = v.constructor.prototype;
30                 t -= 1;
31             }
32             // 从该prototype中获得函数
33             f = v[name];
34             // 否则这就是'uber'函数的第一次调用
35         } else {
36             // 从prototype获得要执行的函数
37             f = p[name];
38             // 如果此函数属于当前的prototype
39             if (f == this[name]) {
40                 // 则改为调用父对象的prototype
41                 f = v[name];
42             }
43         }
44 
45         // 记录在继承堆栈中所在位置的级数
46         d[name] += 1;
47 
48         // 使用除第一个以外所有的arguments调用此函数
49         r = f.apply(this, Array.prototype.slice.apply(arguments, [1]));
50         // 恢复继承堆栈
51         d[name] -= 1;
52 
53         // 返回执行过的函数的返回值
54         return r;
55     });
56     return this;
57 });
58 
59 // 只继承父对象特定函数的函数(非使用new parent()继承所有的函数)
60 Function.method('swiss', function(parent) {
61     // 遍历所有要继承的方法
62     for (var i = 1; i < arguments.length; i += 1) {
63         // 需要导入的方法名
64         var name = arguments[i];
65         // 将此方法导入this对象的prototype中
66         this.prototype[name] = parent.prototype[name];
67     }
68     return this;
69 });
View Code

相关文章:

  • 2021-07-06
  • 2021-08-27
  • 2021-12-06
  • 2021-07-28
  • 2022-12-23
  • 2022-12-23
  • 2022-02-04
  • 2021-04-09
猜你喜欢
  • 2021-04-13
  • 2021-09-06
  • 2021-05-20
  • 2021-10-13
  • 2021-06-23
  • 2022-12-23
相关资源
相似解决方案