整合營(yíng)銷服務(wù)商

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

          免費(fèi)咨詢熱線:

          前端-JavaScript中的class

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

          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語(yǔ)言的寫(xiě)法,引入了 Class(類)這個(gè)概念,作為對(duì)象的模板。通過(guò)class關(guān)鍵字,可以定義類。

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

          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,所以類只是提供了語(yǔ)法糖,本質(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)

          類的另一種定義方式類表達(dá)式

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

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

          constructor()

          constructor()方法是類的默認(rèn)方法,new生成實(shí)例對(duì)象時(shí)會(huì)自動(dòng)調(diào)用該方法。

          一個(gè)類必須有constructor()方法,如果沒(méi)有顯式定義,引擎會(huì)默認(rèn)添加一個(gè)空的constructor()

          constructor()方法默認(rèn)返回實(shí)例對(duì)象(即this)。

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

          getter和setter

          與 ES5 一樣,在類的內(nèi)部可以使用getset關(guān)鍵字,對(duì)某個(gè)屬性設(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,它默認(rèn)指向類的實(shí)例,在調(diào)用存在this的方法時(shí),需要使用 obj.method()方式,否則會(huì)報(bào)錯(cuò)。

          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()     // 報(bào)錯(cuò) Cannot read properties of undefined (reading 'name')

          如果要單獨(dú)調(diào)用又不報(bào)錯(cuò),一種方法可以在構(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) 會(huì)創(chuàng)建一個(gè)新函數(shù),并將傳入的this作為該函數(shù)在調(diào)用時(shí)上下文指向。

          另外可以使用箭頭函數(shù),因?yàn)榧^函數(shù)內(nèi)部的this總是指向定義時(shí)所在的對(duì)象。

          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)屬性指的是類本身的屬性,而不是定義在實(shí)例對(duì)象this上的屬性。

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

          靜態(tài)方法

          可以在類里面定義靜態(tài)方法,該方法不會(huì)被對(duì)象實(shí)例繼承,而是直接通過(guò)類來(lá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暫時(shí)沒(méi)有提供,一般是通過(guò)約定,比如:在方法前面加下劃線_print()表示私有方法。

          繼承

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

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

          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)方法,也會(huì)被子類繼承。

          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();
            }
          }

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

          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時(shí),_proto_prototype 很容易混淆。首先我們知道每個(gè)JS對(duì)象都會(huì)對(duì)應(yīng)一個(gè)原型對(duì)象,并從原型對(duì)象繼承屬性和方法。

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

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

          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 

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

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

          繼承中的__proto__

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

          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

          繼承實(shí)例中的__proto__

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

          子類實(shí)例的__proto__屬性的__proto__屬性,指向父類實(shí)例的__proto__屬性。也就是說(shuō),子類的原型的原型,是父類的原型。

          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更多的還是語(yǔ)法糖,本質(zhì)上繞不開(kāi)原型鏈。歡迎大家留言交流。

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

          有沒(méi)有更簡(jiǎn)單的寫(xiě)法?有!

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

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

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

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

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

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

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

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

          class繼承

          用class定義對(duì)象的另一個(gè)巨大的好處是繼承更方便了。想一想我們從Student派生一個(gè)PrimaryStudent需要編寫(xiě)的代碼量?,F(xiàn)在,原型繼承的中間對(duì)象,原型對(duì)象的構(gòu)造函數(shù)等等都不需要考慮了,直接通過(guò)extends來(lái)實(shí)現(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)鍵字實(shí)現(xiàn)的,而extends則表示原型鏈對(duì)象來(lái)自Student。子類的構(gòu)造函數(shù)可能會(huì)與父類不太相同,例如,PrimaryStudent需要name和grade兩個(gè)參數(shù),并且需要通過(guò)super(name)來(lái)調(diào)用父類的構(gòu)造函數(shù),否則父類的name屬性無(wú)法正常初始化。

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

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

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

          現(xiàn)在用還早了點(diǎn),因?yàn)椴皇撬械闹髁鳛g覽器都支持ES6的class。如果一定要現(xiàn)在就用上,就需要一個(gè)工具把class代碼轉(zhuǎn)換為傳統(tǒng)的prototype代碼,可以試試Babel這個(gè)工具。

          需要瀏覽器支持ES6的class,如果遇到SyntaxError,則說(shuō)明瀏覽器不支持class語(yǔ)法,請(qǐng)換一個(gè)最新的瀏覽器試試。

          面向?qū)ο缶幊讨校?/span>類(class)是對(duì)象(object)的模板,定義了同一組對(duì)象(又稱"實(shí)例")共有的屬性和方法。Javascript語(yǔ)言不支持"類",但是可以用一些變通的方法,模擬出"類"。

          一、構(gòu)造函數(shù)法

          這是經(jīng)典方法,也是教科書(shū)必教的方法。它用構(gòu)造函數(shù)模擬"類",在其內(nèi)部用this關(guān)鍵字指代實(shí)例對(duì)象。

            function Cat() {

              this.name = "張三";

            }

          生成實(shí)例的時(shí)候,使用new關(guān)鍵字。

            var cat1 = new Cat();

            alert(cat1.name); // 張三

          類的屬性和方法,還可以定義在構(gòu)造函數(shù)的prototype對(duì)象之上。

            Cat.prototype.makeSound = function(){

              alert("你好我是類");

            }

          二、Object.create()法

          為了解決"構(gòu)造函數(shù)法"的缺點(diǎn),更方便地生成對(duì)象,Javascript的國(guó)際標(biāo)準(zhǔn)ECMAScript第五版(目前通行的是第三版),提出了一個(gè)新的方法Object.create()。

          用這個(gè)方法,"類"就是一個(gè)對(duì)象,不是函數(shù)。

            var Cat = {

              name: "李四",

              makeSound: function(){ alert("早上好?。?!"); }

            };

          然后,直接用Object.create()生成實(shí)例,不需要用到new。

            var cat1 = Object.create(Cat);

            alert(cat1.name); // 張三

            cat1.makeSound(); // 早上好?。。?/span>

          主流瀏覽器比較適配此方法。若遇到舊版本瀏覽器(如:IE6),可以用下面的代碼自行部署。

            if (!Object.create) {

              Object.create = function (o) {

                 function F() {}

                F.prototype = o;

                return new F();

              };

            }

          這種方法比"構(gòu)造函數(shù)法"簡(jiǎn)單,但是不能實(shí)現(xiàn)私有屬性和私有方法,實(shí)例對(duì)象之間也不能共享數(shù)據(jù),對(duì)"類"的模擬不夠全面。

          三、極簡(jiǎn)主義法

          3.1 封裝

          這種方法不使用this和prototype,代碼部署起來(lái)非常簡(jiǎn)單,這大概也是它被叫做"極簡(jiǎn)主義法"的原因。

          首先,它也是用一個(gè)對(duì)象模擬"類"。在這個(gè)類里面,定義一個(gè)構(gòu)造函數(shù)createNew(),用來(lái)生成實(shí)例。

            var Cat = {

              createNew: function(){

                // some code here

              }

            };

          然后,在createNew()里面,定義一個(gè)實(shí)例對(duì)象,把這個(gè)實(shí)例對(duì)象作為返回值。

            var Cat = {

              createNew: function(){

                var cat = {};

                cat.name = "張三";

                cat.makeSound = function(){ alert("沙沙沙"); };

                return cat;

              }

            };

          使用的時(shí)候,調(diào)用createNew()方法,就可以得到實(shí)例對(duì)象。

            var cat1 = Cat.createNew();

            cat1.makeSound(); // 沙沙沙

          這種方法的好處是,容易理解,結(jié)構(gòu)清晰優(yōu)雅,符合傳統(tǒng)的"面向?qū)ο缶幊?#34;的構(gòu)造,因此可以方便地部署下面的特性。

          3.2 繼承

          讓一個(gè)類繼承另一個(gè)類,實(shí)現(xiàn)起來(lái)很方便。只要在前者的createNew()方法中,調(diào)用后者的createNew()方法即可。

          先定義一個(gè)Animal類。

            var Animal = {

              createNew: function(){

                var animal = {};

                animal.sleep = function(){ alert("沙沙沙"); };

                return animal;

              }

            };

          然后,在Cat的createNew()方法中,調(diào)用Animal的createNew()方法。

            var Cat = {

              createNew: function(){

          var cat = Animal.createNew();

                cat.name = "張三";

                cat.makeSound = function(){ alert("沙沙沙"); };

                return cat;

              }

            };

          這樣得到的Cat實(shí)例,就會(huì)同時(shí)繼承Cat類和Animal類。

            var cat1 = Cat.createNew();

            cat1.sleep(); // 沙沙沙

          3.3 私有屬性和私有方法

          在createNew()方法中,只要不是定義在cat對(duì)象上的方法和屬性,都是私有的。

            var Cat = {

              createNew: function(){

                var cat = {};

          var sound = "沙沙沙";

          cat.makeSound = function(){ alert(sound); };

                return cat;

              }

            };

          上例的內(nèi)部變量sound,外部無(wú)法讀取,只有通過(guò)cat的公有方法makeSound()來(lái)讀取。

            var cat1 = Cat.createNew();

          alert(cat1.sound); // undefined

          3.4 數(shù)據(jù)共享

          有時(shí)候,我們需要所有實(shí)例對(duì)象,能夠讀寫(xiě)同一項(xiàng)內(nèi)部數(shù)據(jù)。這個(gè)時(shí)候,只要把這個(gè)內(nèi)部數(shù)據(jù),封裝在類對(duì)象的里面、createNew()方法的外面即可。

            var Cat = {

          sound : "沙沙沙",

              createNew: function(){

                var cat = {};

          cat.makeSound = function(){ alert(Cat.sound); };

          cat.changeSound = function(x){ Cat.sound = x; };

                return cat;

              }

            };

          然后,生成兩個(gè)實(shí)例對(duì)象:

            var cat1 = Cat.createNew();

            var cat2 = Cat.createNew();

            cat1.makeSound(); // 沙沙沙

          這時(shí),如果有一個(gè)實(shí)例對(duì)象,修改了共享的數(shù)據(jù),另一個(gè)實(shí)例對(duì)象也會(huì)受到影響。

          cat2.changeSound("啾啾啾");

            cat1.makeSound(); //啾啾啾


          主站蜘蛛池模板: 精品一区二区三区高清免费观看 | 一区二区三区电影在线观看| 99精品一区二区三区| 免费一区二区三区| 蜜桃无码一区二区三区| 麻豆亚洲av熟女国产一区二| 文中字幕一区二区三区视频播放 | 一区二区三区精品视频| 精品无码人妻一区二区三区不卡| 中文字幕日本一区| 国99精品无码一区二区三区| 日韩好片一区二区在线看| 日韩国产免费一区二区三区| 国产视频一区二区| 狠狠色成人一区二区三区| 竹菊影视欧美日韩一区二区三区四区五区 | 国偷自产一区二区免费视频| 一区二区三区视频在线观看| 一区二区三区在线观看| 日韩美女在线观看一区| 美女啪啪一区二区三区| 在线电影一区二区| 大屁股熟女一区二区三区| 精品国产一区AV天美传媒| 日本韩国黄色一区二区三区| 国产AV一区二区三区无码野战 | 无码少妇丰满熟妇一区二区| 波多野结衣一区二区三区高清av | 亚洲日本久久一区二区va| 日韩av片无码一区二区三区不卡| 日韩AV无码久久一区二区| 精品一区二区三区视频| 亚洲视频一区在线观看| 中文无码AV一区二区三区| 欧洲精品一区二区三区| 日韩精品区一区二区三VR| 亚洲中文字幕无码一区二区三区| 国产伦精品一区二区三区不卡| 少妇无码一区二区三区| 国模吧无码一区二区三区| 一区二区三区AV高清免费波多|