JS 語法之--對象類型(構造器,class,this),高階對象(高階類,Minix模式)


1、JS 對象類型

  JavaScript 是一種基於原型(prototype)的面向對象語言,而不是基於類的面向對象語言

  C++, Java 有類Class 和實例Instance 的概念,類是一類事物 的抽象,而實例則是類的實體。

  JS是基於原型的語言,它只有原型對象的概念,原型對象就是一個模板,新的對象從這個模板構建從而獲取最初的屬性,任何對象在運行的時候可以動態的增加屬性,而且,任何一個對象都可以作為另一個對象的原型,這樣后者就可以共享前者的屬性。

2、定義類 

  字面式聲明方式

    

     這種方法也稱為 字面值創建對象。JS 1.2 開始支持

  ES6之前--構造器

    1、應以一個函數(構造器)對象,函數名首字母大寫

    2、使用this 的定義屬性

    3、使用new 和構造器創建一個新對象

    測試:
 1 // 定義類,構造器
 2 function Point(x, y) {
 3     this.x = x;
 4     this.y = y;
 5     this.show = () => {console.log(this, this.x, this.y)}
 6     console.log('======================')
 7 }
 8 
 9 //繼承
10 function Point3D(a, b, z) {
11     Point.call(this,a,b)
12     this.z = z;
13     console.log('print3d')
14 }
15 
16 console.log(Point3D)
17 p2 = new Point3D(1,2,3)
18 console.log('******************')
19 console.log(p2)
20 console.log('******************')
21 p2.show()
 
        

 

       結果:

 

1 Info: Start process (19:52:17)
2 [Function: Point3D]
3 ======================
4 print3d
5 ******************
6 Point3D { x: 1, y: 2, show: [Function], z: 3 }
7 ******************
8 Point3D { x: 1, y: 2, show: [Function], z: 3 } 1 2
9 Info: End process (19:52:17)

 

 

 

 

     new 構建一個新的通用對象,new操作符會將新對象的this 值傳遞給Point3D 構造器函數,函數為這個對象創建 z 屬性

     從上句話知道,new 后得到一個對象,使用這個對象的this 來調用構造器,那么如何執行‘基類’的構造方法

     使用Point3D 對象的this來執行Point 的構造器,所以使用call 方法,傳入子類的this

    最終,構造完成后,將對象賦值給 p2

    注意:如果不使用new 關鍵字,就是一個普通函數的函數調用,this不代表實例。

   

  ES6 中的class

     從ES 6開始,新提供了class 關鍵字,使得創建對象更加簡單,清晰。

    1. 類定義使用了class 關鍵字,創建的本質上海市函數,是一個特殊的函數
    2. 一個類只能擁有一個名為constructor 的構造方法,如果沒有顯式的定義一個構造方法,則會添加一個默認的constructor 方法
    3. 繼承使用extends 關鍵字
    4. 一個構造器可以使用super 關鍵字來調用一個父類的構造函數
    5. 類沒有私有屬性。

     測試:

 1 // 基類定義
 2 class Point {
 3     constructor(x, y) /*構造器 */{
 4         this.name = x
 5         this.age = y
 6     }
 7     show() /* 方法 */ {
 8         console.log(this.name, this.age)
 9     }
10 }
11 
12 
13 let p1 = new Point('tom', 12)
14 p1.show()
15 console.log(p1) // 可以看到對象的屬性
16 
17 //繼承
18 class Point3D extends Point {
19     constructor (a,b,c) {
20         console.log('---------------------------')
21         super(a, b)
22         this.password = c
23     }
24 }
25 
26 let p2 = new Point3D('jack',18,'123456')
27 p2.show()
28 console.log(p2)

 

     結果

1 Info: Start process (20:11:33)
2 tom 12
3 Point { name: 'tom', age: 12 }
4 ---------------------------
5 jack 18
6 Point3D { name: 'jack', age: 18, password: '123456' }
7 Info: End process (20:11:33)

 

   重寫方法:

     子類Point3D 的show方法,需要重寫

     

     子類中直接重寫父類的方法即可

    如果需要使用父類的方法,使用super.method() 的方式調用

    使用箭頭函數重寫上面的方法   

 1 // 基類定義
 2 class Point {
 3     constructor(x, y) /*構造器 */{
 4         this.name = x
 5         this.age = y
 6         // this.show =  function () {console.log(this.name, this.age)}
 7         this.show = () => console.log(this.name, this.age)
 8     }
 9 
10 }
11 
12 
13 let p1 = new Point('tom', 12)
14 console.log(p1.show())
15 console.log(p1) // 可以看到對象的屬性
16 
17 //繼承
18 class Point3D extends Point {
19     constructor (a,b,c) {
20         console.log('---------------------------')
21         super(a, b)
22         this.password = c
23         // this.show = function () {console.log(this, this.name, this.age, this.password)}
24         this.show = () => console.log(this, this.name, this.age, this.password)
25     }
26 }
27 
28 let p2 = new Point3D('jack',18,'123456')
29 p2.show()
30 console.log(p2)

 

      結果:從運行結果看,箭頭函數也支持子類的覆蓋

1 Info: Start process (20:24:52)
2 tom 12
3 undefined
4 Point { name: 'tom', age: 12, show: [Function] }
5 ---------------------------
6 Point3D { name: 'jack', age: 18, show: [Function], password: '123456' } 'jack' 18 '123456'
7 Point3D { name: 'jack', age: 18, show: [Function], password: '123456' }
8 Info: End process (20:24:53)

 

    測試:shou方法的調用次序

 1 // 基類定義
 2 class Point {
 3     constructor(x, y) /*構造器 */{
 4         this.name = x
 5         this.age = y
 6         // this.show =  function () {console.log(this.name, this.age)}
 7         // this.show = () => console.log(this.name, this.age,'===1')
 8     }
 9     show() {console.log(this.name, this.age,'===2')}
10 
11 
12 }
13 
14 let p1 = new Point('tom', 12)
15 console.log(p1.show())
16 console.log(p1) // 可以看到對象的屬性
17 
18 //繼承
19 class Point3D extends Point {
20     constructor (a,b,c) {
21         console.log('---------------------------')
22         super(a, b)
23         this.password = c
24         // this.show = function () {console.log(this, this.name, this.age, this.password)}
25         // this.show = () => console.log(this, this.name, this.age, this.password,'===3')
26     }
27     // show(){console.log(this, this.name, this.age, this.password,'===4')}
28 
29 }
30 
31 let p2 = new Point3D('jack',18,'123456')
32 p2.show()
33 console.log(p2)

 

    通過測試,p2.show() 先調用自己類的對象 show,再 到父類的對象的show,在自己所屬類的show在到父類的show

   靜態屬性:

    靜態屬性目前還沒有很好的支持

  靜態方法;

    在方法名前加上 static ,就是靜態方法   

 1 class Add{
 2     constructor(x, y) {
 3         this.x = x;
 4         this.y =y;
 5     }
 6     static point() {
 7         console.log(this.x)
 8     }
 9 }
10 
11 add = new Add(20,40)
12 console.log(Add)
13 Add.point()
14 //add.point() 實例不能直接訪問驚天方法,java 和C++ 一樣
15 add.constructor.point();// 對象通過 constructor 訪問靜態方法

 

  結果:

[Function: Add]
undefined
undefined

    靜態方法中的this 是Add類, 不是Add的實例

    注:靜態的概念不同於python的靜態

  this 的坑

    雖然JS 和C++ java 一樣有this,但是JS 的變現是不同的

    原因在於c++ java 是靜態編譯語言,this是編譯期間綁定,而js 是動態,運行期綁定   

    * 普通函數調用, this就是全局對象

    測試

    

    前提:

      函數執行的時候,會開啟新的執行上下文環境ExecutionContext

      創建this 屬性,但是this 是什么 就要看函數怎么調用。

    1. myFunction(1,2,3)普通函數調用方式,this 指向全局對象 ,全局對象是node.js的global或者瀏覽器中的window
    2. myObject.myFunction(1,2,3) 對象方法的調用方式,this指向包含該方法的對象
    3. call 和apply 方法調用,要看第一個參數是誰

    分析上例:

        

    解決方式 1:顯式傳入

      

      通過主動傳入對象,這樣就避開了 this 問題

    解決方式2: ES3(ES-262第三版),引入了apply,call方法

      

        apply,call方法都是函數對象的方法,第一參數都是傳入對象引入的

        

        apply 傳其他參數需要使用數組

        call 傳其他參數需要使用可變參數收集

        

    解決方式 3 ES5引入了bind方法       

      

  

   解決方式 4:ES6 支持this 箭頭函數

      ES6 新技術,就不需要兼容this 問題

      

      ES6 新的定義方式如下;

        

      最常用的就是bind()

7、高階對象,高階類,或稱為Mixin模式

  Mixin模式。混合模式,這是一種不用繼承 就可以 復用的技術,主要還是為了解決多重繼承的問題,

  多繼承的繼承路徑是個問題

  JS 是基於對象,類,和對象都是對象模板

  混合Mixin 指的是,將一個對象的全部或者部分拷貝到另一個對象上去,其實就是屬性了

  可以將多個類或者對象混合成一個類或對象

  繼承的實現:

    先看一個繼承實現的例子:

      

    父類構造函數中,要求具有屬性 stringify 的序列化函數,如果沒有則拋出異常      

 1 class Serillization {
 2     constructor(){
 3         console.log('serialization constructor ------')
 4         if (typeof(this.stringify) !== 'function') {
 5             throw new ReferenceError('should define stringify')
 6         }
 7     }
 8 }
 9 
10 class Point extends Serillization {
11     constructor(x, y) {
12         console.log('point constructor -------')
13         super();// 繼承必須調用 super()
14         this.x = x
15         this.y = y
16     }
17     stringify() {
18         return `<Point x=${this.x}, y=${this.y}>`
19     }
20 }
21 
22 class Point3D extends Point{
23     constructor(x, y, z){
24         super(x, y)
25         this.z = z
26     }
27     stringify (){
28         return `<Point x=${this.x}, y=${this.y}, z=${this.z}>`
29     }
30 }
31 
32 p = new Point(4,5)
33 console.log(p.stringify())
34 p3d = new Point3D(7,8,9)
35 console.log(p3d.stringify())

 

    結果:  

1 Info: Start process (21:53:17)
2 point constructor -------
3 serialization constructor ------
4 <Point x=4, y=5>
5 point constructor -------
6 serialization constructor ------
7 <Point x=7, y=8, z=9>
8 Info: End process (21:53:18)

 

 

   高階對象的實現:

      將類的繼承構建成箭頭函數  

 1 // 普通的繼承
 2 class A extends Object{}; // 大寫O
 3 console.log(A)
 4 
 5 // 匿名類
 6 const A1 = class {
 7     constructor(x) {
 8         this.x = x
 9     }
10 }
11 
12 console.log(A1)
13 console.log(new A1(100).x)
14 
15 console.log('--------------------------------------------')
16 
17 // 匿名類繼承
18 const B = class extends Object { // 這里返回的是一個類
19     constructor(){
20         super()
21         console.log('B constructor')
22     }
23 }
24 console.log(B)
25 b = new B()
26 console.log(b) // B {}     B 是?
27 
28 
29 console.log('--------------------------------------------')
30 const C = Sup => class extends Sup { // 這里返回的是一個函數
31     constructor(){
32         super()
33         console.log('C constructor')
34     }
35 }
36 
37 
38 
39 cls = new C(Object) // 不可以new 因為返回的是一個普通函數
40 
41 
42 obj2 = new (C(Object))()
43 
44 cls = C(Object) // 返回來,調用一次,才能用// 返回一個類 
45 obj = new cls()
46 console.log(obj)
47 
48 // cls = C(A)
49 // console.log(cls)
50 // c = new cls()
51 // console.log(c)
52 
53 // c1 = new (C(Object))()

 

 

 

    改造上面序列化的例子:

       

 1 const Serialication = Sup => class extends Sup{
 2     constructor(...args) {
 3         console.log('seroalization ------')
 4         super(...args)
 5         if (typeof(this.stringify) !== 'function') {
 6             throw new ReferenceError('should define stringify')
 7         }
 8     }
 9 }
10 
11 class Point {
12     constructor(x, y) {
13         console.log('point constructor -------')
14         this.x = x
15         this.y = y
16     }
17 }
18 
19 class Point3D extends Serialication(Point){
20     constructor(x, y, z){
21         super(x, y) // super 是 Serialication(Point) 包裝過的新類型
22         this.z = z
23     }
24     stringify (){
25         return `<Point x=${this.x}, y=${this.y}, z=${this.z}>`
26     }
27 }
28 
29 let p3d = new Point3D(70,80,90)
30 console.log(p3d.stringify())
代碼

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM