整合營銷服務(wù)商

          電腦端+手機端+微信端=數(shù)據(jù)同步管理

          免費咨詢熱線:

          JavaScript:class繼承

          上面的章節(jié)中我們看到了JavaScript的對象模型是基于原型實現(xiàn)的,特點是簡單,缺點是理解起來比傳統(tǒng)的類-實例模型要困難,最大的缺點是繼承的實現(xiàn)需要編寫大量代碼,并且需要正確實現(xiàn)原型鏈。

          有沒有更簡單的寫法?有!

          新的關(guān)鍵字class從ES6開始正式被引入到JavaScript中。class的目的就是讓定義類更簡單。

          我們先回顧用函數(shù)實現(xiàn)Student的方法:

          function Student(name) {
           this.name = name;
          }
          Student.prototype.hello = function () {
           alert('Hello, ' + this.name + '!');
          }
          

          如果用新的class關(guān)鍵字來編寫Student,可以這樣寫:

          class Student {
           constructor(name) {
           this.name = name;
           }
           hello() {
           alert('Hello, ' + this.name + '!');
           }
          }
          

          比較一下就可以發(fā)現(xiàn),class的定義包含了構(gòu)造函數(shù)constructor和定義在原型對象上的函數(shù)hello()(注意沒有function關(guān)鍵字),這樣就避免了Student.prototype.hello = function () {...}這樣分散的代碼。

          最后,創(chuàng)建一個Student對象代碼和前面章節(jié)完全一樣:

          var xiaoming = new Student('小明');
          xiaoming.hello();
          

          class繼承

          用class定義對象的另一個巨大的好處是繼承更方便了。想一想我們從Student派生一個PrimaryStudent需要編寫的代碼量。現(xiàn)在,原型繼承的中間對象,原型對象的構(gòu)造函數(shù)等等都不需要考慮了,直接通過extends來實現(xiàn):

          class PrimaryStudent extends Student {
           constructor(name, grade) {
           super(name); // 記得用super調(diào)用父類的構(gòu)造方法!
           this.grade = grade;
           }
           myGrade() {
           alert('I am at grade ' + this.grade);
           }
          }
          

          注意PrimaryStudent的定義也是class關(guān)鍵字實現(xiàn)的,而extends則表示原型鏈對象來自Student。子類的構(gòu)造函數(shù)可能會與父類不太相同,例如,PrimaryStudent需要name和grade兩個參數(shù),并且需要通過super(name)來調(diào)用父類的構(gòu)造函數(shù),否則父類的name屬性無法正常初始化。

          PrimaryStudent已經(jīng)自動獲得了父類Student的hello方法,我們又在子類中定義了新的myGrade方法。

          ES6引入的class和原有的JavaScript原型繼承有什么區(qū)別呢?實際上它們沒有任何區(qū)別,class的作用就是讓JavaScript引擎去實現(xiàn)原來需要我們自己編寫的原型鏈代碼。簡而言之,用class的好處就是極大地簡化了原型鏈代碼。

          你一定會問,class這么好用,能不能現(xiàn)在就用上?

          現(xiàn)在用還早了點,因為不是所有的主流瀏覽器都支持ES6的class。如果一定要現(xiàn)在就用上,就需要一個工具把class代碼轉(zhuǎn)換為傳統(tǒng)的prototype代碼,可以試試Babel這個工具。

          需要瀏覽器支持ES6的class,如果遇到SyntaxError,則說明瀏覽器不支持class語法,請換一個最新的瀏覽器試試。

          類是用于創(chuàng)建對象的模板。JavaScript中生成對象實例的方法是通過構(gòu)造函數(shù),這跟主流面向?qū)ο笳Z言(java,C#)寫法上差異較大,如下:

          function Point(x, y) {
            this.x = x;
            this.y = y;
          }
          
          Point.prototype.toString = function () {
            return '(' + this.x + ', ' + this.y + ')';
          };
          
          var p = new Point(1, 1);

          ES6 提供了更接近Java語言的寫法,引入了 Class(類)這個概念,作為對象的模板。通過class關(guān)鍵字,可以定義類。

          如下:constructor()是構(gòu)造方法,而this代表實例對象:

          class Point {
            constructor(x, y) {
              this.x = x;
              this.y = y;
            }
          
            toString() {
              return '(' + this.x + ', ' + this.y + ')';
            }
          }

          類的數(shù)據(jù)類型就是函數(shù),它本身就是指向函數(shù)的構(gòu)造函數(shù):

          // ES5 函數(shù)聲明
          function Point() {
              //...
          }
          
          // ES6 類聲明
          class Point {
            //....
            constructor() {
            }
          }
          typeof Point // "function"
          Point === Point.prototype.constructor // true

          在類里面定義的方法是掛到Point.prototype,所以類只是提供了語法糖,本質(zhì)還是原型鏈調(diào)用。

          class Point {
            constructor(x, y) {
              this.x = x;
              this.y = y;
            }
          
            toString() {
              return '(' + this.x + ', ' + this.y + ')';
            }
          }
          
          Point.prototype = {
            //....
            toString()
          }
          var p = new Point(1, 1);
          p.toString() // (1,1)

          類的另一種定義方式類表達式

          // 未命名/匿名類
          let Point = class {
            constructor(x, y) {
              this.x = x;
              this.y = y;
            }
          };
          Point.name // Point

          函數(shù)聲明和類聲明有個重要區(qū)別,函數(shù)聲明會提升,類聲明不會提升。

          constructor()

          constructor()方法是類的默認方法,new生成實例對象時會自動調(diào)用該方法。

          一個類必須有constructor()方法,如果沒有顯式定義,引擎會默認添加一個空的constructor()

          constructor()方法默認返回實例對象(即this)。

          class Point {
          }
          
          // 自動添加
          class Point {
            constructor() {}
          }

          getter和setter

          與 ES5 一樣,在類的內(nèi)部可以使用getset關(guān)鍵字,對某個屬性設(shè)置存值函數(shù)和取值函數(shù),攔截該屬性的存取行為。

          class User {
            constructor(name) {
              this.name = name;
            }
          
            get name() {
              return this.name;
            }
          
            set name(value) {
              this.name = value;
            }
          }
          
          

          this

          類的方法內(nèi)部的this,它默認指向類的實例,在調(diào)用存在this的方法時,需要使用 obj.method()方式,否則會報錯。

          class User {
            constructor(name) {
              this.name = name;
            }
            printName(){
              console.log('Name is ' + this.name)
            }
          }
          const user = new User('jack')
          user.printName() // Name is jack
          const { printName } = user;
          printName()     // 報錯 Cannot read properties of undefined (reading 'name')

          如果要單獨調(diào)用又不報錯,一種方法可以在構(gòu)造方法里調(diào)用bind(this)

          class User {
            constructor(name) {
              this.name = name;
              this.printName = this.printName.bind(this);
            }
            printName(){
              console.log('Name is ' + this.name)
            }
          }
          const user = new User('jack')
          const { printName } = user;
          printName()     // Name is jack

          bind(this) 會創(chuàng)建一個新函數(shù),并將傳入的this作為該函數(shù)在調(diào)用時上下文指向。

          另外可以使用箭頭函數(shù),因為箭頭函數(shù)內(nèi)部的this總是指向定義時所在的對象。

          class User {
            constructor(name) {
              this.name = name;
            }
            printName = () => {
              console.log('Name is ' + this.name)
            }
          }
          const user = new User('jack')
          const { printName } = user;
          printName()     // Name is jack

          靜態(tài)屬性

          靜態(tài)屬性指的是類本身的屬性,而不是定義在實例對象this上的屬性。

          class User {
          }
          
          User.prop = 1;
          User.prop // 1

          靜態(tài)方法

          可以在類里面定義靜態(tài)方法,該方法不會被對象實例繼承,而是直接通過類來調(diào)用。

          靜態(tài)方法里使用this是指向類。

          class Utils {
            static printInfo() {
               this.info();
            }
            static info() {
               console.log('hello');
            }
          }
          Utils.printInfo() // hello

          關(guān)于方法的調(diào)用范圍限制,比如:私有公有,ES6暫時沒有提供,一般是通過約定,比如:在方法前面加下劃線_print()表示私有方法。

          繼承

          Java中通過extends實現(xiàn)類的繼承。ES6中類也可以通過extends實現(xiàn)繼承。

          繼承時,子類必須在constructor方法中調(diào)用super方法,否則新建實例時會報錯。

          class Point3D extends Point {
            constructor(x, y, z) {
              super(x, y); // 調(diào)用父類的constructor(x, y)
              this.z = z;
            }
          
            toString() {
              return super.toString() + '  ' + this.z ; // 調(diào)用父類的toString()
            }
          }

          父類的靜態(tài)方法,也會被子類繼承。

          class Parent {
            static info() {
              console.log('hello world');
            }
          }
          
          class Child extends Parent {
          }
          
          Child.info()  // hello world
          
          

          super關(guān)鍵字

          在子類的構(gòu)造函數(shù)必須執(zhí)行一次super函數(shù),它代表了父類的構(gòu)造函數(shù)。

          class Parent {}
          
          class Child extends Parent {
            constructor() {
              super();
            }
          }

          在子類普通方法中通過super調(diào)用父類的方法時,方法內(nèi)部的this指向當(dāng)前的子類實例。

          class Parent {
            constructor() {
              this.x = 1;
              this.y = 10
            }
            printParent() {
              console.log(this.y);
            }
            print() {
              console.log(this.x);
            }
          }
          
          class Child extends Parent {
            constructor() {
              super();
              this.x = 2;
            }
            m() {
              super.print();
            }
          }
          
          let c = new Child();
          c.printParent() // 10
          c.m() // 2

          _proto_和prototype

          初學(xué)JavaScript時,_proto_prototype 很容易混淆。首先我們知道每個JS對象都會對應(yīng)一個原型對象,并從原型對象繼承屬性和方法。

          • prototype 一些內(nèi)置對象和函數(shù)的屬性,它是一個指針,指向一個對象,這個對象的用途就是包含所有實例共享的屬性和方法(我們把這個對象叫做原型對象)。
          • _proto_ 每個對象都有這個屬性,一般指向?qū)?yīng)的構(gòu)造函數(shù)的prototype屬性。

          下圖是一些擁有prototype內(nèi)置對象。

          prototype

          根據(jù)上面描述,看下面代碼

          var obj = {} // 等同于 var obj = new Object()
          
          // obj.__proto__指向Object構(gòu)造函數(shù)的prototype
          obj.__proto__ === Object.prototype // true 
          
          // obj.toString 調(diào)用方法從Object.prototype繼承
          obj.toString === obj.__proto__.toString // true
          
          // 數(shù)組
          var arr = []
          arr.__proto__ === Array.prototype // true 

          對于function對象,聲明的每個function同時擁有prototype__proto__屬性,創(chuàng)建的對象屬性__proto__指向函數(shù)prototype,函數(shù)的__proto__又指向內(nèi)置函數(shù)對象(Function)的prototype

          function Foo(){}
          var f = new Foo();
          f.__proto__ === Foo.prototype // true
          Foo.__proto__ === Function.prototype // true
          
          

          繼承中的__proto__

          類作為構(gòu)造函數(shù)的語法糖,也會同時有prototype屬性和__proto__屬性,因此同時存在兩條繼承鏈。

          1. 子類的__proto__屬性,表示構(gòu)造函數(shù)的繼承,總是指向父類。
          2. 子類prototype屬性的__proto__屬性,表示方法的繼承,總是指向父類的prototype屬性。
          class Parent {
          }
          
          class Child extends Parent {
          }
          
          Child.__proto__ === Parent // true
          Child.prototype.__proto__ === Parent.prototype // true

          繼承實例中的__proto__

          子類實例的__proto__屬性,指向子類構(gòu)造方法的prototype

          子類實例的__proto__屬性的__proto__屬性,指向父類實例的__proto__屬性。也就是說,子類的原型的原型,是父類的原型。

          class Parent {
          }
          
          class Child extends Parent {
          }
          
          var p = new Parent();
          var c = new Child();
          
          c.__proto__ === p.__proto__ // false
          c.__proto__ === Child.prototype // true
          c.__proto__.__proto__ === p.__proto__ // true
          
          

          小結(jié)

          JavaScript中的Class更多的還是語法糖,本質(zhì)上繞不開原型鏈。歡迎大家留言交流。

          <!DOCTYPE html>
          <html lang="en">
          <head>
              <meta charset="UTF-8">
              <title>登錄頁面</title>
              /*總體的樣式*/
              <style>
              	/*盒子樣式*/
                  #box{
                      width: 350px; //寬
                      height: 450px; //高
                      border: 1px solid black; //邊框
                      border-radius: 10px; //邊框弧度
                      font-family: 黑體; //字體
                      letter-spacing:8px; //段間距
                      word-spacing: 10px; //字間距
                      line-height: 40px; //行高
                      font-size: 18px; //字大小
                      padding: 20px; //內(nèi)邊框
                  }
                  /*給'注冊'賦予樣式*/
                  .register{
                      width:280px ; //寬
                      height: 50px; //高
                      background-color: skyblue; //背景顏色
                      border-radius: 10px; //邊框弧度
          
                  }
                  /*將所有邊框都改變*/
                  *{
                      border-radius: 5px; 邊框弧度
                  }
                  /*使用class選擇器,賦予number寬高和邊框*/
                  .number{
                      width: 185px; //寬
                      height: 27px; //高
                      border-width: 1px; //邊框?qū)挾?
                  }
                  /*id選擇器*/
                  #two{
                      width: 55px; //寬
                      border-width: 1px; 邊框?qū)挾?        }
                  /*id選擇器*/
                  #phone{
                      width: 103px; //寬
                  }
                  /*class 選擇器*/
                  .boxs{
                      zoom: 75%; //清除浮動
                      color: darkgray; //顏色
                  }
                  /*class選擇器*/
                  .box_a{
                      width: 50px; //寬
                      height: 50px; //高
                      background-image: url("../image/04.jpg "); //背景圖片
                      background-repeat: no-repeat; // 是否平鋪
                      background-size: 50px 25px; //背景尺寸
                      position: relative; //定位 相對定位
                      left: 310px; //定位后左移
                      bottom: 32px; //定位后下移
          
                  }
              </style>
          </head>
          <body>
          <div id="box">
              <h1>請注冊</h1>
          <p style="color: darkgray">已有帳號?<a href="https://im.qq.com/index">登錄</a></p>
          <form action="" method="post">
              <label for="name">用戶名</label>
              <input type="text" placeholder="請輸入用戶名" id="name" class="number"> <br>
              <label for="phone">手機號</label>
              <select name="" id="two" class="number">
              <optgroup>
                  <option style="" class="">+86</option>
              </optgroup>
              </select>
              <input type="text" placeholder="請輸入手機號" id="phone" class="number"> <br>
              <label for="mima">密?碼</label>
              <input type="password" placeholder="請輸入密碼" id="mima" class="number"> <br>
              <label for="mima">驗證碼</label>
              <input type="password" placeholder="請輸入驗證碼" id="is" class="number">
              <div class="box_a"></div>
              <div class="boxs">
                  <input type="radio" id="" class="accept">閱讀并接受協(xié)議<br>
              </div>
              <input type="submit" value="注冊" class="register" >
          
              </form>
          </div>
          
          
          </body>
          </html>
          在這里插入圖片描述


          主站蜘蛛池模板: 亚洲另类无码一区二区三区| 亚洲第一区香蕉_国产a| 国产激情无码一区二区| 国产传媒一区二区三区呀| 中文字幕一区二区人妻性色 | 中文字幕av一区| 激情综合一区二区三区| 日本在线视频一区二区三区| 亚洲日本va午夜中文字幕一区| 日本一区二区在线不卡| 波多野结衣电影区一区二区三区| 亚洲乱码一区av春药高潮| 无码午夜人妻一区二区三区不卡视频| 亚洲av区一区二区三| 日本一区高清视频| 无码一区二区三区在线| 亚洲欧美国产国产综合一区| 国产高清一区二区三区| 免费无码A片一区二三区| 一区二区三区在线观看| 国产日本亚洲一区二区三区| 亚洲一区二区三区播放在线| 精品国产AV一区二区三区| 精品女同一区二区三区免费站| 男女久久久国产一区二区三区| 久久久综合亚洲色一区二区三区| 中日韩一区二区三区| 国产在线一区二区三区| 国产精品伦一区二区三级视频| 国产精品无码AV一区二区三区| 亚洲av日韩综合一区在线观看| 性色A码一区二区三区天美传媒| 国产一区二区三区免费观看在线| 肥臀熟女一区二区三区| 日韩一区二区电影| 亚洲日韩中文字幕无码一区| 国产91精品一区| 岛国无码av不卡一区二区 | 欧洲精品免费一区二区三区| 亚洲日韩中文字幕无码一区| 日韩十八禁一区二区久久|