ES5模拟实现面向对象的三大特征:封装、继承、多态

第一次尝试写技术文章,文笔比较渣。内容不是不少,但仍是花费了很多时间。javascript

先前去某互联网公司面试,面试官问能不能说一下怎样用ES5模拟实现面向对象的三大特征:封装、继承、多态,记得当时只是balabala的说了一下js继承的6种方式。java

js继承的6种方式:

  1. 原型链继承
  2. 构造函数继承
  3. 组合继承
  4. 原型式继承
  5. 寄生式继承
  6. 寄生组合式继承

最近有时间,因而就抽空大概封装整理了一下...面试

关于js的继承,网上有不少大佬写的文章,这里就再也不赘述了。数据结构

下面会用到寄生组合式继承的方式来封装咱们的Class方法,首先我认为有两点是须要考虑的:app

一、命名空间namespace的问题函数

就是我声明的这个类须要挂载到哪一个父级下面,就像java里的输入流类InputStream、输出流类OutputStream 都是位于java.io.*包(package)下的,日期类Date、数据结构类HashMap/HashSet 都是位于java.util.*包(package)下的ui

二、实现相似ES6中在子类构造函数里面调用父类的方法(super)this

二话很少说,直接上代码spa

1、实现Namespace函数:
/** * 挂载Class到指定的命名空间下 * @param { String } namespace 以点链接的字符串 * @param { Function } Ctor 被挂载的Class * @param { Object } rootNamespace 顶级命名空间 */
    let Namespace = function(namespace, Ctor, rootNamespace) {
        let nsName, nsNames = namespace.split("."),
            max = nsNames.length - 1,
            index = 0;
        if (!rootNamespace) try {
            if (!new RegExp("^[a-zA-Z_$][a-zA-Z0-9_$]*$").test(nsNames[0])) 
            	throw "";
            rootNamespace = new Function("return " + nsNames[0])(), index = 1
        } catch (i) {
            rootNamespace = window
        }
        for (; max > index; index++) 
        	nsName = nsNames[index], rootNamespace[nsName] || (rootNamespace[nsName] = {}), rootNamespace = rootNamespace[nsName];
        rootNamespace[nsNames[max]] || (rootNamespace[nsNames[max]] = Ctor)
    };
复制代码
2、实现相似call super,须要添加一个默认的顶级父类SuperClass
let SuperClass = function() {},
        _proto_ = new Object;
    _proto_.superclass = Object, _proto_.__NAME__ = "Object", _proto_.superinstance = new Object;
    _proto_.callsuper = function(a) {
        let self = this;
        // 传入a为父类的方法名
        if (this._realsuper = this._realsuper ? this._realsuper.prototype.superclass : this.superclass, "string" == typeof a) {
            let args = Array.prototype.slice.call(arguments, 1);
            self._realsuper.prototype[a].apply(self, args)
        } else {
            let args = Array.prototype.slice.call(arguments, 0);
            self._realsuper.apply(self, args)
        }
        this._realsuper = null // 调用后置空,便于下次再次调用 
    }, SuperClass.prototype = _proto_;
复制代码
3、Class方法封装
let Class = function(className, oArgs) {
        let ns = oArgs.ns && oArgs.ns + "." + className;
        if (ns) try {
            let clazz = new Function("return " + ns)();
            if (clazz) return clazz // 存在则直接返回
        } catch (g) {}

        let superClass = oArgs.extend || SuperClass,
            fn = function() {},
            plugins = oArgs.plugins || [];
        fn.prototype = superClass.prototype;
        let construct = oArgs.construct || function() {},
            properties = oArgs.properties || {},
            methods = oArgs.methods || {},
            statics = oArgs.statics || {},
            _proto_ = new fn;
        for (let property in _proto_) _proto_.hasOwnProperty(property) && delete _proto_[property];
        for (let prop in properties) _proto_[prop] = properties[prop];
        for (let methodName in methods) _proto_[methodName] = methods[methodName];
        for (let index = 0; index < plugins.length; index++) {
            let plugin = plugins[index];
            for (let p in plugin) _proto_[p] = plugin[p]
        }
        _proto_.constructor = construct, _proto_.superclass = superClass, _proto_.superinstance = new fn, _proto_.__NAME__ = className, construct.prototype = _proto_;
        for (let p in statics) construct[p] = statics[p];
        return ns && Namespace(ns, construct), construct
    };
复制代码

至此,封装完成prototype

调用方式以下:
let myClass = Class("Test", {
    ns: "", // 命名空间
    extend: SuperClass, // 父类、可选
    plugins: [], // 插件类
    construct: function(e) { // 构造函数,能够在这里执行callsuper
    	...
        this.callsuper(e)
    },
    statics: {}, // 静态属性和方法
    methods: {}  // 实例属性和方法
});
复制代码
相关文章
相关标签/搜索