CFC and this scope to call a method

Hi,

My questioning is simple, but I cannot find any explanation despite my research.

When calling a method inside a Coldfusion Component, should I use this.theMethodName() or just theMethodName()? Both are working fine. A while time ago, I saw many components calling methods with the this scope, but if I look at more recent components (ex. from Ben Nadel), he call the methods directly without this.

I also remember reading an article suggesting not to use this to call a method inside a CFC, but can’t remember the exact reason.

Without the this. See <cfif getSomething()...

<cfcomponent displayname="example" output="false">

    <cffunction name="doIt" output="false" access="public" returntype="boolean">

        <cfif getSomething() EQ "Hello world">
            <cfreturn true>
        <cfelse>
            <cfreturn false>
        </cfif>

    </cffunction>

    <cffunction name="getSomething" output="false" access="public" returntype="string">

        <cfreturn "Hello world">

    </cffunction>

</cfcomponent>

With the this. See <cfif this.getSomething()...

<cfcomponent displayname="example" output="false">

    <cffunction name="doIt" output="false" access="public" returntype="boolean">

        <cfif this.getSomething() EQ "Hello world">
            <cfreturn true>
        <cfelse>
            <cfreturn false>
        </cfif>

    </cffunction>

    <cffunction name="getSomething" output="false" access="public" returntype="string">

        <cfreturn "Hello world">

    </cffunction>


</cfcomponent>

The advantage of using this to call a method is when I create a method inside my component that have the same name as a native Lucee function like datetimeFormat, if I don’t call it with this.dateTimeFormat, Lucee will call the native datetimeFormat function instead of the one in the component. So I find it better to always use this to make sure Lucee call the methods inside the component and not an external function.

But there is probably a good reason not to use this… Could you enlighten me?

Thank you!

the general rule is always scope except the nearest scope, when in doubt, scope it!

variables isn’t the nearest scope in a cfc function, local is, then arguments

when the scope isn’t specified, it falls back on this logic, which is what the implicit scope access debugging is generated from. it’s slower and the debugging has overhead

this is an odd one…
https://luceeserver.atlassian.net/browse/LDEV-2776

I just added a Scopes category to docs

there are some open scope bugs too

https://luceeserver.atlassian.net/issues/?jql=labels%20%3D%20scope%20ORDER%20BY%20updated%20DESC

5.3.8 also adds this which improves performance by ignoring the query scope

1 Like
component {
    variables.missing_variables = "variables.missing_variables";

    public function init(){
        this.missing_this = "this.missing_this";
        return this;
    }

    private function _private (){
        return variables;
    }

    public function _public (){
        return this;
    }

    public function test(){
        try {
            echo('test: this.missing_this<br>');
            echo(this.missing_this);
        } catch (e){
            echo("<pre>#cfcatch.stacktrace#</pre>");
        }
        echo("<hr>");
        try {
            echo('test: missing_this<br>');
            echo(missing_this);
        } catch (e){
            echo("<pre>#cfcatch.stacktrace#</pre>");
        }
        echo("<hr>");
        try {
            echo('test: _missing_variables<br>');
            echo(missing_variables);
        } catch (e){
            echo("<pre>#cfcatch.stacktrace#</pre>");
        }
        echo("<hr>");
        try {
            echo('test: this["_missing"]<br>');
            echo(this["_missing"]);
        } catch (e){
            echo("<pre>#cfcatch.stacktrace#</pre>");
        }
        echo("<hr>");
        try {
            echo('test: variables["_missing"]<br>');
            echo(variables["_missing"]);
        } catch (e){
            echo("<pre>#cfcatch.stacktrace#</pre>");
        }
        echo("<hr>");
        try {
            echo('test: getVariable("_missing")<br>');
            echo(getVariable("_missing"));
        } catch (e){
            echo("<pre>#cfcatch.stacktrace#</pre>");
        }

    }
}

test: this.missing_this
this.missing_this

test: missing_this
lucee.runtime.exp.ExpressionException: The key [MISSING_THIS] does not exist, but there is a similar key with name [MISSING_VARIABLES] available.
at lucee.runtime.type.scope.UndefinedImpl.get(UndefinedImpl.java:238)
at lucee.runtime.type.scope.UndefinedImpl.get(UndefinedImpl.java:182)
at test_cfc$cf$n.udfCall(/test.cfc:27)
at lucee.runtime.type.UDFImpl.implementation(UDFImpl.java:106)
at lucee.runtime.type.UDFImpl._call(UDFImpl.java:344)
at lucee.runtime.type.UDFImpl.call(UDFImpl.java:217)
at lucee.runtime.ComponentImpl._call(ComponentImpl.java:648)
at lucee.runtime.ComponentImpl._call(ComponentImpl.java:570)
at lucee.runtime.ComponentImpl.call(ComponentImpl.java:1900)
at lucee.runtime.util.VariableUtilImpl.callFunctionWithoutNamedValues(VariableUtilImpl.java:785)
at lucee.runtime.PageContextImpl.getFunction(PageContextImpl.java:1732)
at scope_cfm$cf.call(/scope.cfm:4)

test: _missing_variables
variables.missing_variables

test: this["_missing"]
lucee.runtime.exp.ExpressionException: Component [Test] has no accessible Member with name [_missing]
at lucee.runtime.ComponentImpl.get(ComponentImpl.java:1769)
at lucee.runtime.util.VariableUtilImpl.get(VariableUtilImpl.java:278)
at lucee.runtime.PageContextImpl.get(PageContextImpl.java:1524)
at test_cfc$cf$n.udfCall(/test.cfc:41)
at lucee.runtime.type.UDFImpl.implementation(UDFImpl.java:106)
at lucee.runtime.type.UDFImpl._call(UDFImpl.java:344)
at lucee.runtime.type.UDFImpl.call(UDFImpl.java:217)
at lucee.runtime.ComponentImpl._call(ComponentImpl.java:648)
at lucee.runtime.ComponentImpl._call(ComponentImpl.java:570)
at lucee.runtime.ComponentImpl.call(ComponentImpl.java:1900)
at lucee.runtime.util.VariableUtilImpl.callFunctionWithoutNamedValues(VariableUtilImpl.java:785)
at lucee.runtime.PageContextImpl.getFunction(PageContextImpl.java:1732)
at scope_cfm$cf.call(/scope.cfm:4)

test: variables["_missing"]
lucee.runtime.exp.ExpressionException: Component [Test] has no accessible Member with name [_missing]
at lucee.runtime.ComponentScopeShadow.get(ComponentScopeShadow.java:128)
at test_cfc$cf$n.udfCall(/test.cfc:48)
at lucee.runtime.type.UDFImpl.implementation(UDFImpl.java:106)
at lucee.runtime.type.UDFImpl._call(UDFImpl.java:344)
at lucee.runtime.type.UDFImpl.call(UDFImpl.java:217)
at lucee.runtime.ComponentImpl._call(ComponentImpl.java:648)
at lucee.runtime.ComponentImpl._call(ComponentImpl.java:570)
at lucee.runtime.ComponentImpl.call(ComponentImpl.java:1900)
at lucee.runtime.util.VariableUtilImpl.callFunctionWithoutNamedValues(VariableUtilImpl.java:785)
at lucee.runtime.PageContextImpl.getFunction(PageContextImpl.java:1732)
at scope_cfm$cf.call(/scope.cfm:4)

test: getVariable("_missing")
lucee.runtime.exp.ExpressionException: The key [_missing] does not exist, but there is a similar key with name [MISSING_VARIABLES] available.
at lucee.runtime.type.scope.UndefinedImpl.get(UndefinedImpl.java:238)
at lucee.runtime.type.scope.UndefinedImpl.get(UndefinedImpl.java:182)
at lucee.runtime.type.util.StructSupport.get(StructSupport.java:152)
at lucee.runtime.interpreter.VariableInterpreter.getVariable(VariableInterpreter.java:125)
at lucee.runtime.PageContextImpl.getVariable(PageContextImpl.java:1555)
at lucee.runtime.functions.dynamicEvaluation.GetVariable.call(GetVariable.java:30)
at test_cfc$cf$n.udfCall(/test.cfc:55)
at lucee.runtime.type.UDFImpl.implementation(UDFImpl.java:106)
at lucee.runtime.type.UDFImpl._call(UDFImpl.java:344)
at lucee.runtime.type.UDFImpl.call(UDFImpl.java:217)
at lucee.runtime.ComponentImpl._call(ComponentImpl.java:648)
at lucee.runtime.ComponentImpl._call(ComponentImpl.java:570)
at lucee.runtime.ComponentImpl.call(ComponentImpl.java:1900)
at lucee.runtime.util.VariableUtilImpl.callFunctionWithoutNamedValues(VariableUtilImpl.java:785)
at lucee.runtime.PageContextImpl.getFunction(PageContextImpl.java:1732)
at scope_cfm$cf.call(/scope.cfm:4)

component {
    variables.now2 = function (){
        return "now_variables";
    }

    public function init(){
        return this;
    }

    public function now2 (){
        return "now";
    }

    public function test(){
        try {
            echo('test: now2()<br>');
            echo(now2());
        } catch (e){
            echo("<pre>#cfcatch.stacktrace#</pre>");
        }
        echo("<hr>");

        try {
            echo('test: this.now()<br>');
            echo(this.now2());
        } catch (e){
            echo("<pre>#cfcatch.stacktrace#</pre>");
        }
        echo("<hr>");
    }
}

test: now2()
now_variables

test: this.now()
now_variables

component {
    variables.missing_variables = "variables.missing_variables";
    variables.now2 = function (){
        return "now_variables";
    }

    public function init(){
        this.missing_variables = "this.missing_variables";
        return this;
    }

    public function now2 (){
        return "now";
    }

    public function test(){
        try {
            echo('test: now2()<br>');
            echo(now2());
        } catch (e){
            echo("<pre>#cfcatch.stacktrace#</pre>");
        }
        echo("<hr>");

        try {
            echo('test: this.now2()<br>');
            echo(this.now2());
        } catch (e){
            echo("<pre>#cfcatch.stacktrace#</pre>");
        }
        echo("<hr>");

        try {
            echo('test: missing_variables<br>');
            echo(missing_variables);
        } catch (e){
            echo("<pre>#cfcatch.stacktrace#</pre>");
        }
        echo("<hr>");

        try {
            echo('test: this.missing_variables<br>');
            echo(this.missing_variables);
        } catch (e){
            echo("<pre>#cfcatch.stacktrace#</pre>");
        }

    }
}

test: now2()
now_variables

test: this.now2()
now_variables

test: missing_variables
variables.missing_variables

test: this.missing_variables
this.missing_variables

so

  • this.function() resolves to variables before this
  • this.variable resolves to this before variables

huh?

Bug filed, ACF gets this right, Lucee gets it wrong

https://luceeserver.atlassian.net/browse/LDEV-3296

1 Like

For what it’s worth, the reasons I don’t use variables or this is two-fold.

First, I just like the way the code looks when there are fewer tokens. It feels like less for me to have parse when I am reading it.

Second, and this is also somewhat superficial, but I like that my ColdFusion code and my JavaScript code can look more like each other. In JavaScript, I usually have code that looks like this:

function Test() {

	// Expose the public API.
	return({
		publicMethod: publicMethod
	});

	// ---
	// PUBLIC METHODS.
	// ---

	function publicMethod() {

		return( privateMethod() );

	}

	// ---
	// PRIVATE METHODS.
	// ---

	function privateMethod() {

		return( "private" );

	}

}

And, my ColdFusion version of this would look like this:

component {

	// ---
	// PUBLIC METHODS.
	// ---

	public string function publicMethod() {

		return( privateMethod() );

	}

	// ---
	// PRIVATE METHODS.
	// ---

	private string function privateMethod() {

		return( "private" );

	}

}

As you can see, they two snippets look mostly the same. Though, the irony of this latter point is now that I use a lot of TypeScript, I actually have to use this. in TypeScript (as I am using “Classes” now, not just functions that return public APIs). So, the two looks have begun to diverge.

But, both of my reasons are fairly superficial.

1 Like

Thank you for your answers @Zackster and @bennadel!

I will therefore continue to use the this scope for calling my methods inside a component. I find it clearer by reading the code and it avoids conflicting with native functions. It has already helped me during my transition from ACF to Lucee.

Also glad that this will lead to the correction of bugs. I like to contribute to the evolution of Lucee, directly or indirectly.

Have a good day!

1 Like