AngularJS:实现控制器原型继承的最简洁方法

时间:2015-12-02 15:52:58

标签: javascript angularjs inheritance controller

我浏览过很多博客& stackoverflow线程找到了实现兄弟控制器原型继承的最佳方法,它让我学习了3个解决方案。没有一个是完美的,每个都有它的缺点,所以我想知道它们是否可以改进,或者是否有人有更好的解决方案。

解决方案1:

// helper function to achieve prototypal inheritance
angular.inherit = function (childCtor, parentCtor) {
    childCtor.super = function() {
        var args = Array.prototype.slice.call(arguments);
        parentCtor.apply(args.shift(), args);
    };
    childCtor.prototype = Object.create(parentCtor.prototype, {
        constructor: {
            value: childCtor,
            enumerable: false
        }
    });
    return this;
};


angular.module('test', [])

    // simple service
    .factory('animalService', function() {
        return {
            getScream: function(specy) {
                switch (specy.toLowerCase()) {
                    case 'cat':
                        return 'Meeeeoooow';
                    case 'dog':
                        return 'Waaaaaaarf';
                }
                return '???';
            }
        };
    })

    // AnimalController logic encapsulated in a factory
    .factory('AAnimalController', function ($window, animalService) {

        function AAnimalController($scope, specy) {
            this._name = 'name me!';
            this._specy = specy || 'Animal';

            $scope.$on('cat:mew', react.bind(this));
        }

        function react() {
            if (this.specy === 'Dog') {
                console.log('A cat is around?!? Attack!!!');
            }
        }

        AAnimalController.prototype = {
            introduceItself: function() {
                return 'Hello, my name is "' + this.name + '" and I\'m a ' + this.specy.toLowerCase();
            },

            scream: function() {
                $window.alert(animalService.getScream(this.specy));
            },

            get name() {
                return this._name;
            },
            set name(value) {
                this._name = value;
            },

            get specy() {
                return this._specy;
            }
        };

        return AAnimalController;
    })


    .controller('AnimalController', function ($scope, AAnimalController) {
        return new AAnimalController($scope);
    })


    .controller('DogController', function ($scope, $window, AAnimalController, $controller) {

        function DogController() {
            DogController.super(this, $scope, 'Dog');

            this.fetchStick = function() {
                $window.alert('Here\'s your stick, master!');
            };
        }

        angular.inherit(DogController, AAnimalController);

        return new DogController();
    })


    .controller('CatController', function ($rootScope, $scope, $window, AAnimalController) {

        function CatController() {
            CatController.super(this, $scope, 'Cat');

            this.purr = function() {
                $window.alert('Rrrrrrrr!');
            };

            this.scream = function() {
                AAnimalController.prototype.scream.call(this);
                $rootScope.$broadcast('cat:mew');
            };
        }

        angular.inherit(CatController, AAnimalController);

        return new CatController();
    })
;

缺点:不能直接从角度控制器继承,因此我们需要在工厂中封装基本控制器逻辑,然后通过实例化基本控制器类来创建相应的角度基本控制器。

JSBin:fipujoxube / edit?html,js,console,output

解决方案2(来自http://ng.malsup.com/#!/controller-inheritance-simplified):

// helper function to achieve controller inheritance
var ngModule = angular.module;
angular.module = function() {
    var mod = ngModule.apply(this, arguments);

    mod.inheritController = function(name, inheritFrom, Ctor) {
        this.controller(name, function ($scope, $controller, $injector) {
            var locals = { $scope: $scope, this: this };
            console.log($controller(inheritFrom, locals));
            Ctor.prototype = $controller(inheritFrom, locals);
            locals.superCtrl = Ctor.prototype;
            return $injector.instantiate(Ctor, locals);
        });
        return this;
    };

    return mod;
};


angular.module('test', [])

    // service example
    .factory('animalService', function() {
        return {
            getScream: function(specy) {
                switch (specy.toLowerCase()) {
                    case 'cat':
                        return 'Meeeeoooow';
                    case 'dog':
                        return 'Waaaaaaarf';
                }
                return '???';
            }
        };
    })

    .controller('AnimalController', function ($scope, $window, animalService) {

        // 'PRIVATE' PROPERTIES
        this._name = 'name me!';
        this._specy = 'Animal';

        // PUBLIC METHODS
        /*this.listenForCat = function() {
            $scope.$on('cat:mew', react.bind(this));
        };*/

        this.introduceItself = function() {
            return 'Hello, my name is "' + this._name + '" and I\'m a ' + this._specy.toLowerCase();
        };

        this.scream = function() {
            $window.alert(animalService.getScream(this._specy));
        };

        Object.defineProperties(this, {
            name: {
                get: function() {
                    return this._name;
                },
                set: function(value) {
                    this._name = value;
                }
            },

            specy: {
                get: function() {
                    return this._specy;
                }
            }
        });

        // PRIVATE METHODS
        function react() {
            if (this.specy === 'Dog') {
                console.log('A cat is around? Attack!!');
            }
        }

        $scope.$on('cat:mew', react.bind(this));
    })

    .inheritController('DogController', 'AnimalController', function ($window, superCtrl) {
        this._specy = 'Dog';
        this.fetchStick = function() {
            $window.alert('Here\'s your stick, master!');
        };

        this.listenForCat();
    })

    .inheritController('CatController', 'AnimalController', function ($rootScope, $window, superCtrl) {
        this._specy = 'Cat';

        this.purr = function() {
            $window.alert('Rrrrrrrr!');
        };

        this.scream = function() {
            superCtrl.scream.call(this);
            $rootScope.$broadcast('cat:mew');
        };
    })
;

缺点:无法将方法附加到原型,因此基本控制器'这个'始终指的是基本控制器上下文,而不是继承的控制器。

JSBin:hvagonenowe / edit?html,js,console,output

解决方案3(使用RequireJS或AlmondJS):

define('inherit', function() {
    function inherit(childCtor, parentCtor) {
        childCtor.super = function() {
            var args = Array.prototype.slice.call(arguments);
            parentCtor.apply(args.shift(), args);
        };
        childCtor.prototype = Object.create(parentCtor.prototype, {
            constructor: {
                value: childCtor,
                enumerable: false
            }
        });
        return this;
    }

    return inherit;
});

define('animalService', function() {

    function animalService() {

        return {
            getScream: function(specy) {
                switch (specy.toLowerCase()) {
                    case 'cat':
                        return 'Meeeeoooow';
                    case 'dog':
                        return 'Waaaaaaarf';
                }
                return '???';
            }
        };
    }

    return animalService;
});


define('AnimalController', ['animalService'], function (animalService) {

    animalService = animalService();

    function AnimalController($scope) {
        this._name = 'name me!';
        this._specy = 'Animal';

        $scope.$on('cat:mew', react.bind(this));
    }

    AnimalController.prototype = {
        introduceItself: function() {
            return 'Hello, my name is "' + this.name + '" and I\'m a ' + this.specy.toLowerCase();
        },

        scream: function() {
            window.alert(animalService.getScream(this.specy));
        },

        get name() {
            return this._name;
        },
        set name(value) {
            this._name = value;
        },

        get specy() {
            return this._specy;
        }
    };

    function react() {
        if (this.specy === 'Dog') {
            console.log('A cat is around?!? Attack!!!');
        }
    }

    return AnimalController;
});

define('DogController', ['AnimalController', 'inherit'], function (AnimalController, inherit) {

    function DogController($scope, $window) {
        DogController.super(this, $scope);
        this._specy = 'Dog';

        this.fetchStick = function() {
            $window.alert('Here\'s your stick, master!');
        };
    }

    inherit(DogController, AnimalController);

    return DogController;
});

define('CatController', ['AnimalController', 'inherit'], function (AnimalController, inherit) {

    function CatController($rootScope, $scope, $window, animalService) {
        CatController.super(this, $scope);
        this._specy = 'Cat';

        this.purr = function() {
            $window.alert('Rrrrrrrr!');
        };

        this.scream = function() {
            AnimalController.prototype.scream.call(this);
            $rootScope.$broadcast('cat:mew');
        };
    }

    inherit(CatController, AnimalController);

    return CatController;
});

require([
    'animalService',
    'AnimalController',
    'DogController',
    'CatController'
], function (animalService, AnimalController, DogController, CatController) {

    angular.module('test', [])
        .factory('animalService', animalService)
        .controller('AnimalController', AnimalController)
        .controller('DogController', DogController)
        .controller('CatController', CatController)
    ;

    angular.bootstrap(document, ['test']);
});

缺点:

  • 详细
  • 额外的第三方图书馆
  • 在棱角分明的世界之外,我们 需要调用工厂模块才能访问其公共方法

JSBin:yagahofefo / edit?html,js,console,output

0 个答案:

没有答案