AngularJS: "Controller as" or "$scope"?

原文地址: http://codetunnel.io/angularjs-controller-as-or-scope/
作者:Chev

译者:timeless,转载请注明出处http://blog.csdn.net/u014788227


           我才读完一篇由JonPapa写的博客,他谈论了使用 Controller as someName 替代在你的controller中注入$scope的未来趋势。我想详细讲解下他的观点,但是先让我向你们中还没有听过这种(controller as)技术的人说明一下这个技术。它是个相当简单的特性,但是我认为它甚至拥有比John所涵盖的(好处)更多更有用的意义。

          传统上你可能习惯于像下面那样做一件事:

         

<div ng-controller="MainController">  
  {{ someObj.someProp }}
</div>  

app.controller('MainController', function ($scope) {  
  $scope.someObj = {
    someProp: 'Some value.'
  };
});

有了新的 Controller as技术你现在可以像这样来做一件事情:

<div ng-controller="MainController as main">  
  {{ main.someProp }}
</div> 

app.controller('MainController', function () {  
  this.someProp = 'Some value.'
});

            John Papa在他的博客中提出以上两种方式唯一并且真正的不同之处在于个人偏好。他说Controller as技术就像语法糖,如果你想使用就使用。我大体上是同意他的说法的,但我也认为这技术解决了一些之前我在Angular中遇到的问题。

           像这样的一个问题就是作用域原型继承的方式。在javascript中,当一个对象原型继承另一个对象,你就能从父对象获取到所有的属性和方法。

var obj1 = {  
  someProp: 'obj1 property!',
  someMethod: function () {
    alert('obj1 method!');
  }
};
var obj2 = Object.create(obj1);  
obj2.someProp = 'obj2 property!';  



            对于以上代码你可能想说,当对象obj2被实例化时someProp属性会从 "obj1 property!"变为 "obj2 property!"。但是那样的方式并不能改变对象原型中的属性(译者注:obj2的__proto__属性为obj1,也就是实例化obj2的“类”的原型是obj1,obj2中纯变量属性的值的改变是不会影响到obj1中的同名属性值的。@flag1,文末具体解释)。
那些所有的代码所做的只是在obj2对象上创建了一个名为someProp的属性从而挡住了obj1中潜在的同名属性someProp。
如果你运行 delete obj2.someProp,obj2上对象面的someProp属性并不会消失,如果你打印出来,它显示的是obj1.someProp的值。这是嵌套作用域所起到的作用。在子对象范围设置一个属性值并不会改变父对象范围中同名属性的值。它只是会隐藏(遮住)父对象中的同名属性值。
为什么我一开始的有关典型的$scope技术例子中会在$scope上绑定一个对象someObj并且设置了一个someProp的属性值。如果父作用域有一个foo的属性(变量),并且我想在子范围修改它的值(修改子范围同时父范围跟着改变),那么foo必须是父作用域中一个对象中的属性。因为对象是引用传递,改变一个附着着父作用域的对象(子对象)的属性实际上确实就想改了那个属性(父子那个同名属性都会变)。如果我们在子范围设置对象属性的值,那个属性确实代表了那个对象(父对象)本身(两个对象引用的同一地址),属性并不会被隐藏。(以上可能有点晦涩难懂)

           这里有个例子来说明当你嵌套controller以及在$scope上绑定纯量(不是对象这种引用类型)时发生了什么。

<div ng-controller="ParentController">  
  ParentController: <input type="text" ng-model="foo" />
  <div ng-controller="ChildController">
    ChildController: <input type="text" ng-model="foo" />
  </div>
</div>  

app  
  .controller('ParentController', function ($scope) {
    $scope.foo = "bar";
  })
  .controller('ChildController', function ($scope) { /*empty*/ });


               起初子作用域并没有foo这个属性,但是它读取了从父作用域继承的foo属性。这就是为什么你改变父作用域的输入子作用域的输入也会更新的原因。但是,一旦你修改了子作用域的输入,它就使用了它自己范围的值,并且更新了子范围的foo值(译者注,自己建了一个foo,与父作用域的foo完全无关)。由于原型继承的作用,子作用域的foo属性仅仅遮住了父作用域的foo属性。换句话说,由于子作用域的foo属性变为了一个新值,父作用域中的foo属性是保持不变的。一旦你改变子作用域的输入(译者注:子scope生成一个独立的foo属性,不再依赖于原型继承从而去父scope寻找未拥有的属性,@flag2),你再去改变父作用域的输入子作用域是不会再跟着改变的,因为子作用域现在有了它自己的foo属性和对应的值(你在子作用域输入的)。

             作用域继承这个问题我经常在stackflow上看到新手遇到。传统上,修复方式就是我一开始的例子,把你的纯变量绑定到作用域上的一个对象上。


<div ng-controller="ParentController">  
  ParentController: <input type="text" ng-model="obj.foo" />
  <div ng-controller="ChildController">
    ChildController: <input type="text" ng-model="obj.foo" />
  </div>
</div>  

app  
  .controller('ParentController', function ($scope) {
    $scope.obj = {
      foo: "bar"
    };
  })
  .controller('ChildController', function ($scope) { /*empty*/ });


               你现在可能看到两个输入互相跟着更新了。我们不得不使用这样一个奇怪的设定来避免这个问题,真是令人唏嘘。
现在有了新增的Controller as技术我们不用再担心了。我们可以简单的参考我们想参考的controller,并且不用再担心这作用域继承的细微差异。

<div ng-controller="ParentController as parent">  
  ParentController: <input type="text" ng-model="parent.foo" />
  parent.foo: {{ parent.foo }}
  <div ng-controller="ChildController as child">
    ChildController: <input type="text" ng-model="parent.foo" />
    parent.foo: {{ parent.foo }}
  </div>
</div>  

app  
  .controller('ParentController', function () {
    this.foo = "bar";
  })
  .controller('ChildController', function () { /*empty*/ });



              这不仅帮助我们绕开了令人烦恼的继承问题,我认为它比起使用$scope使得界面更加干净。你可以清晰的看到,即使在 被子controller管理(控制)的DOM,我们也可以明确的绑定一些东西到父作用域的值中。我们不再必须使用$scope.$parent这样无意义的表达式,或者创建复杂的服务,到处注入它们。你现在只需要在一开始简单的参考你想参考的controller和值。

              在脑子里保持这样的想法很重要:Angular对于这个新语法实际上做了什么?它不是神器的全局变量,你可以在哪都参考它。它知识一个变量,参考了controller的 execution context,并且这变量在背后实际上与$scope有关。

本质上是这样:

app.controller('MyController', function () {  
  this.someValue = "Hello!";
});

和这样无异:

app.controller('MyController', function ($scope) {  
  $scope.myController = this;
  this.someValue = "Hello!";
}

        仅仅是“as”语法在controller的作用域上明确的创建了一个命名空间。在后者例子中你必须手动创建命名空间。不相信我?这有证明。

          正如你所看到的,this和$scope.myController在上面的例子中是相同的对象。这给了你很大的一个线索告诉你angular在背后是怎么做的。它只是把controller上的变量绑定到$scope上。明确的给我们要暴露的变量赋予了一个十分可读的以及合乎逻辑的命名空间。这也意味着如果我们不注意子作用域中myController,它依然会遮住我们在父作用域中明确定义的myController属性。所以只要你不在你的子作用域中设置一个变量和值都与你在使用“controller as someName”的父作用域中的变量一样的(@flag3),那么所有的继承问题将会远离你并且帮助你不轻易的犯此类错误。


(完)

译者注:

以下为个人观点,如有错误,欢迎指正

@flag1

function F1() {
        this.name = "f1";
    }

    function F2() {}

    f1 = new F1();
    F2.prototype = f1;

    f2 = new F2();
    f2.name ="f2";
    console.log(f1.name)
    console.log(f2.name)


    我把scalar 译为“纯量”,就是指this.name这样的,反例就是this.person = {name : "f1"}
    F2的原型是f1,如果f2中没有name属性,那么由于原型继承就会去f1中寻找。如果你给f2赋予name属性并赋值。那么就相当于新建了一个值为“f2”的name属性,并且与f1无关,互相不会有影响


@flag2

对于这个例子,一开始子scope没有foo属性,所以子scope显示的时候会由于原型继承去父scope中寻找,找到了就显示。此时你改变父scope的输入,子scope也会跟随变化。但是一旦你在子scope输入值,子scope自己就会新建一个foo属性并赋予输入的值。此时由于foo是纯量,那么此时子scope与父scope就完全无关了,不管哪一方变化都不会影响对方了。如果换做对象,因为是引用类型,就不会有这问题了。

@flag3

function F1() {
		this.person = {
			name : "f1"
		};
	}

	function F2() {}
	f1 = new F1();
	F2.prototype = f1; 
	f2 = new F2();
	f2.person = {
		name : "f2"
	}
	// f2.person.name = "f2";
	console.log(f2.person.name)
	console.log(f1.person.name)

这样就懂了。

(完)

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值