Tuesday, April 22, 2025
HomeJavaScriptContemplating Polyfill Perform Mechanics And Hoisting In ColdFusion

Contemplating Polyfill Perform Mechanics And Hoisting In ColdFusion


In my work, I exploit each Adobe ColdFusion and Lucee CFML. The performance in these utility severs overlap to a fantastic diploma. Nonetheless, they every have built-in features which are distinctive. Those that all the time leap to thoughts for me are Lucee’s dump() and structValueArray() features. Now, dump() is admittedly simply an alias for ColdFusion’s writeDump() perform; however, making a perform alias, which I checked out earlier, is totally different than making a polyfill. Not like an alias, which is all the time utilized, a polyfill is barely meant to be utilized in a context wherein it’s wanted. And, implementing this in ColdFusion is usually a little complicated.

In Adobe ColdFusion, there is not any native, documented technique to create a globally obtainable perform. So, no matter we do, there will likely be artifacts regarding the polyfill that present up within the calling code. On this exploration, we’ll restrict these artifacts to the CFInclude tag. That’s, we’ll cover our polyfill implementation in an included template.

And to implement the polyfill, we should perceive the mechanics of a perform declaration. In ColdFusion, simply as in JavaScript, there are two other ways to outline a perform:

  • As a declaration.
  • As an expression.

A declaration is what you employ for many features. It is the stand-alone technique definitions that make up nearly each ColdFusion part member technique. An expression, alternatively, is what you employ while you cross a fat-arrow perform into one other operation (corresponding to arrayMap()).

Each perform declarations and performance expressions end in a Perform object. Nonetheless, these two Perform objects have two totally different life-cycles. With a perform expression, the perform is barely created when the expression is evaluated. With a perform declaration, the perform all the time exists and is mechanically hoisted to the highest of the present web page context.

Perform hoisting (and var hoisting) is a magical function of CFML and JavaScript and plenty of different languages; and vastly improves the ergonomics of the developer expertise. However, it creates a complexity for perform polyfills.

To see what I imply, think about this ColdFusion code:

<cfscript>

	if ( false ) {

		non-public void perform demo() {
			// .... this perform will likely be hoisted ....
		}

	}

	writeDump(
		label = "Demo perform was hoisted!",
		var = demo
	);

</cfscript>

If you happen to have been unfamiliar with the mechanics of hoisting, you would possibly assume that this code throws a null pointer exception since we’re referencing the demo perform that was outlined inside an if-block that by no means executed. Nonetheless, after we run this ColdFusion code, we get the next output:

A CFDump showing the Function object, demo.

As you possibly can see, although our if-block by no means executed, the demo() perform was nonetheless outlined. It is because we outlined it utilizing a perform declaration; and, perform declarations are hoisted to the highest of the web page. As such, it would not matter if our if-block runs, the compilation course of nonetheless exposes the perform.

Within the context of a polyfill, that is problematic as a result of it means than any perform we attempt to declare conditionally will likely be created. As such, we won’t merely examine for the existence of a local perform after which declare it:

<cfscript>

	if ( ! getFunctionList().keyExists( "structValueArray" ) ) {

		non-public array perform structValueArray() {
			// .... this perform will likely be hoisted ....
		}

	}

</cfscript>

This can run tremendous in Adobe ColdFusion, the place there isn’t any conflicting structValueArray() perform. However, if we attempt to run this in Lucee CFML, we get the next error:

Error: The identify structValueArray is already utilized by a in-built Perform; Failed in test3.cfm:5.

The aim of a polyfill is—typically—to run safely in every single place, however solely modify the runtime the place wanted. As such, this method would not make for an excellent polyfill.

To securely create a cross-platform polyfill, we will use a conflict-free perform declaration; after which conditionally create an alias to mentioned perform as wanted. Within the following code, we’ll declare consumer outlined features that finish with Impl (wanting “implementation”) in order to keep away from battle. Then, inside our evaluation if-block, we’ll assign the Impl model to the native identify:

<cfscript>

	// Polyfill the Lucee CFML perform.
	if ( ! getFunctionList().keyExists( "structValueArray" ) ) {

		// The perform definition for "structValueArrayImpl" is hoisted to the highest of
		// the web page context throughout compilation and processing. Then, because the expressions
		// are being evaluated right here, on this if-block, we're assigning the hoisted
		// perform to a context-level variable with a brand new identify (the polyfilled identify).
		structValueArray = structValueArrayImpl;

	}

	// Polyfill the Lucee CFML perform.
	if ( ! getFunctionList().keyExists( "dump" ) ) {

		dump = dumpImpl;

	}

	// Use the 2 polyfilled features in Adobe ColdFusion.
	dump(
		structValueArray([
			key1: "value1",
			key2: "value2",
			key3: "value3"
		])
	);

	// ------------------------------------------------------------------------------- //
	// ------------------------------------------------------------------------------- //

	/**
	* A simplistic polyfill of the structValueArray() technique.
	*/
	non-public array perform structValueArrayImpl( required struct enter ) {

		return structKeyArray( enter )
			.map( ( key ) => enter[ key ] )
		;

	}

	/**
	* A simplistic polyfill of the dump() technique.
	*/
	non-public void perform dumpImpl( required any var ) {

		writeDump( var );

	}

</cfscript>

On the backside of the file, we’re declaring two features that finish with the Impl suffix. Each of those features are hoisted to the highest of the web page context as soon as the template is compiled. However, we’re solely re-assigning to a context-level variable as wanted.

Now, if we run this code, it really works in each Lucee CFML (utilizing the native implementation) and in Adobe ColdFusion (utilizing the polyfilled implementation):

Two CFDump, one in Lucee CFML one in Adobe ColdFusion, both showing the results of a structValueArray() call.

Through the use of perform hoisting and conditional aliasing, we have been in a position to efficiently create a polyfill. However, this can be a lot of rigamarole that we arrived at iteratively. Now that we perceive perform hoisting a bit extra, nevertheless, we will create a less complicated resolution that defers the hoisting mechanics.

Contemplate a CFML template known as polyfill.cfm. Inside this CFML template, we will execute an immediately-invoking perform expression (generally pronounced, “Iffy”). This polyfill file will iterate over an inventory of polyfilled perform names; after which consists of every polyfill perform, as wanted, utilizing CFInclude. On this method, every polyfill lives in its personal file:

<cfscript>
	(() => {

		var nativeFunctions = getFunctionList();
		var polyfillFunctions = [
			"dump",
			"structValueArray"
		];

		for ( var methodName in polyfillFunctions ) {

			if ( ! nativeFunctions.keyExists( methodName ) ) {

				embrace "./#methodName#.cfm";

			}

		}

	})();
</cfscript>

Right here, the “iffy” creates a container for the var variables so as to stop them from leaking into the calling context. However, the iffy would not stop the perform declarations inside the next CFInclude tags from being hoisted to the highest of the web page context. And, on the similar time, it would not execute the included CFML templates until they’re wanted. Which signifies that our polyfill features can now use the native, non-conflicting names.

This is the ./dump.cfm template that declares the dump() polyfill:

<cfscript>

	/**
	* A simplistic polyfill of the dump() technique.
	*/
	non-public void perform dump( required any var ) {

		writeDump( var );

	}

</cfscript>

And here is the ./structValueArray.cfm template that declares the structValueArray() polyfill:

<cfscript>

	/**
	* A simplistic polyfill of the structValueArray() technique.
	*/
	non-public array perform structValueArray( required struct enter ) {

		return structKeyArray( enter )
			.map( ( key ) => enter[ key ] )
		;

	}

</cfscript>

Since these CFML templates are solely included if they do not battle with native strategies, we do not have to fret in regards to the naming conflicts. Nonetheless, since they’re nonetheless declared perform and never perform expressions, as soon as the CFML template is parsed, the features are hoisted up, previous the “iffy” container and uncovered on the web page context. Which signifies that we will use them in whichever template initially included the polyfill.cfm file:

<cfscript>

	embrace "./polyfill.cfm";

	// Use the 2 polyfilled features in Adobe ColdFusion.
	dump(
		structValueArray([
			key3: "value3",
			key4: "value4",
			key5: "value5"
		])
	);

</cfscript>

Perform and variable hoisting mechanics are simply great options of the language, aren’t they?!

Now, to be truthful, I’ve by no means truly used this method to polyfill features in ColdFusion. Principally, I do not write for cross-platform runtimes. So, if there is a runtime that is lacking a perform that I would like, I simply write it. However, I feel the method is mostly sound.

Wish to use code from this publish?
Try the license.


https://bennadel.com/4780

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Most Popular

Recent Comments