使用new运算符而不是工厂函数调用构造函数有什么意义

时间:2014-05-20 21:58:06

标签: javascript angularjs factory

我正在试图找出何时在angularjs中使用服务与工厂。

这是我喜欢的答案,但我对一部分感到困惑

https://stackoverflow.com/a/20761653/663447

  

服务配方几乎与工厂配方相同,但在此处   Injector使用new运算符而不是a调用构造函数   工厂功能。

这是什么意思?为什么我需要一个带有new运算符而不是工厂函数的构造函数。

感谢。

3 个答案:

答案 0 :(得分:0)

  

这是什么意思?为什么我需要一个带有new运算符而不是工厂函数的构造函数。

因为构造函数将原型与它们生成的对象相关联。工厂函数可以这样做(通过调用底层的构造函数,或者使用Object.create覆盖范围内),但是如果你有一个构造函数坐在那里确实你想要什么,嗯,很高兴能够使用它。

让我们从Angular退一步看一个简单的例子:

function Foo() {
    this.answer = 42;
}

这几乎可以肯定是一个构造函数。那就是:

var f = new Foo();
console.log(f.answer); // 42

如果没有new,我们会收到错误:

var f = Foo();
console.log(f.answer); // TypeError: Cannot read property 'answer' of undefined

...因为Foo没有返回值。

原型就像这样:

function Foo() {
}
Foo.prototype.speak = function() {
    alert("I'm a Foo");
};
var f = new Foo();
f.speak(); // alerts "I'm a Foo"

...再次,如果您没有使用new,则在尝试访问f.speak时会出错。

工厂函数可以创建由原型支持的对象,但它需要一个(微不足道的)额外步骤。他们只是做得很好,如果你有一个构造函数,你可以直接使用它。

答案 1 :(得分:0)

基本上它减少了你需要编写的代码量。如果在对象上调用new,则可以使用它来定义构造函数中的函数,如果您只是调用函数,则由您创建对象并将其返回。

答案 2 :(得分:0)

这是我以前给出的一个很长的答案。它非常详细地解释了使用“' new'关键字vs不使用它。

TL; DR

1)当您使用工厂时,您需要创建一个对象,向其添加属性,然后返回该对象。当您将此工厂传递到控制器中时,该对象上的这些属性现在将通过您的工厂在该控制器中可用。

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)当您使用服务时,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)工厂
工厂是最流行的创建和配置服务的方式。 DR说的确没有比TL更多的东西。您只需创建一个对象,向其添加属性,然后返回该对象。然后,当您将工厂传递到控制器时,该对象上的这些属性现在将通过您的工厂在该控制器中可用。下面是一个更广泛的例子。

app.factory(‘myFactory’, function(){
  var service = {};
  return service;
});

当我们将'myFactory'传递给我们的控制器时,我们可以使用我们附加到'service'的任何属性。

现在让我们在回调函数中添加一些“私有”变量。这些不能直接从控制器访问,但我们最终会在'service'上设置一些getter / setter方法,以便在需要时能够改变这些'private'变量。

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;
});

在这里你会注意到我们没有将这些变量/功能附加到'service'。我们只是创建它们以便以后使用或修改它们。

  • baseUrl是iTunes API所需的基本网址
  • _artist是我们希望查找的艺术家
  • _finalUrl是最终完全构建的URL,我们将调用iTunes makeUrl是一个函数,它将创建并返回我们的 iTunes友好网址。

现在我们的助手/私有变量和函数已经到位,让我们为'service'对象添加一些属性。无论我们提供什么“服务”,我们都可以直接使用我们通过“myFactory”的控制器。

我们将创建setArtist和getArtist方法,只返回或设置艺术家。我们还将创建一个方法,使用我们创建的URL调用iTunes API。一旦数据从iTunes API返回,此方法将返回一个承诺。如果你在Angular中没有使用promises的经验,我强烈建议你深入研究它们。

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()方法,只有在我们的承诺与iTunes数据一起完成后才设置$ scope.data.artistData。你会注意到我们的控制器非常“薄”。我们所有的逻辑和持久数据都位于我们的服务中,而不是我们的控制器中。

2)服务
在处理创建服务时,最重要的事情可能是它使用'new'关键字进行实例化。对于JavaScript 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中创建服务应该更容易理解。

创建服务时要了解的最重要的事情是知道服务是使用'new'关键字实例化的。将这些知识与上面的示例相结合,您现在应该认识到您将直接将属性和方法附加到'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的工厂相同。