0

Consider the following code in the official angular repository

function createInjector(modulesToLoad, strictDi) {
  strictDi = (strictDi === true);
  var testingScope = 'this is a test';
  var INSTANTIATING = {},
      providerSuffix = 'Provider',
      path = [],
      loadedModules = new HashMap([], true),
      providerCache = {
        $provide: {
            provider: supportObject(provider),
            factory: supportObject(factory),
            service: supportObject(service),
            value: supportObject(value),
            constant: supportObject(constant),
            decorator: decorator
          }
      },
      providerInjector = (providerCache.$injector =
          createInternalInjector(providerCache, function(serviceName, caller) {
            if (angular.isString(caller)) {
              path.push(caller);
            }
            throw $injectorMinErr('unpr', "Unknown provider: {0}", path.join(' <- '));
          })),
      instanceCache = {},
      protoInstanceInjector =
          createInternalInjector(instanceCache, function(serviceName, caller) {
            var provider = providerInjector.get(serviceName + providerSuffix, caller);
            return instanceInjector.invoke(
                provider.$get, provider, undefined, serviceName);
          }),
      instanceInjector = protoInstanceInjector;

  providerCache['$injector' + providerSuffix] = { $get: valueFn(protoInstanceInjector) };
  var runBlocks = loadModules(modulesToLoad);
  instanceInjector = protoInstanceInjector.get('$injector');
  instanceInjector.strictDi = strictDi;
  forEach(runBlocks, function(fn) { if (fn) instanceInjector.invoke(fn); });

  return instanceInjector;

And

  function createInternalInjector(cache, factory) {

    function getService(serviceName, caller) {
      console.log(testingScope);
      if (cache.hasOwnProperty(serviceName)) {
        if (cache[serviceName] === INSTANTIATING) {
          throw $injectorMinErr('cdep', 'Circular dependency found: {0}',
                    serviceName + ' <- ' + path.join(' <- '));
        }
        return cache[serviceName];
      } else {
        try {
          path.unshift(serviceName);
          cache[serviceName] = INSTANTIATING;
          return cache[serviceName] = factory(serviceName, caller);
        } catch (err) {
          if (cache[serviceName] === INSTANTIATING) {
            delete cache[serviceName];
          }
          throw err;
        } finally {
          path.shift();
        }
      }
    }
    return {
      get: getService,
    };
  }

We see that createInternalInjector is able to access local variables (like path) in its caller's scope createInjector without passing in to the parameter.

Indeed if I add testingScope to createInjector and try to access in createInternalInjector, I was able to do it.

This is weird because I try to replicate this behavior like the following.

testOuter();

function testOuter() {
  var outer = 'outer'

  testInner().test();

}

function testInner() {
  function testing() {
    console.log(outer);
  }
  return {
      test: testing
  }
}

But got an error instead.

ReferenceError: outer is not defined

Can someone give me some pointers on why this is happening?

user3591466
  • 797
  • 2
  • 8
  • 18

1 Answers1

1

The issue here is scope chaining. createInternalInjector function is called within createInjector function, therefore it can access its parent properties. In your case you have sibling functions that are unaware to each other variables.

More on scope chains and closures: https://stackoverflow.com/a/1484230/5954939

Community
  • 1
  • 1
AranS
  • 1,871
  • 10
  • 22