使用显式接收器调用ColdFusion方法

使用显式接收器调用ColdFusion方法,coldfusion,mixins,cfc,coldfusion-2016,Coldfusion,Mixins,Cfc,Coldfusion 2016,我想用ColdFusion写一个混音 ExampleMixin.cfc: component { remote void function mixin(component, methodName) { var original = component[methodName]; component[methodName] = function() { writeOutput("Mixin!"); return

我想用ColdFusion写一个混音

ExampleMixin.cfc:

component {
    remote void function mixin(component, methodName) {
        var original = component[methodName];
        component[methodName] = function() {
            writeOutput("Mixin!");
            return original(arguments);
        };
    }
}
component {
    new ExampleMixin().mixin(this, 'foo');

    remote string function foo() {
        return getOutput();
    }

    private string function getOutput() {
        return "Hello, World!";
    }
}
测试。cfc:

component {
    remote void function mixin(component, methodName) {
        var original = component[methodName];
        component[methodName] = function() {
            writeOutput("Mixin!");
            return original(arguments);
        };
    }
}
component {
    new ExampleMixin().mixin(this, 'foo');

    remote string function foo() {
        return getOutput();
    }

    private string function getOutput() {
        return "Hello, World!";
    }
}
运行
foo
会产生错误,
变量GETOUTPUT未定义。
。如果我注释掉
newexamplemixin().mixin(这个“foo”),运行良好


看起来当从包装器运行
foo
时,它没有在正确的上下文中运行。在JavaScript中,可以编写
foo.call(组件,…参数)
来纠正这一点。ColdFusion中是否有等效项?

ColdFusion使用
this
变量
作用域来存储函数 参考资料。所使用的引用取决于函数的调用方式。如果 函数从同级调用,使用
变量
引用。如果 函数正在外部调用,然后使用
引用

下面的代码使用基类来提供mixin功能。这个
$mixin
函数获取组件实例并注入其所有函数。 如果出现名称冲突,包装器将首先调用mixin,然后调用 原始功能。我正在为原始函数和 mixin函数,以便可以在两个作用域中设置引用

这在Lucee 5.2.8.50上进行了测试

可混合。cfc

component {
    function $mixin(obj) {
        var meta = getComponentMetadata(obj);

        for(var func in meta.functions) {
            if(structKeyExists(this, func.name)) {
                var orig = func.name & replace(createUUID(), '-', '', 'all');
                var injected = func.name & replace(createUUID(), '-', '', 'all');

                this[orig] = this[func.name];
                variables[orig] = this[func.name];

                this[injected] = obj[func.name];
                variables[injected] = obj[func.name];

                var wrapper = function() {
                    this[injected](argumentCollection=arguments);
                    return this[orig](argumentCollection=arguments);
                };
                this[func.name] = wrapper;
                variables[func.name] = wrapper;
            } else {
                this[func.name] = obj[func.name];
                return variables[func.name] = obj[func.name];
            }
        }
    }
}
component extends="mixable" {
    remote function foo() {
        writeOutput("foo(), calling bar()<br>");
        bar();
    }

    private function bar() {
        writeOutput("bar()<br>");
    }
}
component {
    function foo() {
        writeOutput("foo mixin, calling bar()<br>");
        bar();
    }

    function myfunc() {
        writeOutput("myfunc()<br>");
    }
}
测试cfc

component {
    function $mixin(obj) {
        var meta = getComponentMetadata(obj);

        for(var func in meta.functions) {
            if(structKeyExists(this, func.name)) {
                var orig = func.name & replace(createUUID(), '-', '', 'all');
                var injected = func.name & replace(createUUID(), '-', '', 'all');

                this[orig] = this[func.name];
                variables[orig] = this[func.name];

                this[injected] = obj[func.name];
                variables[injected] = obj[func.name];

                var wrapper = function() {
                    this[injected](argumentCollection=arguments);
                    return this[orig](argumentCollection=arguments);
                };
                this[func.name] = wrapper;
                variables[func.name] = wrapper;
            } else {
                this[func.name] = obj[func.name];
                return variables[func.name] = obj[func.name];
            }
        }
    }
}
component extends="mixable" {
    remote function foo() {
        writeOutput("foo(), calling bar()<br>");
        bar();
    }

    private function bar() {
        writeOutput("bar()<br>");
    }
}
component {
    function foo() {
        writeOutput("foo mixin, calling bar()<br>");
        bar();
    }

    function myfunc() {
        writeOutput("myfunc()<br>");
    }
}

您可能想改用继承(扩展cfc的属性)。Test.cfc不会对新创建的ExampleMixin.cfc实例执行任何操作,因此无法执行任何操作。请看,继承是独占的。一个组件可能有多个mixin,还可能有一个父组件。如果ColdFusion支持多重继承,那么使用继承实现混合是合理的,但不幸的是它不支持。ColdFusion支持与Java或C#相同的继承模型,因此:
A
可以继承自
B
可以继承自
C
可以继承自
D
等。您可以将mixin设置为抽象基类并从中开始。或者,您可以简单地将所有mixin函数放在包含在其他组件中的
.cfm
模板中。这将不允许组件使用两个独立的mixin,或任何mixin和不使用该mixin的父类。单一继承对于混合不是一个可行的模型。使用
cfinclude
then(将每个函数放在一个单独的模板中)并混合组件所需的任何内容。没有实际的用例,很难判断是否真的需要使用mixin。