使用 Angularjs module.service 的原型继承

Prototypal inheritance using Angularjs module.service

本文关键字:原型 继承 service Angularjs module 使用      更新时间:2023-09-26

我已经使用 Angularjs 几个月了,我想知道如何使用这个框架实现高效的 OOP。

我正在一个项目中工作,我需要实例化一个"终端"类,该类具有构造函数属性和一系列方法(作为通常的类)。在普通的JS中,我会使用伪经典模式(将构造函数属性与原型链中的方法分开)。

根据我对 Angularjs 的了解,model.service() 将是最佳选择,因为我每次调用它时都会创建一个服务的新实例。但是在定义方法时,我通常会看到下一个实现:

myApp.service('helloWorldFromService', function() {
    this.sayHello = function() {
        return "Hello, World!"
    };
});

但是,每次我调用类时,它不会创建函数 sayHello() 吗?我想知道分离功能是否会更好,例如:

myApp.service('helloWorldFromService', function() {
    this.msg= "Hello, World!";
});
helloWorldFromService.prototype.showMessage = function() {
    return this.msg;
};

这样,函数 showMessage() 将只在内存中创建一次,并在创建的所有服务实例之间共享。

此外,如果这是可能的(并且如果它真的使代码更有效率),那么实现它的方法是什么?(上面的代码只是一个疯狂的猜测)

谢谢

编辑以响应评论:看起来如果你只是为你的对象返回构造函数,你可以这样做,然后通过调用服务来访问原型。

快速说明,在示例中,单击"Get Again"将调用原型函数 changeHelloWorldString 并使用控件的名称更新字符串。单击"更改原型"后,该更改HelloWorldString函数将更改为将" [PROTOTYPE CHANGE]"附加到字符串中。单击任一"获取再次"按钮将证明在第二个控制器中所做的原型更改影响了两个控制器中对象的原型链。

请参阅以下示例:

angular.module('myModule2', [])
  .factory('myService', function() {
    function FactoryConstructor(thirdFunction) {
      this.helloWorldFunction = function() {
        return this.helloWorldString;
      }
      this.thirdFunction = thirdFunction;
    }
    FactoryConstructor.prototype.helloWorldString = 'Hello World';
    FactoryConstructor.prototype.changeHelloWorldString = function(newString) {
      this.helloWorldString = newString;
    };
    FactoryConstructor.prototype.changeThirdFunction = function(newFunction) {
      this.thirdFunction = newFunction;
    }
    return FactoryConstructor;
  })
  .controller('ctrl1', function($scope, myService) {
    var factoryResult = new myService(function() {
      this.helloWorldString += ' first';
    });
    $scope.hwString = factoryResult.helloWorldString;
    $scope.hwString2 = factoryResult.helloWorldFunction();
    // console.log(factoryResult instanceof myService) //tested true
    $scope.getAgain = function() {
      factoryResult.changeHelloWorldString('ctrl1 String');
      factoryResult.thirdFunction();
      $scope.hwString = factoryResult.helloWorldString;
      $scope.hwString2 = factoryResult.helloWorldFunction();
    }
  })
  .controller('ctrl2', function($scope, myService) {
    var factoryResult = new myService(function() {
      this.helloWorldString += ' second';
    });
    $scope.hwString = factoryResult.helloWorldString;
    $scope.hwString2 = factoryResult.helloWorldFunction();
    // console.log(factoryResult instanceof myService) //tested true
    $scope.getAgain = function() {
      factoryResult.changeHelloWorldString('ctrl2 String');
      factoryResult.thirdFunction();
      factoryResult.changeThirdFunction(function() {
        this.helloWorldString += ' third';
      });
      $scope.hwString = factoryResult.helloWorldString;
      $scope.hwString2 = factoryResult.helloWorldFunction();
    }
    
    $scope.changePrototype = function() {
      myService.prototype.changeHelloWorldString = function(newString) {
        this.helloWorldString = newString + " [PROTOTYPE CHANGE]";
      }
    }
  });
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app='myModule2'>
  <div ng-controller='ctrl1'>
    <div>{{hwString}}</div>
    <div>{{hwString2}}</div>
    <button ng-click='getAgain()'>Get again</button>
  </div>
  <div ng-controller='ctrl2'>
    <div>{{hwString}}</div>
    <div>{{hwString2}}</div>
    <button ng-click='getAgain()'>Get again</button>
    <button ng-click='changePrototype()'>Change Prototype</button>
  </div>
</div>

在这个答案中找到了另一个很好的解释。这可能会显示一种更好/更简洁的方式来使用提供程序(服务和工厂都派生自哪个,请参阅作者的旁注)来执行此示例显示的内容。

下面原始帖子的其余部分作为背景

Angular 服务是可以注入许多地方的单例。因此,如果您这样做:

angular.module('myModule', [])
.service('myService', function() {
  var myService = this;
  this.helloWorldString = 'Hello World String';
  this.helloWorldFunction = function() {
    return myService.helloWorldString;
  }
})
.controller('main', function($scope, myService) {
  $scope.getAgain = function() {
    $scope.hwString = myService.helloWorldString;
    $scope.hwString2 = myService.helloWorldFunction();
  }
  $scope.getAgain();
})
.controller('notMain', function($scope, myService) {
  myService.helloWorldString = 'edited Hello World String';
  $scope.hwString = myService.helloWorldString;
  $scope.hwString2 = myService.helloWorldFunction();
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app='myModule'>
  <div ng-controller='main'>
    <div>{{hwString}}</div>
    <div>{{hwString2}}</div>
    <button ng-click='getAgain()'>Get again</button>
  </div>
  <div ng-controller='notMain'>
    <div>{{hwString}}</div>
    <div>{{hwString2}}</div>
  </div>
</div>

您会注意到,最初两者是不同的,因为第一对是在更改之前获得的,但是在第二个控制器中进行的更改实际上会影响第一个控制器。只需单击"获取再次"按钮,它将从服务中重新提取信息,并且现在匹配,证明尽管它们被注入到两个不同的控制器中,但它们是同一个对象。

看起来你真正想要的是一个工厂(虽然这主要是语义,你可以在下一个示例中将"factory"换成"服务",它将产生相同的结果。这也可以在 Angular 文档本身中看到。服务的文档实际上从未使用 .service,它始终使用 .factory)。这样,从本质上讲,当您调用"myService(...)"时,您可以构造工厂对象的新实例。使用这些函数参数,您可以自定义返回对象的属性,包括函数,如示例中所示。

angular.module('myModule2', [])
  .factory('myService', function() {
    return function(stringInput, thirdFunction) {
      return {
        helloWorldString: stringInput,
        helloWorldFunction: function() {
          return this.helloWorldString;
        },
        thirdFunction: thirdFunction
      }
    }
  })
  .controller('ctrl1', function($scope, myService) {
    var factoryResult = myService('Hello World String', function () {
      this.helloWorldString += ' first';
    });
    $scope.hwString = factoryResult.helloWorldString;
    $scope.hwString2 = factoryResult.helloWorldFunction();
  
    $scope.getAgain = function() {
      factoryResult.thirdFunction();
      $scope.hwString = factoryResult.helloWorldString;
      $scope.hwString2 = factoryResult.helloWorldFunction();
    }
  })
  .controller('ctrl2', function($scope, myService) {
    var factoryResult = myService('new Hello World String', function () {
      this.helloWorldString += ' second';
    });
    $scope.hwString = factoryResult.helloWorldString;
    $scope.hwString2 = factoryResult.helloWorldFunction();
  
    $scope.getAgain = function() {
      factoryResult.thirdFunction();
      $scope.hwString = factoryResult.helloWorldString;
      $scope.hwString2 = factoryResult.helloWorldFunction();
    }
  });
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app='myModule2'>
  <div ng-controller='ctrl1'>
    <div>{{hwString}}</div>
    <div>{{hwString2}}</div>
    <button ng-click='getAgain()'>Get again</button>
  </div>
  <div ng-controller='ctrl2'>
    <div>{{hwString}}</div>
    <div>{{hwString2}}</div>
    <button ng-click='getAgain()'>Get again</button>
  </div>
</div>

服务函数只会被调用一次,因此你的"类方法"只会用你的两种方法创建一次。但是,由于以下原因,我不会使用您的第一种方法。

  • 不能使用继承
  • 你必须把你的整个班级放在一个大函数中
  • 在构造函数中定义函数之前,您必须注意不要调用该函数。

相反,我会做一些更像你的第二种方法的事情。

myApp.service('helloWorld', HelloWorldService);
function HelloWorldService() {
    this.msg = "Hello, World!";
}
HelloWorldService.prototype.showMessage = function() {
    return this.msg;
};

或者在 ES6 中你可以做

myApp.service('helloWorld', HelloWorldService);
class HelloWorldService {
    constructor() {
        this.msg = "Hello, World!";
    }
    showMessage() {
        return this.msg;
    }
}

编辑

如果您希望每次注入类时都能获得类的新实例,则可以将其包装为工厂。我添加了$log来展示 DI 的工作原理:

myApp.factory('buildHelloWorld', function($log) {
    return function() {
        return new HelloWorld($log);
    }
});
function HelloWorld($log) {
    this.msg = "Hello, World!";
    $log.info(this.msg);
}
HelloWorld.prototype.showMessage = function() {
    return this.msg;
};

然后,您可以将 buildHelloWorld() 函数注入控制器并调用它以获取 HelloWorld 的实例。

//controller
var myCtrl = function(buildHelloWorld) {
    this.helloWorld = buildHelloWorld();
}