资讯专栏INFORMATION COLUMN

[ AngularJS ] 自己实现一个简单的依赖注入

seasonley / 1819人阅读

摘要:我们开始使用的时候,它的双向数据绑定是最让我们印象深刻的,那第二个就应该算是它的那神奇的依赖注入的功能了。来实现依赖注入现在大部分的框架都提供依赖注入机制的模块,可能会叫做。

  

我们开始使用AngularJS的时候,它的双向数据绑定是最让我们印象深刻的,那第二个就应该算是它的那神奇的依赖注入的功能了。

举个栗子
function myController = ($scope, $http){
    $http.get("users/users.json").success(function(data){
        $scope.users = data;
    })
}

这是一个典型的angularjs的控制器,他发送了一个http请求,从后台获取json数据,然后把他传递给当前作用域。你会发现,我们并没有执行这个myController函数(我们没有机会给它传递参数),其实,是angular这个框架帮我们做了;那么,$scope, $http这些变量从哪里来的呢? 这是angular的一个非常酷的特性,我们一步步来实现一个简单的注入器,从而知道它是怎么实现的。

传统的方式

现在,我们有一个js函数去把用户列表展示在网页上,那么这个函数就需要从ajax获取过来的数据和用于展示数据的DOM元素。为了简单点,我们直接用静态数据了来代替ajax的http请求:

var data = ["John", "Steve", "David"];
var body = document.querySelector("body");
var ajaxFn = {
  get: function(path, cb) {
    console.log(path + " requested");
    cb(data);
  }
}

我们把body标签作为列表的容器,ajaxFn是模拟ajax请求的对象,data是一个包含所有用户的数组。下面来使用:

var displayUsers = function(domEl, ajax) {
  ajax.get("/api/users", function(users) {
    var html = "";
    for(var i=0; i < users.length; i++) {
      html += "

" + users[i] + "

"; } domEl.innerHTML = html; }); }

很明显,我们运行displayUsers(body, ajaxFn),我们将会看到三个用户名显示在网页上,同时控制台输出了/api/users requested。那这样的话,我们可以认为我们的displayUsers函数有两个依赖,bodyajaxFn.

那么现在,我们的目的是使displayUsers这个函数在不传参数的情况下照样工作,即,运行displayUsers()要得到和上面一样的结果,如果我们直接运行,你会发现报错了:

Uncaught TypeError: Cannot read property "get" of undefined

很明显是因为ajax这个参数没有定义。

来实现依赖注入

现在大部分的框架都提供依赖注入机制的模块,可能会叫做:injector。为了在某个地方使用一个依赖,我们需要在那个地方注册那个依赖:

来创建我们的injector

var injector = {
  storage: {},
  register: function(name, resource) {
    this.storage[name] = resource;
  },
  resolve: function(target) {

  }
};

我们只需要两个方法,第一个是register, 接收我们的依赖并将它存储起来。第二个方法是resolve,这个方法接收的参数是我们我们要注入依赖的对象,这里的关键的点就是这个注入器不应该调用我们的函数,所以我们在resolve方法中返回一个闭包来包裹我们的target,然后再调用它:

resolve: function(target) {
  return function() {
    target();
  };
}

那么到现在呢,我们的注入器依然是不可用的:

displayUsers = injector.resolve(displayUsers);
displayUsers();

我们依然得到同样的错误,你觉得少了什么呢?绝壁就是依赖项了,所以我们下一步就是要找出这个函数的依赖项,如何找? 这是比较棘手的问题,不过我们可以参考angularjs的方式:

var FN_ARGS = /^functions*[^(]*(s*([^)]*))/m;
var STRIP_COMMENTS = /((//.*$)|(/*[sS]*?*/))/mg;
...
function annotate(fn) {
  ...
  fnText = fn.toString().replace(STRIP_COMMENTS, "");
  argDecl = fnText.match(FN_ARGS);
  ...
}
We purpo

这里省略了一些细节部分,我们需要注意的就是annotate这个函数,它负责将目标函数转换为一个字符串,删除它的注释(如果有),然后提取他的参数(依赖项):

resolve: function(target) {
  var FN_ARGS = /^functions*[^(]*(s*([^)]*))/m;
  var STRIP_COMMENTS = /((//.*$)|(/*[sS]*?*/))/mg;
  fnText = target.toString().replace(STRIP_COMMENTS, "");
  argDecl = fnText.match(FN_ARGS);
  console.log(argDecl);
  return function() {
    target();
  }
}

上面主要是通过正则表达式来找到依赖项,来看看结果:

你会看到返回的数组中包含了我们需要的依赖项,我们找到它们并用注入器注册和存储:

resolve: function(target) {
  var FN_ARGS = /^functions*[^(]*(s*([^)]*))/m;
  var STRIP_COMMENTS = /((//.*$)|(/*[sS]*?*/))/mg;
  fnText = target.toString().replace(STRIP_COMMENTS, "");
  argDecl = fnText.match(FN_ARGS)[1].split(/, ?/g);
  var args = [];
  for(var i=0; i

现在,我们可以使用我们的注入器了:

injector.register("domEl", body);
injector.register("ajax", ajaxFn);

displayUsers = injector.resolve(displayUsers);
displayUsers();

你会发现得到了和刚开始一样的结果。

这样做有什么优点呢?有点就是我们可以把DOM元素和ajaxFn注入到任何一个函数中,我们甚至可以将我们的应用程序配置成这样的方式,那我们就不用通过继承来传递这些对象了。这仅仅是injector的register和resolve方法而已,我们的注入器还不够完美,还有扩展空间,比如定义作用域scope.

而angularjs中的依赖注入更加强大:

displayUsers = injector.resolve(["domEl", "ajax", displayUsers]);

和displayUsers 不同,它传递的是真实的依赖名称。

译自:http://www.sitepoint.com/revealing-magic-javascript/

有错误还请指正!

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/85840.html

相关文章

  • AngularJs 入门(一)--前言

    摘要:入门一前言目前来说相对于现在流行的高版本以及来说实属是老套的前端框架了,当然这都不重要,没有完美的框架,只有不断优化的代码。通过使用我们称为指令的结构,让浏览器能够识别新的语法。使用作为输入,而不是字符串,是区别于其它的框架的最大原因。 AngularJs 入门(一) 前言 AngularJs目前来说相对于现在流行的高版本ng2、ng4,以及Vue2.0、React来说实属是老套的前...

    wenyiweb 评论0 收藏0
  • 【译】《精通使用AngularJS开发Web App》(五)

    摘要:上一篇译精通使用开发四下一篇译精通使用开发六书名合作对象正如所见,提供了一种将对象组织为模块的方式。模块不仅可以注册可以直接被框架所调用的对象控制器,过滤器等,还可以使用任何应用开发者所定义的对象。 上一篇:【译】《精通使用AngularJS开发Web App》(四) 下一篇:【译】《精通使用AngularJS开发Web App》(六) 书名:Mastering Web Applic...

    PiscesYE 评论0 收藏0
  • AngularJS 依赖注入

    摘要:代码代码需要注意的地方这个方法只适合未经过压缩和混淆的代码因为需要原始未经压缩的参数列表来进行解析。代码代码注意的地方行内声明的方式允许我们直接传入一个参数数组而不是一个函数。 AngularJS 依赖注入的方法 1. 通过函数的参数进行推断式注入声明 如果没有明确的什么, AngularJS 会假定参数名称就是依赖的名称。因此, 它会在内部调用函数对象的 toString() 方法...

    mindwind 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<