变量绑定/第一个 class 函数是否优于私有方法命名?提升如何受到影响?

Are variable bound / 1st class functions preferable over private method naming? How is hoisting affected?

关于构建 Angular 代码和 JavaScript 使用变量绑定与私有方法命名函数约定时的行为的几个问题。在 AngularJS 中通过私有方法命名使用变量绑定函数/第一个 class 函数是否有性能或风格原因?提升在每种方法中是如何受到影响的?下面的第二种方法会减少执行的提升量吗?这会对应用程序性能产生显着影响吗?

一个私有方法命名的例子。这是构造 Angular 代码的推荐方法吗?

    (function () {
       'use strict'
        function modalController(dataItemsService, $scope) {
            var vm = this;

            function _getSelectedItems() {
               return dataItemsService.SelectedItems();
            }

            function _deleteSelectedItems() {
               dataItemService.DeleteItem();
               $("#existConfirmDialog").modal('hide');
            }

            vm.SelectedItems = _getSelectedItems;
            vm.deleteItemRecord = _deleteItemRecord;
        }

        angular.module('app').controller('modalController', ['dataItemService', '$scope', modalController]
})();

变量绑定函数的一个例子。这种在控制器中构建 angular 代码的方法怎么样 - 在 performance/style 方面有什么缺点或优点吗?

angular.module('appname').controller("NameCtrl", ["$scope", "$log", "$window", "$http", "$timeout", "SomeService",
    function ($scope, $log, $window, $http, $timeout, TabService) {

    //your controller code
       $scope.tab = 0;    
       $scope.changeTab = function(newTab){
          $scope.tab = newTab;
       };    
       $scope.isActiveTab = function(tab){
          return $scope.tab === tab;
       }; 
    }
]);

第一种方法使用“私有”方法并通过 public 别名公开它们,称为 Revealing Module Pattern,尽管在示例中这些方法实际上并不是私有的。

后者是一个相当标准的 Constructor Pattern,使用 $scope 作为上下文。

Is there a performance or stylistic reason for using variable bound functions / first class functions in AngularJS over private method naming?

Is [there] a recommended way to structure Angular code?

TL;DR

  • 从根本上来说,这两种风格并没有太大区别 多于。一个使用 $scope,另一个使用 this。一个构造函数定义在闭包中,一个定义在行内。

  • 有些情况下您可能需要私有方法或值。 还有风格和(可能微不足道的)表现 在 $scope 上使用变量 this/vm 的原因。这些并不相互排斥。

  • 您可能想要使用 基本的,基本的,老派的构造函数模式,以及很多 人们通过 this 而不是 $scope.

    公开状态和行为
  • 您可以在 Controller 中允许自己的数据隐私,但大多数 Service/Factory 应该利用这个时间。主要的例外是代表视图状态的数据。

  • 请不要在您的控制器中使用 jQuery。

参考文献:

AngularJS Style Guide 托德座右铭。

AngularJS Up & Running


要彻底回答您的问题,我认为了解 Controller 的职责很重要。每个控制器的工作是向视图公开一组严格的状态和行为。简而言之,只分配给 this$scope 您不介意用户在您的视图中看到或玩的东西。

问题中的 variable (vm, $scope) 是由 Controller 函数创建的实例的上下文 (this)。

$scope是Angular的“特殊”语境;它已经定义了一些行为供您使用(例如 $scope.$watch)。 $scopes 也遵循继承链,即 $scope 继承分配给其父 $scope 的状态和行为。

拿这两个控制器:

angular.module("Module")
    .controller("Controller", ["$scope", function($scope) {
        $scope.tab = 0;
        $scope.incrementTab = function() {
            $scope.tab++;
        };
    }])
    .controller("OtherController", ["$scope", function($scope) {
        // nothing
    }]);

还有风景

<div ng-controller="Controller">
    <p>{{ tab }}</p>
    <button ng-click="incrementTab();">Increment</button>
    <div ng-controller="OtherController">
        <p>{{ tab }}</p>
        <button ng-click="incrementTab();">Increment</button>
    </div>
</div>

示例here

你会注意到,即使我们没有在 OtherController 中定义 $scope.tab,它也从 Controller 继承了它,因为 Controller 是它在 DOM 中的父级。在显示选项卡的两个地方,您应该看到“0”。这可能是您所指的 "hoisting",尽管那是一个完全不同的概念。

单击第一个按钮时会发生什么?在我们暴露“制表符”的两个地方,它们现在将显示“1”。当您按下第二个按钮时,两者也会更新和递增。

当然,我很可能不希望我的子选项卡与父选项卡具有相同的选项卡值。如果将 OtherController 更改为:

.controller("OtherController", ["$scope", function($scope) {
    $scope.tab = 42;
}]);

您会注意到此行为已发生变化 - 选项卡的值不再同步。

但现在很混乱:我有两个叫做“制表符”的东西是不一样的。其他人可能稍后使用“tab”写了一些代码并无意中破坏了我的代码。

我们过去常常通过在 $scope 上使用命名空间来解决这个问题,例如$scope.vm 并将所有内容分配给命名空间:$scope.vm.tab = 0;

<div ng-controller="OtherController">
    <p>{{ vm.tab }}</p>
    <button ng-click="vm.incrementTab();">Increment</button>
</div>

另一种方法是使用 this 的简单性和简洁性,并利用 controllerAs 语法。

.controller("OtherController", function() {
    this.tab = 0;
});

<div ng-controller="OtherController as oc">
    <p>{{ oc.tab }}</p>
</div>

对于习惯使用普通 JS 的人来说,这可能会更舒服,而且这样也更容易避免与其他 Angular 来源发生冲突。您可以随时更改命名空间。由于您没有创建新的 $scope 实例,因此它在性能上也有点“轻”,但我不确定是否有很多收获。


为了实现隐私,我建议将您的数据封装在服务或工厂中。请记住,控制器并不总是单例;视图和控制器之间存在 1:1 关系,您可以多次实例化同一个控制器!然而,工厂和服务对象是单例。他们非常擅长存储共享数据。

让所有控制器从单例中获取状态的副本,并确保所有控制器都使用 Service/Factory 上定义的行为修改单例状态。

function modalController(dataItemsService) {
    var vm = this;
    vm.selectedItems = dataItemsService.SelectedItems(); // get a copy of my data
    vm.updateItem = dataItemService.UpdateItem; // update the source
}

等等,我怎么知道我的应用程序的另一部分何时更改了我的私人数据?我如何知道何时获取 SelectedItems 的新副本?这就是 $scope.$watch 发挥作用的地方:

function modalController(dataItemsService, $scope) {
    var vm = this;

    vm.updateItem = dataItemService.UpdateItem; // update the source

    // periodically check the selectedItems and get a fresh copy.
    $scope.$watch(dataItemsService.SelectedItems, function(items) {
        vm.items = items;
    });

    // thanks $scope!
}

如果您的数据不共享,或者如果您的私有数据代表视图层而不是模型层,那么将其保留在控制器中是完全可以的。

function Controller() {
    var buttonClicked = false;

    this.click = function() {
        buttonClicked = true; // User can not lie and say they didn't.
    };
}

最后,不要像您的参考那样在您的控制器中使用 JQUERY!

$("#existConfirmDialog").modal('hide'); 

这个例子可能不是纯粹的邪恶,但要避免在指令之外访问和修改 DOM,你不想通过修改它下面的 DOM 来破坏应用程序的其他部分.