整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          JavaScript:class繼承

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

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

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

          我們先回顧用函數實現Student的方法:

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

          如果用新的class關鍵字來編寫Student,可以這樣寫:

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

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

          最后,創建一個Student對象代碼和前面章節完全一樣:

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

          class繼承

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

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

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

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

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

          你一定會問,class這么好用,能不能現在就用上?

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

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



          、構造函數

          首先,構造函數其實就是JavaScript程序中定義好的函數,我們可以直接使用。構造函數實際也是一種函數,它是專門用于生成定義對象,通過構造函數生成的對象,稱為實例化對象。

          構造函數分為兩種,一種是JavaScript程序定義好的構造函數,我們成為稱為內置構造函數,另外一種是程序員自己定義的構造函數,稱為自定義構造函數。

          構造函數雖然也是一種函數,但是和普通函數是區別的: 

          1、構造函數一定要和關鍵詞new一起使用,new關鍵詞會自動的給構造函數定義一個對象,并且返回這個對象,我們只要對這個對象設定屬性,設定方法就可以使用。  

          2、構造函數為了和其他函數區別,語法規范規定構造函數的函數名稱,第一個字母必須大寫,使用大駝峰命名法。  

          3、構造函數給對象定義屬性和方法的語法,與一般函數不同

          //自定義構造函數
          function Person(name,sex,age,addr){
            // 定義屬性
            this.name = name;
            this.sex = sex;
            this.age = age;
            this.addr = addr;
            // 定義方法
            this.fun = function(){
              console.log(this.name,this.sex,this.age,this.addr);
            }
          }
          // 生成實例化對象
          const person = new Person('終結者','男',28,'杭州');
          console.log(person); //輸出實例化對象


          普通構造函數實現繼承

          //普通構造函數繼承
          //動物
          function Animal() {
              this.eat = function () {
                  console.log('animal eat')
              }
          }
           
          //狗
          function Dog() {
              this.bark = function () {
                  console.log('dog bark')
              }
          }
          //綁定原型,實現繼承
          Dog.prototype = new Animal()
          //哈士奇
          var hashiqi = new Dog()



          二、class

          ES6構造函數語法:ES6與ES5構造函數語法對比,其功能作用完全相同,只是語法不同。ES6提供了更接近傳統語言的寫法,引入了Class(類)這個概念,作為對象的模板。通過class關鍵字,可以定義類。

          class Perosn{// ES6class方法定義構造函數
            //constructor是構造器,定義實例化對象的屬性和屬性值, ()中的是參數
            constructor (name,age,sex){
              this.name = name;
              this.age = age; 
            	this.sex = sex;
          	}
            //定義方法,雖然沒聲明,但是也是定義在構造函數中的prototype中
            printAll(){
            	console.log(this.name,this.age,this.sex);
          	}
          }
          // 生成實例化對象
          const test = new Perosn('終結者','男',28,'杭州');
          console.log(person);//輸出實例化對象
          person.printAll();  //輸出方法

          上面代碼定義了一個“類”,可以看到里面有一個constructor方法,這就是構造方法,而this關鍵字則代表實例對象。也就是說,ES5的構造函數Person,對應ES6的Person類的構造方法。

          定義“類”的方法的時候,前面不需要加上function這個關鍵字,直接把函數定義放進去了就可以了。另外,方法之間不需要逗號分隔,加了會報錯。


          Class實現繼承

          class Animal{
              constructor(name){
                  this.name=name
              }
              eat(){
                  console.log(this.name +'eat');
              }
          }
          class Dog extends Animal{
              constructor(name){
                  super(name)//訪問和調用Dog對象的父對象Animal上的函數。
                  this.name=name
                // 在派生類中, 必須先調用 super() 才能使用 "this",忽略這個,將會導致一個引用錯誤。
              }
              bark(){
                  console.log(this.name+'bark');
              }
          }
          const dog=new Dog("泰迪");
          dog.eat();
          dog.bark();

          構造函數的prototype屬性,在ES6的“類”上面繼續存在。事實上,類的所有方法都定義在類的prototype屬性上面。

          ES6中的class語法就是ES5中構造函數的另一種寫法,一種更高級的寫法,

          class語法的底層還是ES5中的構造函數,只是把構造函數進行了一次封裝而已。

          ES6出現的目的為了讓我們的讓對象原型的寫法更加清晰、在語法上更加貼合面向對象的寫法、更像面向對象編程讓JavaScript更加的符合通用編程規范,即大部分語言對于類和實例的寫法,class實現繼承更加容易理解,更易于后端語言使用。



          三、構造函數與Class的區別

          • Class 類中不存在變量提升

          類不存在變量提升(hoist),這一點與 ES5 完全不同。

           // ES5
          var bar  = new Bar(); // 可行
          function Bar() {
            this.bar = 42;
          }
              
          //ES6
          const foo = new Foo(); // Uncaught ReferenceError
          class Foo {
            constructor() {
              this.foo = 42;
            }
          }
          
          • class內部會啟用嚴格模式

          類和模塊的內部,默認就是嚴格模式,所以不需要使用use strict指定運行模式。只要你的代碼寫在類或模塊之中,就只有嚴格模式可用。考慮到未來所有的代碼,其實都是運行在模塊之中,所以 ES6 實際上把整個語言升級到了嚴格模式。

          // ES5
          function Bar() {
            // 引用一個未聲明的變量
            baz = 42; // it's ok
          }
          var bar  = new Bar(); 
          
          // ES6
          class Foo {
            constructor(){
              // 引入一個未聲明的變量
              fol = 42;
              // Uncaught ReferenceError: fol is not defined
            }
          }
          let foo = new Foo();
          • class的所有方法都是不可枚舉的

          ES6 中的 class,它的方法(包括靜態方法和實例方法)默認是不可枚舉的,而構造函數默認是可枚舉的。細想一下,這其實是個優化,讓你在遍歷時候,不需要再判斷 hasOwnProperty 了

          // ES5
          function Bar() {}   
          Bar.answer = function () {};
          Bar.prototype.print = function () {};
          console.log(Object.keys(Bar));// ["answer"]
          console.log(Object.keys(Bar.prototype))// ["print"]
          // ES6
          class Foo {
          	constructor(){}
            static answer() {}
            print(){}
          }
          console.log(Object.keys(Foo))// []
          console.log(Object.keys(Foo.prototype));// []
          • class 必須使用new調用

          class 必須使用new調用,否則會報錯。這是它跟普通構造函數的一個主要區別,后者不用new也可以執行。

          // ES5
          function Bar(){ }
          var bar = Bar();// it's ok;
          // ES6
          class Foo {
          }
          let foo = Foo();
          // Uncaught TypeError: Class constructor Foo cannot be invoked without 'new'
          
          
          • class 內部無法重寫類名
          // ES5 
          function Bar() {
          	Bar = 'Baz';
          	this.bar = 42;
          }
          var bar = new Bar();
          console.log(bar);// Bar {bar: 42}
          console.log(Bar);// 'Baz'
          
          // ES6
          class Foo {
          	constructor(){
          		this.foo = 42;
          		Foo = 'Fol'; // Uncaught TypeError: Assignment to constant variable.
          	}
          }
          let foo = new Foo();
          Foo = 'Fol';// it's ok
          • class 的繼承有兩條繼承鏈

          一條是:子類的__proto__指向父類

          另一條:子類prototype屬性的__proto__屬性指向父類的prototype屬性.

          ES6的子類可以通過__proto__屬性找到父類,而ES5的子類通過__proto__找到Function.prototype

          // ES5
          function Super() {}
          function Sub() {}
          Sub.prototype = new  Super();
          Sub.prototype.constructor = Sub;
          var sub = new Sub();
          console.log( Sub.__proto__ === Function.prototype);// true
          
          // ES6
          class Super{}
          class Sub extends Super {}
          let sub = new Sub();
          console.log( Sub.__proto__ === Super);// true
          
          • ES5與ES6子類this的生成順序不同。

          ES5的繼承是先建立子類實例對象this,再調用父類的構造函數修飾子類實例(Surper.apply(this))。

          ES6的繼承是先建立父類實例對象this,再調用子類的構造函數修飾this。即在子類的constructor方法中必須使用super(),之后才能使用this.

          • 正是因為this的生成順序不同,所有ES5不能繼承原生的構造函數,而ES6可以繼承
          // ES5
          function MyES5Array() {
          	Array.apply(this, arguments);
            // 原生構造函數會忽略apply方法傳入的this,
            //即this無法綁定,先生成的子類實例,拿不到原生構造函數的內部屬性。
          }
          MyES5Array.prototype = Object.create(Array.prototype, {
          	constructor: {
              value: MyES5Array,
              writable: true,
              configurable: true,
              enumerable: true
             }
          })
          var arrayES5 = new MyES5Array();
          arrayES5[0] = 3;
          console.log(arrayES5.length);// 0 
          arrayES5.length = 0;
          console.log(arrayES5[0]);// 3
          
          // ES6
          class arrayES6 extends Array {
          	constructor(...args){
          		super(...args);
          	}
          }
          let arrayes6 = new arrayES6();
          arrayes6[0] = 3;
          console.log(arrayes6.length);// 1
          arrayes6.length = 0;
          console.log(arrayes6[0]);// undefined
          • ES6可以繼承靜態方法,而構造函數不能
          • class 的所有方法(包括靜態方法和實例方法)都沒有原型對象 prototype,所以也沒有[[construct]],不能使用new來調用。

          需要注意一點:

          ES6的class語法無法執行預解析,是不能被提前調用;

          ES5的function函數可以提前調用,但是只能調用屬性不能調用方法。


          天在寫頁面的時候,發現class="1212-sale"開頭的樣式無法調用,后來大群里有同學說class樣式不能以數字的開頭。試了一下果然是的!為了避免犯同樣的錯誤,我上網查了一下css命名規范。整理了一下:

          一、命名規則說明:1、所有的命名最好都小寫2、屬性的值一定要用雙引號("")括起來,且一定要有值如class="divcss5",id="divcss5"3、每個標簽都要有開始和結束,且要有正確的層次,排版有規律工整4、空元素要有結束的tag或于開始的tag后加上"/"5、表現與結構完全分離,代碼中不涉及任何的表現元素,如style、font、bgColor、border等6、<h1到<h5>的定義,應遵循從大到小的原則,體現文檔的結構,并有利于搜索引擎的查詢7、給每一個表格和表單加上一個唯一的、結構標記id8、給圖片加上alt標簽9、盡量使用英文命名原則10、盡量不縮寫,除非一看就明白的單詞11、類名的第一個字符不能使用數字!它無法在 Mozilla 或 Firefox 中起作用

          二、相對網頁外層重要部分CSS樣式命名:外套 wrap ------------------用于最外層頭部 header ----------------用于頭部主要內容 main ------------用于主體內容(中部)左側 main-left -------------左側布局右側 main-right -----------右側布局導航條 nav -----------------網頁菜單導航條內容 content ---------------用于網頁中部主體底部 footer -----------------用于底部

          CSS命名其它說明:DIV+CSS命名小結:無論是使用“.”(小寫句號)選擇符號開頭命名,還是使用“#”(井號)選擇符號開頭命名都無所謂,但我們最好遵循,主要的重要的特殊的最外層的盒子用“#”(井號)選擇符號開頭命名,其它都用“.”(小寫句號)選擇符號開頭命名,同時考慮命名的CSS選擇器在HTML中重復使用調用。通常我們最常用主要命名有:wrap(外套、最外層)、header(頁眉、頭部)、nav(導航條)、menu(菜單)、title(欄目標題、一般配合h1\h2\h3\h4標簽使用)、content (內容區)、footer(頁腳、底部)、logo(標志、可以配合h1標簽使用)、banner(廣告條,一般在頂部)、copyRight(版權)。其它可根據自己需要選擇性使用。

          讓我們來看下這個類名定義:  .right-red { color:red; }  你可能很明確的知道這個class選擇符的所起的作用。但是這里還有個問題,當你在一星期的時間需要重新設計。在重新設計的時候,這個模塊被放置到了左邊,而且還是綠色。這個類就不再有存在的價值。所以現在不得不選擇,要么改變所有的屬性值,要么放著它不動,這可能導致混亂。  最好不要在你的類名或者ID名中去加入顏色或者長寬的尺寸等帶有屬性的名字。你應該避免任何的屬性值都是直接的詞匯。(如box)直接屬性可能會導致內容的分離。讓我們來看看最合理ID/CLASS的命名規范:  .product-description { color: red; }  用這種樣式定義的product-description(產品描述),不管你怎么改變,她都是那么的干凈清晰。


          主站蜘蛛池模板: 国产经典一区二区三区蜜芽 | 久久青草精品一区二区三区| 国产成人亚洲综合一区| 伊人久久精品无码麻豆一区| 国产在线一区二区杨幂| 久久精品国产一区二区三区| 亚洲av无码不卡一区二区三区| 国产福利91精品一区二区| 日韩精品一区在线| 成人区精品人妻一区二区不卡| 久久精品道一区二区三区| 亚洲AV无码一区二区三区久久精品 | 亚洲国产精品一区| 亚洲AV无码一区二区三区性色 | 久久亚洲一区二区| 国产精品一区二区三区久久| 在线精品亚洲一区二区三区| 伊人色综合一区二区三区影院视频| 免费观看日本污污ww网站一区| 久久se精品一区精品二区| 狠狠综合久久av一区二区| 国产成人一区二区三区视频免费| 国产小仙女视频一区二区三区 | 无码囯产精品一区二区免费 | 国产MD视频一区二区三区| 精品一区二区久久久久久久网站| 亚洲日韩AV一区二区三区四区 | 国产福利一区二区三区在线观看| 国产成人精品一区在线| 中文字幕一区一区三区| 一区二区三区福利| 欧美日韩一区二区成人午夜电影| 丰满岳乱妇一区二区三区| 国产精品一区在线播放| 中文字幕色AV一区二区三区| 精品国产一区二区三区免费看| 麻豆天美国产一区在线播放| 中文字幕在线观看一区| 欧洲精品一区二区三区| 91精品乱码一区二区三区| 日韩免费观看一区|