- AngularJS入門與進階
- 江榮波
- 2489字
- 2020-11-28 23:44:32
5.2 AngularJS作用域繼承
5.2.1 JavaScript對象繼承機制
學習AngularJS作用域繼承之前,我們需要先了解一下JavaScript對象的繼承機制。JavaScript語言遵循ECMAScript規范,在ECMAScript 6規范之前,JavaScript語言并沒有類的概念,也不具備面向對象多態的特性,所以嚴格地講JavaScript并不是一門面向對象語言,而是一門基于對象的語言。
JavaScript構造對象通常有兩種方式。第一種方式是通過字面量創建,形式如下:
var obj = { name:'jane', age:32 };
另一種方式是通過對象的構造方法來創建,需要用到function關鍵字。JavaScript語言中的方法可以作為構造方法創建對象,比較容易讓人產生疑惑。例如,我們使用下面這段代碼定義一個構造方法:
function Person(name, age){ this.name = name; this.age = age; this.eat = function() { console.log('eat..'); } }
但是我們把它作為普通方法調用就不會有問題。當使用構造方法創建對象時需要用到JavaScript的new關鍵字,使用方法如下:
var person = new Person('Jane',32);
在實際項目中,當我們明確地使用function關鍵字定義一個構造方法時,構造方法名稱通常采用帕斯卡命名法,即每個單詞首字母大寫(例如:LoginController);而使用function定義一個普通方法時,方法名稱可采用駝峰命名法,駝峰命名法跟帕斯卡命名法相似,只是首字母為小寫(例如:checkUser),看上去像駝峰,因此而得名。
JavaScript語言為我們提供了幾個內置的構造方法,例如Object、Array 、String、Boolean等。我們可以直接使用這些構造方法創建對象。
了解了JavaScript對象的創建方法后,我們再來看看JavaScript的對象繼承機制。JavaScript語言有以下3種方式實現對象繼承。
1.構造方法原型鏈繼承
每個JavaScript構造方法都有一個名稱為prototype的屬性,可以指向另一個對象。當我們訪問對象屬性時(例如obj.name), JavaScript引擎會從對象的所有屬性中查找該屬性,如果找到就返回屬性值,如果沒有找到就繼續從prototype屬性指向的對象屬性中查找,如果仍然沒有找到,則會沿著prototype鏈一直查找下去,直到prototype鏈結束或找到對象為止。接下來我們看一個原型鏈繼承的案例,代碼如下:
代碼清單:ch05\ch05_02.html
<!doctype html> <html> <head> <meta charset="UTF-8"> <title>ch05_02</title> </head> <body> <script type="text/javascript"> function Animal(){ this.eat = function(){ console.log('eat...'); } } function Cat(age){ this.age = age; } Cat.prototype = new Animal(); var cat = new Cat(10); console.log("cat.age=" + cat.age); cat.eat(); </script> </body> </html>
如上面的代碼所示,首先定義兩個構造方法Animal和Cat,把Cat的prototype屬性指向一個Animal對象:
Cat.prototype = new Animal();
接下來通過new關鍵字創建一個Cat對象:
var cat = new Cat(10);
在瀏覽器中運行該案例,控制臺輸出:
cat.age=10 eat...
Cat構造方法中并沒有定義eat()方法,而我們通過Cat實例調用eat()方法輸出了內容,說明Cat對象通過prototype屬性繼承了Animal對象的eat()方法。
2.使用apply、call方法實現繼承
由于JavaScript構造方法的apply()、call()方法可以改變對象構造中“this”的上下文環境,使特定的對象實例具有對象構造中所定義的屬性、方法,因此我們可以使用apply()、call()方法實現JavaScript對象的繼承,例如下面的案例:
代碼清單:ch05\ch05_03.html
<!doctype html> <html> <head> <meta charset="UTF-8"> <title>ch05_03</title> </head> <body> <script type="text/javascript"> function Person(name, age){ this.name = name; this.age = age; } function Student(name, age, love){ //Person.apply(this, [name, age]); Person.call(this, name, age); this.love = love; } var student = new Student('jane',23, 'pingpong'); console.log("student.name=" + student.name); console.log("student.age=" + student.age); console.log("student.love=" + student.love); </script> </body> </html>
如上面的代碼所示,在本例中我們首先定義了Person構造方法,接著在Student構造方法中調用Person.call()方法實現了繼承,然后通過new關鍵字創建一個Student對象,在控制臺中輸出Student對象的屬性值。在瀏覽器中預覽ch05_03.html頁面,打開開發人員工具,控制臺輸出內容如下:
student.name=jane student.age=23 student.love=pingpong
說明Student對象繼承了Person對象的name和age屬性。apply()方法和call()方法的不同之處在于apply()方法只接收兩個參數,第二個參數是一個數組,而call()方法可以接收多個參數。
3.對象實例間繼承
在JavaScript語言中,對象可以繼承另外一個對象的屬性,例如下面的案例:
代碼清單:ch05\ch05_04.html
<!doctype html> <html> <head> <meta charset="UTF-8"> <title>ch05_04</title> </head> <body> <script type="text/javascript"> function Person(name, age){ this.name = name; this.age = age; } var person = new Person('jane',28); var student = Object.create(person); student.love = "pingpong"; console.log(Object.getPrototypeOf(student)); console.log("student.name→"+student.name); console.log("student.age→"+student.age); console.log("student.love→"+student.love); </script> </body> </html>
如上面的代碼所示,在本例中我們用到了Object.create()方法,它的作用是以一個對象為原型創建另外一個對象,創建的對象和原對象具有相同的屬性,我們可以通過Object. getPrototypeOf()方法獲取新對象的原型。
在瀏覽器中運行ch05_04.html頁面,打開開發人員工具,控制臺輸出內容如下:
Person student.name→jane student.age→28 student.love→pingpong
結合源代碼,從輸出的日志信息可以看出,student對象繼承了Person對象的name和age屬性,調用Object.getPrototypeOf()方法獲取student對象的原型依然為Person。
本節中筆者對JavaScript對象的繼承方式進行了比較全面的學習,下一小節我們一起學習AngularJS如何使用原型方式實現作用域對象的繼承。
5.2.2 AngularJS作用域對象原型繼承
上一小節介紹了JavaScript語言中對象繼承的3種方式,其中AngularJS作用域對象繼承采用第一種方式,即構造方法原型鏈繼承。AngularJS作用域構造方法中提供了一個$new()成員方法,用于創建子作用域。AngularJS框架創建子作用域的過程大致如下:
var parent = $rootScope; var child = parent.$new();
我們不妨了解一下$new()方法的定義,內容如下,有興趣的讀者可以參考AngularJS源碼。
Scope.prototype = {
constructor: Scope,
$new: function(isolate, parent){
var child;
parent = parent || this;
if(isolate){
child = new Scope();
child.$root = this.$root;
} else {
// Only create a child scope class if somebody asks for one,
// but cache it to allow the VM to optimize lookups.
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;
}
// When the new scope is not isolated or we inherit from `this`, and
// the parent scope is destroyed, the property `$$destroyed` is
// inherited prototypically. In all other cases, this property
// needs to be set when the parent scope is destroyed.
// The listener needs to be added after the parent is set
if(isolate || parent ! = this)child.$on('$destroy',
destroyChildScope);
return child;
},
......
}
上面的代碼為AngularJS1.5.5版本中$new()方法的定義,如上面黑體代碼所示。其中,this.$$ChildScope為子作用域構造方法,由createChildScopeClass()方法調用返回。下面是createChildScopeClass()方法的定義:
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; } ChildScope.prototype = parent; return ChildScope; }
createChildScopeClass()方法中定義了ChildScope構造方法,ChildScope即為子作用域的構造方法,接著指定ChildScope的prototype屬性為parent, parent即為父作用域對象,這樣子作用域就繼承了父作用域的所有屬性。
上面我們了解了AngularJS作用域繼承機制,所有作用域對象都是$rootScope作用域的子作用域。還有一種情況需要我們考慮,即在AngularJS控制器中可以嵌套另外一個控制器,例如:
<div ng-app> <div ng-controller="OuterController"> <div ng-controller="InnerController"> </div> </div> </div>
在上面的代碼片段中,ng-controller指令范圍內嵌套了另外一個ng-controller指令,這種情況下AngularJS是如何處理作用域對象繼承的呢?過程如下:
AngularJS框架遍歷DOM元素,查找到ng-app指令時啟動應用,創建$rootScope作用域。
然后AngularJS框架查找到第一個ng-controller指令,指向名稱為OuterController的控制器,并調用$rootScope.$new()方法,以原型繼承的方式創建$rootScope作用域的子作用域對象(記為$scope1)。當OuterController構造方法接收一個名稱為$scope的參數時,AngularJS實例化控制器對象時會把$scope1對象注入控制器對象中。
接下來AngularJS繼續遍歷DOM元素,遇到第二個嵌套的ng-controller指令時調用$scope1. new()方法,以$scope1為原型創建子作用域(記為$scope2, $scope2作用域對象能夠訪問$scope1作用域對象的所有屬性)。
除了ng-app、ng-controller指令會創建作用域對象外,AngularJS指令也可能會產生子作用域,在后面的章節中我們會接觸到。本節內容就介紹這么多,下節我們開始學習AngularJS作用域監視機制。
- Functional Python Programming
- 演進式架構(原書第2版)
- Mastering Entity Framework Core 2.0
- Hands-On Image Processing with Python
- JMeter 性能測試實戰(第2版)
- NLTK基礎教程:用NLTK和Python庫構建機器學習應用
- CKA/CKAD應試教程:從Docker到Kubernetes完全攻略
- SAS數據統計分析與編程實踐
- OpenGL Data Visualization Cookbook
- MySQL入門很輕松(微課超值版)
- Regression Analysis with Python
- 深入理解BootLoader
- jQuery for Designers Beginner's Guide Second Edition
- 現代CPU性能分析與優化
- 超好玩的Scratch 3.5少兒編程