ES6里玩接口

作者: LostAbaddon | 来源:发表于2015-12-31 16:49 被阅读5024次

    OOP里,class和interface是很有用的两个东西,废话。
    在JS/ES中,class被prototype取代,而interface则完全没影。
    那么,class和interface到底有什么区别呢?

    如果只是将class和interface看做是定义了一套方法的话,那么class和interface之间的区别大概只能看做是前者实现了方法(abstract的当然就没实现了),而后者没实现这套方法了。
    所以class可以用作代码复用,而interface显然不能。
    从这个角度来看的话,两者的区别的确不大。
    不过这其实不是重点。
    重点在于,从C++时代后开始,class不能多重继承就(C++里当然还是可以的),但interface却可以多重实现。
    因此,class和interface的区别就体现了出来:class的作用更多的想是设计蓝图,而interface则是不同对象之间互动的协议。
    也就是说,class规范了object的性质,而interface规范了object之间的交互
    interface给出了在一个上下文环境中一个对象可以提供的服务,并且对于超出的部分不予理睬。

    好了,明白了这点,就可以来搞一套虽然完全没什么意义但还是很好玩的ES6的实现了——

    其实这套方案ES5里也能玩。

    var ClassTest;
    
    (function () {
        var id = 0;
        ClassTest = class {
            constructor (name) {
                var _id = id;
                this.name = name;
                Object.defineProperty(this, 'id', {
                    configurable: false,
                    enumerable: true,
                    get: function () {
                        return _id;
                    },
                    set: function (nouse) {},
                });
                id ++;
                this.value = 0;
            }
    
            toString () {
                return "I'm " + this.name + ", the " + this.id + "nth child of THE DARK LORD!";
            }
    
            add (value) {
                this.value += value;
                return this;
            }
            minus (value) {
                this.value -= value;
                return this;
            }
            time (value) {
                this.value *= value;
                return this;
            }
            divide (value) {
                this.value /= value;
                return this;
            }
    
            Add (value) {
                this.value += value;
                return this.value;
            }
            Minus (value) {
                this.value -= value;
                return this.value;
            }
            Time (value) {
                this.value *= value;
                return this.value;
            }
            Divide (value) {
                this.value /= value;
                return this.value;
            }
        };
    })();
    var test1 = new ClassTest('Aloha'), test2 = new ClassTest('Kosmos');
    
    class ClassOther {
        constructor () {
            this.name = "MiaoWu~~~";
            this.id = 0;
        }
    
        toString () {
            return "I'm Nothing...";
        }
    }
    var other = new ClassOther();
    
    class Interface {
        constructor (kernel, options) {
            options = options || {};
            if (!!options.changable) {
                Object.defineProperty(this, 'kernel', {
                    configurable: false,
                    enumerable: false,
                    writable: true,
                    value: kernel,
                });
            }
            else {
                Object.defineProperty(this, 'kernel', {
                    configurable: false,
                    enumerable: false,
                    get: function () {
                        return kernel;
                    },
                    set: function (nouse) {},
                });
            }
            if (!!options.frozen) {
                Object.freeze(this);
            }
        }
    
        get value () {
            if (isNaN(this.kernel.value)) return 0;
            else return this.kernel.value;
        }
        set value (nouse) {}
        get description () {
            if (!!this.kernel.toString) return this.kernel.toString();
            else return "EMPTY";
        }
        set description (nouse) {}
    
        add (value) {
            if (!!this.kernel.add) return this.kernel.add(value);
            else return this;
        }
        minus (value) {
            if (!!this.kernel.minus) return this.kernel.minus(value);
            else return this;
        }
        time (value) {
            if (!!this.kernel.time) return this.kernel.time(value);
            else return this;
        }
        divide (value) {
            if (!!this.kernel.divide) return this.kernel.divide(value);
            else return this;
        }
    
    }
    
    var int1 = new Interface(test1, { changable: true });
    var int2 = new Interface(test2, { frozen: true });
    

    在上面的例子中,我们先定义了两个类(ES6中的类基本可以看做Syntactic Sugar),然后构造了一个接口。
    这个接口的作用非常简单,就是将真正的数据与操作行为分离开,从而操作层的错误操作不会影响到真正的数据。
    事实上,这里我们完全可以再定义一个接口只实现Add、Minus、Time和Divide这四个函数。
    从而我们可以看到,通过不同的接口,我们只能操作特定的方法、访问特定的属性,且任何在接口上的操作都不会影响到真正的数据——除非提供了方法让你去修改。
    从而,这个模型就可以实现M与C的隔离。

    其中还提供了两个参数,一个用来控制接口实例是否可以替换真正的数据,而另一个则控制接口实例是否可以拥有自己的属性。

    从某种程度来说,这个Interface就是给一个真正的Data穿上了一件衣服,只露出那些想要露出的,而将那些不想露出的部分遮掩起来。

    当然啦,最后还是要说:这套方案其实并没有什么真正的大用处——我直接访问Interface实例的kernel对象不就好了(当然这样的话一个很鸡贼的方式就是直接把所有方法与属性全部写在constructor里从而这个Interface没有prototype从而可以保证你访问不到kernel)。
    毕竟ES不是OOP,硬将OOP中的东西弄到ES中很多时候是没必要的,反而破坏了ES本身Prototype Chain的美好——所以当我看到ES6里OOP的味道加重后总是感觉很不适应……(大家别理我这枚异端)

    最后PS一下:上面所说的方案其实和ES6半毛钱关系都没有,在ES5里都能做到毫无障碍,科科。

    相关文章

      网友评论

      本文标题:ES6里玩接口

      本文链接:https://www.haomeiwen.com/subject/fyzihttx.html