A common and entirely sensible programming practice is to put commonly used utility functions in one file, and then somehow link that file in to many different programs. In traditional compiled languages you can compile a bunch of utilities into a statically linked .LIB file or a dynamically linked .DLL file. But what do you do in script, where a program is just text?
People often ask me why there is no #include statement in VBScript or JScript. Wouldn’t it be nice to be able to write up a page full of helpful utility functions and then in half a dozen different scripts say
#include myUtilityFunctions.vbs ?
If you think for a bit about how such a statement would be implemented, you see why we didn’t do it. The reason is pretty simple: the script engine does not know where the current file came from, so it has no way of finding the referenced file. Consider IE, for example:
<script language="vbscript"> #include myUtilityFunctions.vbs ' ...
How does the script engine know that this page was downloaded from any particular site? The script engine knows nothing about where the script came from — IE just hands the text to the engine. Even assuming that it could figure it out, the script engine would then have to call IE’s code to download files off the internet. Worse, what if the path was fully qualified to a path in a different domain? Now the script engine needs to implement IE’s security rules.
Now consider: do any of those factors apply when running in ASP? No! ASP would have to have a completely different include mechanism where the relative path was based on facts about the current virtual root and the security semantics checked for cross-root violations.
What about WSH? There we’re worried about the current directory but have no security considerations.
What about third party hosts? Every host could have a different rule for where the script comes from and what is a legal path. We cannot possibly put all those semantics into the script engine. To make an “include” feature work, we’d have to define a callback function into the host so that the host could fetch the included script for us.
It’s just too much work for too little gain, so we simply declared a rule: the host is responsible for implementing any mechanism whereby script files can be included into the script namespace. This is why IE and WSH have the
script src attribute.
Commentary from 2019:
Following on the heels of “Why can’t I create the WScript object?” we have another example of younger me focusing on the restrictions imposed by the implementation architecture choices rather than focusing on the user need that drives the question.
As I noted, we could have prioritized this scenario and implemented that callback, but we had many higher priorities, including improvements to performance and robustness that benefit everyone.
Commenters were quick to point out these germane critiques; my rather weak response was that the script engines tried to not be in the business of placing requirements upon the host.
That’s weak because the hosts had to solve the problem of how to find related scripts anyways, and they also had to come up with their own mechanisms for users to specify locations. Moreover, we could have made the “find a file to include” feature optional, thereby reducing the burden on all hosts.
The net result is that every script host has its own unique way of specifying “include this file”, and that created its own set of user confusions.
A stronger response is that the seemingly simple feature comes with a large set of implementation difficulties. For example, how do we detect and prevent hostile or accidental circular includes? How does the feature interact with the debugger? Are includes processed at parse time or run time? And many more; it’s not in any way a trivial feature.
From our perspective today, the whole question seems quaint. Web sites routinely download a whackload of JS modules, and there are whole framework subsystems devoted to figuring out which modules are needed, and so on.
I am able to do basically the same thing by using a .wsf file. I guess it doesn’t work in all contexts, though, but it does work for login scripts.
Pingback: Porting old posts, part 4 | Fabulous adventures in coding