XIANFESchool / FE-problem-collection

前端问题收集和知识经验总结
https://github.com/ShuyunXIANFESchool/FE-problem-collection/issues
63 stars 22 forks source link

modules and dependency injection #34

Open fnjoe opened 8 years ago

fnjoe commented 8 years ago

modules

module构成了我们的web应用。

从面向对象的角度来看,我们期望module拥有:

// 实现module类
function Module(name,requires) {
    this.name = name;
    this.requires = requires;
}
Module.prototype.constant = function (value) {
    // 使该module拥有该value;
};
Module.prototype.service = function () {
    // 使该module拥有该service
};

// 创建一个user module;
var userModule = new Module('user', null);

2. module对象的管理

为了方便对模块的管理,我们还需要一个变量来存储模块,一个简便的方法来新建或获取模块如:

var myAngular = {};
var modules = {};
// 新建或者获取模块,取决于是否传入requires参数
myAngular.module = function (name, requires) {
    if (requires) {
        return modules[name] = new Module(name, requires);
    } else {
        return modules[name];
    }
};

angular内部对模块机制的实现大体是这个思路,只不过没有采用构造函数与原型的写法来新建模块,直接使用了对象的字面量写法。

var moduleInstance = {
            name: name,// 模块名称
            requires: requires,//模块依赖
            constant: invokeLater('constant', 'unshift'),
            provider: invokeLater('provider'),
            _invokeQueue: invokeQueue
        };
modules[name] = moduleInstance;

dependency injection

当使用依赖注入机制时,我们的期望是:当我们在特定的上下文中,需要使用别的值或者对象,甚至是类时,我们只需给出它们名称,然后就可以在接下来的上下文中直接使用它们。

1. 我们的期望

在angular中,我们很熟悉这样的代码:

function myController($scope, $window, myService) {
    // 直接使用$scope,$window, myService。
}

我们希望的是直接在该函数内部使用$scope之类的对象。

2. 如何实现

在如何实现我们的期望之前,我们首先来看看我们最熟悉的函数 function。 在JavaScript中,function拥有作用域,从function的使用中我们就可以理解依赖注入的概念。

var a = 1;
var b = [1,2,3];
var c = {x: 1, y: 2};

function useThem(a, b, c) {
    c.z = b.push(a);
}
useThem(a, b, c);

上面本身就是JavaScript中function的写法,使用参数可以直接从外部作用域去获取需要的值。

如我们之前提到的,函数的参数传递本身就符合我们的希望——使用名称,也可以说是标志来获取对应的值。

唯一不同的是,函数的参数代表什么是由函数调用时我们向它内部传递什么决定的,而我们在controller中的期望是当我声明好对应的参数时,这个参数所代表的值已经确定了,也就是函数声明时的参数决定了它的调用中传入什么参数。

要实现我们的期望,我们只需要增加一层逻辑,具体来说,也就是说我们需要统一我们函数的调用入口;

// 我们的函数
function myController($scope, $window, myService) {
    // 直接使用$scope,$window, myService。
}

// 函数的统一调用入口
function invoke(fn, self) {
    var argues = getArgs(fn);
    fn.apply(self,argues);
}

// 获取函数依赖的方法。
// 1. 从哪里去获取依赖的标志, 从数组式的写法,还是$inject的写法,还是使用正则匹配来进行字符串匹配
// 2. 获取到依赖的标志后,按照该标志从哪里去取对应的值。
function getArgs(fn) {
    // return what you want
}
也许,你已经想到需要一个cache变量来存储你所声明的可以被依赖注入的值了。

接下来,只要我们在调用我们的函数时使用统一的调用入口 invoke 方法时,便可以在函数内部自动根据参数的名称或者其他声明(数组式的依赖以及$inject式的声明依赖)来自动注入我们需要的值了。

由于我们在angular的框架中编码时,其实都是进行module内容的配置,真正帮我们去调用这些内容的是angular,它在内部实现了如上所述的invoke,以及getArgs逻辑。

当然,angular的依赖注入更为全面和复杂,不过,一切都是从这简单的逻辑开始的。

silence717 commented 8 years ago

看到这个突然想起来一个问题: module.exports与exports有什么区别?

hjzheng commented 8 years ago

@silence717 module.exports 这个是CommonJS的规范 export 是 ES6 的规范吧!exports 不清楚

fnjoe commented 8 years ago

@silence717 nodejs中,exports实际上是module.exports的一个引用,添加属性可以,不过直接赋值会切断与module.exports的联系。 参考 understanding-module-exports-exports-node-js