这是我之前给出的一个很长的答案。它非常详细地解释了使用“新”关键字与不使用它的原因。
TL;DR
1) 当您使用 Factory 时,您会创建一个对象,将属性添加到它,然后返回相同的对象。当您将此工厂传递给您的控制器时,对象上的这些属性现在将通过您的工厂在该控制器中可用。
app.controller(‘myFactoryCtrl’, function($scope, myFactory){
$scope.artist = myFactory.getArtist();
});
app.factory(‘myFactory’, function(){
var _artist = ‘Shakira’;
var service = {};
service.getArtist = function(){
return _artist;
}
return service;
});
2) 当您使用 Service 时,Angular 会在后台使用“new”关键字将其实例化。因此,您将向“this”添加属性,服务将返回“this”。当您将服务传递到您的控制器时,“this”上的这些属性现在将通过您的服务在该控制器上可用。
app.controller(‘myServiceCtrl’, function($scope, myService){
$scope.artist = myService.getArtist();
});
app.service(‘myService’, function(){
var _artist = ‘Nelly’;
this.getArtist = function(){
return _artist;
}
});
非 TL/DR
1) 工厂
工厂是最流行的创建和配置服务的方式。真的没有什么比 TL;DR 说的更多了。您只需创建一个对象,为其添加属性,然后返回相同的对象。然后,当您将工厂传递给您的控制器时,对象上的这些属性现在将通过您的工厂在该控制器中可用。下面是一个更广泛的示例。
app.factory(‘myFactory’, function(){
var service = {};
return service;
});
现在,当我们将“myFactory”传递给我们的控制器时,我们可以使用附加到“服务”的任何属性。
现在让我们在回调函数中添加一些“私有”变量。这些将无法从控制器直接访问,但我们最终会在“服务”上设置一些 getter/setter 方法,以便能够在需要时更改这些“私有”变量。
app.factory(‘myFactory’, function($http, $q){
var service = {};
var baseUrl = ‘https://itunes.apple.com/search?term=’;
var _artist = ‘’;
var _finalUrl = ‘’;
var makeUrl = function(){
_artist = _artist.split(‘ ‘).join(‘+’);
_finalUrl = baseUrl + _artist + ‘&callback=JSON_CALLBACK’;
return _finalUrl
}
return service;
});
您会注意到我们没有将这些变量/函数附加到“服务”。我们只是为了以后使用或修改它们而创建它们。
- baseUrl 是 iTunes API 所需的基本 URL
- _artist 是我们要查找的艺术家
- _finalUrl 是我们将调用 iTunes 的最终且完全构建的 URL makeUrl 是一个函数,它将创建并返回我们的
iTunes 友好的 URL。
现在我们的辅助/私有变量和函数已经到位,让我们为“服务”对象添加一些属性。无论我们在“服务”上放置什么,我们都可以直接在我们将“myFactory”传入的任何控制器中使用。
我们将创建简单地返回或设置艺术家的 setArtist 和 getArtist 方法。我们还将创建一个方法,该方法将使用我们创建的 URL 调用 iTunes API。此方法将返回一个承诺,一旦数据从 iTunes API 返回,该承诺将履行。如果你没有太多在 Angular 中使用 Promise 的经验,我强烈建议你深入研究它们。
setArtist 下方接受艺术家并允许您设置艺术家。 getArtist 返回艺术家 callItunes 首先调用 makeUrl() 以构建我们将在 $http 请求中使用的 URL。然后它设置一个promise对象,用我们的最终url发出一个$http请求,然后因为$http返回一个promise,我们可以在请求之后调用.success或.error。然后我们用 iTunes 数据解决我们的承诺,或者我们拒绝它并显示一条消息说“有一个错误”。
app.factory('myFactory', function($http, $q){
var service = {};
var baseUrl = 'https://itunes.apple.com/search?term=';
var _artist = '';
var _finalUrl = '';
var makeUrl = function(){
_artist = _artist.split(' ').join('+');
_finalUrl = baseUrl + _artist + '&callback=JSON_CALLBACK'
return _finalUrl;
}
service.setArtist = function(artist){
_artist = artist;
}
service.getArtist = function(){
return _artist;
}
service.callItunes = function(){
makeUrl();
var deferred = $q.defer();
$http({
method: 'JSONP',
url: _finalUrl
}).success(function(data){
deferred.resolve(data);
}).error(function(){
deferred.reject('There was an error')
})
return deferred.promise;
}
return service;
});
现在我们的工厂已经完工。我们现在可以将“myFactory”注入任何控制器,然后我们就可以调用我们附加到服务对象的方法(setArtist、getArtist 和 callItunes)。
app.controller('myFactoryCtrl', function($scope, myFactory){
$scope.data = {};
$scope.updateArtist = function(){
myFactory.setArtist($scope.data.artist);
};
$scope.submitArtist = function(){
myFactory.callItunes()
.then(function(data){
$scope.data.artistData = data;
}, function(data){
alert(data);
})
}
});
在上面的控制器中,我们注入了“myFactory”服务。然后,我们在 $scope 对象上设置来自“myFactory”数据的属性。上面唯一棘手的代码是如果你以前从未处理过承诺。因为 callItunes 返回了一个 Promise,所以我们可以使用 .then() 方法,并且只有在我们的 Promise 与 iTunes 数据一起实现时才设置 $scope.data.artistData。您会注意到我们的控制器非常“薄”。我们所有的逻辑和持久数据都位于我们的服务中,而不是我们的控制器中。
2) 服务
在处理创建服务时,最重要的事情可能是它是用“new”关键字实例化的。对于您的 JavaScript 专家,这应该会给您一个关于代码性质的重要提示。对于那些 JavaScript 背景有限或不太熟悉“new”关键字实际作用的人,让我们回顾一些 JavaScript 基础知识,这些基础知识最终将帮助我们理解服务的本质。
要真正看到使用“new”关键字调用函数时发生的变化,让我们创建一个函数并使用“new”关键字调用它,然后让我们展示解释器在看到“new”时会做什么关键词。最终结果都是一样的。
首先让我们创建我们的构造函数。
var Person = function(name, age){
this.name = name;
this.age = age;
}
这是一个典型的 JavaScript 构造函数。现在,每当我们使用“new”关键字调用 Person 函数时,“this”都会绑定到新创建的对象。
现在让我们在 Person 的原型上添加一个方法,这样它就可以在我们的 Person ‘类’的每个实例上使用。
Person.prototype.sayName = function(){
alert(‘My name is ‘ + this.name);
}
现在,因为我们将 sayName 函数放在原型上,所以 Person 的每个实例都可以调用 sayName 函数以提醒该实例的名称。
现在我们的原型上已经有了 Person 构造函数和 sayName 函数,让我们实际创建一个 Person 实例然后调用 sayName 函数。
var tyler = new Person(‘Tyler’, 23);
tyler.sayName(); //alerts ‘My name is Tyler’
因此,创建 Person 构造函数、向其原型添加函数、创建 Person 实例,然后在其原型上调用函数的所有代码如下所示。
var Person = function(name, age){
this.name = name;
this.age = age;
}
Person.prototype.sayName = function(){
alert(‘My name is ‘ + this.name);
}
var tyler = new Person(‘Tyler’, 23);
tyler.sayName(); //alerts ‘My name is Tyler’
现在让我们看看在 JavaScript 中使用“new”关键字时实际发生了什么。你应该注意到的第一件事是,在我们的示例中使用了“new”之后,我们可以在“tyler”上调用一个方法(sayName),就像它是一个对象一样——因为它是。所以首先,我们知道我们的 Person 构造函数正在返回一个对象,无论我们是否可以在代码中看到它。其次,我们知道因为我们的 sayName 函数位于原型上而不是直接位于 Person 实例上,所以 Person 函数返回的对象必须在查找失败时委托给它的原型。更简单地说,当我们调用 tyler.sayName() 时,解释器会说:“好的,我要查看我们刚刚创建的‘tyler’对象,找到 sayName 函数,然后调用它。等一下,我在这里看不到——我看到的只是名字和年龄,让我检查一下原型。是的,看起来它在原型上,让我称之为。”。
下面是关于如何思考“new”关键字在 JavaScript 中实际作用的代码。它基本上是上一段的代码示例。我已经将“解释器视图”或解释器在注释中查看代码的方式。
var Person = function(name, age){
//The line below this creates an obj object that will delegate to the person’s prototype on failed lookups.
//var obj = Object.create(Person.prototype);
//The line directly below this sets ‘this’ to the newly created object
//this = obj;
this.name = name;
this.age = age;
//return this;
}
现在了解了“new”关键字在 JavaScript 中的真正作用,在 Angular 中创建服务应该更容易理解。
创建服务时要了解的最重要的事情是知道服务是使用“新”关键字实例化的。将这些知识与我们上面的示例相结合,您现在应该认识到您将直接将属性和方法附加到“this”,然后将从服务本身返回。让我们来看看这个实际操作。
与我们最初对 Factory 示例所做的不同,我们不需要创建一个对象然后返回该对象,因为就像之前多次提到的那样,我们使用了 'new' 关键字,因此解释器将创建该对象,它委托给它的原型,然后将它返回给我们,而我们不必做这项工作。
首先,让我们创建我们的“私有”和辅助函数。这看起来应该很熟悉,因为我们对我们的工厂做了完全相同的事情。我不会在这里解释每一行的作用,因为我在工厂示例中做过,如果您感到困惑,请重新阅读工厂示例。
app.service('myService', function($http, $q){
var baseUrl = 'https://itunes.apple.com/search?term=';
var _artist = '';
var _finalUrl = '';
var makeUrl = function(){
_artist = _artist.split(' ').join('+');
_finalUrl = baseUrl + _artist + '&callback=JSON_CALLBACK'
return _finalUrl;
}
});
现在,我们将在控制器中可用的所有方法附加到“this”。
app.service('myService', function($http, $q){
var baseUrl = 'https://itunes.apple.com/search?term=';
var _artist = '';
var _finalUrl = '';
var makeUrl = function(){
_artist = _artist.split(' ').join('+');
_finalUrl = baseUrl + _artist + '&callback=JSON_CALLBACK'
return _finalUrl;
}
this.setArtist = function(artist){
_artist = artist;
}
this.getArtist = function(){
return _artist;
}
this.callItunes = function(){
makeUrl();
var deferred = $q.defer();
$http({
method: 'JSONP',
url: _finalUrl
}).success(function(data){
deferred.resolve(data);
}).error(function(){
deferred.reject('There was an error')
})
return deferred.promise;
}
});
现在就像在我们的工厂中一样,setArtist、getArtist 和 callItunes 将在我们将 myService 传递到的任何控制器中可用。这是 myService 控制器(与我们的工厂控制器几乎完全相同)。
app.controller('myServiceCtrl', function($scope, myService){
$scope.data = {};
$scope.updateArtist = function(){
myService.setArtist($scope.data.artist);
};
$scope.submitArtist = function(){
myService.callItunes()
.then(function(data){
$scope.data.artistData = data;
}, function(data){
alert(data);
})
}
});
就像我之前提到的,一旦你真正理解了“新”的作用,服务几乎与 Angular 中的工厂相同。