1.scope的几种继承
说到scope,我们可能会想到rootScope,因为所有的scope都是由rootScope产生出来的,我们也知道在angular里面 scope有几种继承形式:
1.scope为false,完成继承父的scope,子scope,父scope修改值都互相影响(其实同一scope下修改的是用一个值)
2.scope为true,子创建一个scope(注意此时和父是两个不用scope),但是还是继承父scope,怎么理解呢,拿个构造函数来说吧,我实例化这个constructor,那么实例化出来的对象应该是同时拥有constructor和prototype上的属性和方法,假如有个属性a在constructor和prototype都有,那么首先肯定先取constructor.a,constructor上没有,再取prototype.a,那么constructor可以比作子scope,prototype可以比作父scope,当子scope没有,再去父scope去取。
3.scope为{},那么此时创建一个独立的scope,和父scope是没有关系,两个scope都是互不想干的(此时有几种父传值给子的方式)
有了这些概念之后我们来看个例子:
html
<div ng-app="App">
<parent-scope>
<child-scope></child-scope>
</parent-scope>
</div>
js
angular.module("App", [])
.directive("parentScope", function () {
return {
restrict: "ECAM",
template: '<input type="text" ng-model="username"><child-scope></child-scope>',
scope: false,
controller: ['$scope', function ($scope) {
$scope.username = '张三'
}]
}
})
.directive("childScope", function () {
return {
restrict: "ECAM",
template: '<input type="text" ng-model="username">',
replace: true,
scope: false,
}
})
结果:子scope继承父scope,并且互相影响!
然后把子的scope改成true,结果是当子scope无此属性,父可以影响到子,当子scope有此属性,父影响不到子,子也影响不到父!
最后把子的scope改成{},那就毫无疑问,两者互不影响!
2.controller继承
可能看到controller继承有点懵?没听过controller继承?还是拿个上面例子来说,稍微改了下:
angular.module("App", [])
.directive("parentScope", function () {
return {
restrict: "ECAM",
template: '<input type="text" ng-model="ctrl.username"><child-scope></child-scope>',
scope: false,
controllerAs:'ctrl',
controller: ['$scope', function ($scope) {
var ctrl = this;
ctrl.username = '张三'
}]
}
})
.directive("childScope", function () {
return {
restrict: "ECAM",
template: '<input type="text" ng-model="ctrl.username">',
replace: true,
scope: true,
}
})
我把username绑到controller上,然后把子scope改成true,想下结果是什么?很多人认为可能和scope一样的吧,ctrl是在scope里面的,应该和scope继承一样,但是结果不是一样,是子scope为false结果一样,互相影响!无论改变哪个scope值,都会一起变化!
为什么会是这样的?
还记得scope是怎么创建的和怎么继承的吗?(不了解的可以点击这里)
这里我大概说下,首先创建的是rootScope(根作用域),然后创建子scope,创建子scope时候会判断此scope是否是隔离的,如果是不是隔离就会继承父scope,ChildScope.prototype = parent,如果是隔离的就new scope ,此时和原型继承毫无关系了。上段源代码:
$new: function (isolate, parent) {
var child;
parent = parent || this;
//是否是隔离scope
if (isolate) {
//此时和原型继无关
child = new Scope();
child.$root = this.$root;
} else {
//判断是否有
if (!this.$$ChildScope) {
//子构造函数的创建并继承父
this.$$ChildScope = createChildScopeClass(this);
}
//子作用域的创建
child = new this.$$ChildScope();
}
//把自己的父,兄弟姐妹,儿子整明白(要不然乱套了)
child.$parent = parent;
// 孩子的前一个兄弟节点为父亲的最后一个孩子
child.$$prevSibling = parent.$$childTail;
if (parent.$$childHead) {
parent.$$childTail.$$nextSibling = child;
parent.$$childTail = child;
} else {
parent.$$childHead = parent.$$childTail = child;
}
if (isolate || parent !== this) child.$on('$destroy', destroyChildScope);
return child;
}
function createChildScopeClass(parent) {
function ChildScope() {
this.$$watchers = this.$$nextSibling =
this.$$childHead = this.$$childTail = null;
this.$$listeners = {};
this.$$listenerCount = {};
this.$$watchersCount = 0;
this.$id = nextUid();
this.$$ChildScope = null;
this.$$suspended = false;
}
ChildScope.prototype = parent;
return ChildScope;
}
上面有注释,已经原理很清楚了,那么再来看上面的例子,首先创建rootScope,parentScope指令创建的scope继承rootScope,也是就用一scope了,所以id都为1。而childScope创建的scope虽然也继承rootScope,但是它还是个创建了一个新的scope(因为scope=true)。
此图为parentScope(id为1):
此图为childScope(id为2):
因为parentScope里面创建了一个controller,所以你在parentScope图中可以看到有个ctrl属性,根据原型继承ChildScope.prototype = parentScope,那么ChildScope原型上必定有ctrl属性,我们来看看ChildScope的__proto__属性:
所以不管输入那边的,它们都互相都受影响!这是一种继承方式,下面我们再来说下另外一种获取controller方式(用的比较少,严格的说不能是继承,只是拿到这个controller)
这里我们需要用到$controller service(angualr内置服务),下面看下怎么实现的:
html:
<div ng-app="App">
<div ng-controller="firstController">
<inherit-test></inherit-test>
</div>
</div>
js:
angular.module("App", [])
.controller("firstController", [function () {
var ctrl = this;
ctrl.name = "张三";
ctrl.age = 18;
ctrl.sex = '男';
}])
.directive("inheritTest", function () {
return {
restrict: "ECAM",
controllerAs: 'ctrl',
controller: ['$controller', '$scope', function ($controller, $scope) {
var ctrl = this;
ctrl.test = 'inherit';
var test = $controller('firstController', {
$scope: $scope
})
angular.extend(this, test);
console.log(test); //age: 18 name: "张三" sex: "男" test: "inherit"
}],
}
})
这里就和scope没有关系,其实大概原理是angular会把注册的controller放到一个invokeQueue数组里面,此时并没有执行,等到 angular初始化时 angularInit(window.document, bootstrap),然后$ControllerProvider.register.apply($ControllerProvider,firstController);在register方法里面把所有注册的controller放到一个controllers对象里面,我们看下这个register源码(无关的代码去掉了):
function $ControllerProvider() {
//装所有注册的controller的对象
var controllers = {};
//两个参数相当是('firstController',firstControllerConstructor)
this.register = function (name, constructor) {
//判断名称是hasOwnProperty抛出错误
assertNotHasOwnProperty(name, 'controller');
//是对象的话就让controllers继承它
if (isObject(name)) {
extend(controllers, name);
} else {
//与firstController为key,firstControllerConstructor为value形式放到controllers里面
controllers[name] = constructor;
}
};
所以我们可以根据controller注册名称可以去这个controllers对象里面找它。另外还有一种组件之间controller获取(用的是require,如果不懂的可以点击这里)。
3.this的指向
Controller是一个的构造函数,必须要new(实例化后)才能进行调用,所以必定会生成一个Controller实例,此实例也必然会继承构造函数中this绑定的所有属性与方法,此时的this指向的是Controller实例。再看下面代码:
angular.module("App", [])
.controller("firstController", ['$scope',function ($scope) {
var ctrl = this;
ctrl.name = "张三";
ctrl.age = 18;
ctrl.sex = '男';
$scope.method =function(){
console.log(this);//scope
}
}])
从上面的代码可以看出,它们其实没什么联系,如果非要说有的话,那么它们都可以在同一个构造函数中互相访问,$scope是这个函数的形参,this是这个函数的上下文,所以它们绝对不是同一个对象。Controller中的this与$scope都可以存储数据,最好不要数据分散存储两个对象中。好了说到这里都已经说的差不多了,有不正的地方请指正,不胜感激!!欢乐的时光总是过得特别快,又到时候和大家讲拜拜!!