3.類和對象 3.1面向對象 這裡順帶提一句學習JAVA時,老師說的面向對象和麵向過程的區別: 面向過程:強調做什麼事情,具體什麼步驟。舉個把大象放進冰箱的例子: 打開冰箱門 把大象放進冰箱 關上冰箱門 面向對象:強調的是做動作的主體(稱之為對象) 冰箱:打開操作 冰箱:放的操作(放的可以是大象也可 ...
3.類和對象
3.1面向對象
這裡順帶提一句學習JAVA時,老師說的面向對象和麵向過程的區別:
面向過程:強調做什麼事情,具體什麼步驟。舉個把大象放進冰箱的例子:
- 打開冰箱門
- 把大象放進冰箱
- 關上冰箱門
面向對象:強調的是做動作的主體(稱之為對象)
- 冰箱:打開操作
- 冰箱:放的操作(放的可以是大象也可以是老鼠)
- 冰箱:關閉的操作
面向對象的思維特點:
- 抽取(抽象)對象共用的屬性和行為封裝成一個類(模板)
- 對類進行實例化,創建類的對象(具體的東西)
3.2對象
現實中:對象是一個具體的事物,比如一本書、一輛車、一個人。
在js中:對象是一組無序的相關屬性和方法的集合,所有的事物都是對象,如:字元串、數組、數值、函數等。
對象是由屬性和方法組成的:
- 屬性:事物的特征,在對象中用屬性來表示
- 方法:事物的行為,在對象中用方法來表示
3.3 類
ES6中新增了類的概念,可以使用class關鍵字來聲明一個類,之後以這個類來實例化對象。
類抽象了對象的公共部分,它泛指某一大類,
對象特指**某一個,通過類實例化一個具體的對**象。
類的本質:
- class本質還是function
- 類所有的方法都定義在類的prototype屬性上
- 類創建的實例裡面也有__ proto__ 指向類的prototype原型對象
- 所以ES6的類的絕大部分功能。ES5都可以做到,新的class的寫法只是讓對象原型的寫法更加清晰、更面向對象編程的語法。
- 所以ES6的類其實就是語法糖
class Father { } console.log(Father instanceof Function); //true //(1)類有原型對象prototype console.log(Father.prototype); //(2)類的原型對象prototype 裡面有 constructor 指向類本身 console.log(Father.prototype.constructor); //(3)類可以通過原型對象添加方法 Father.prototype.speak = function () { console.log("speak"); }; //(4)類創建的實例對象有 __proto__ 指向類的原型對象 const zooey = new Father(); console.dir(zooey.__proto__ === Father.prototype); //true
3.4創建類和實例化對象
//創建類 class Star { //共有屬性放在constructor中 constructor(uname, age) { this.name = uname; this.age = age; } } // 利用類實例化對象 const ldh = new Star("劉德華", 19); const syz = new Star("孫燕姿", 18); //列印查看 console.log(ldh); console.log(syz);
- 註意:
- 通過class關鍵字創建類,類名首字母大寫。
- 類裡面有一個constructor函數,可以接收傳遞過來的函數,同時返回實例對象。
- 只要使用new生成實例時,就會調用constructor函數,如果我們不寫這個函數, 類也會自動生成這個函數。
3.5類中添加方法
//創建類 class Star { //共有屬性放在constructor中 constructor(uname, age) { this.uname = uname; this.age = age; } sing(song) { //方法中可以this.使用實例屬性 console.log(this.uname + "會唱" + song); } } // 利用類實例化對象 const ldh = new Star("劉德華", 19); const syz = new Star("孫燕姿", 18); //列印查看 console.log(ldh); console.log(syz); ldh.sing("忘情水"); syz.sing("遇見");
註意:
- 類裡面的所有函數(方法)不需要寫function
- 多個函數方法(包括構造函數)之間不需要用 ',' 分割
4 類的繼承
現實中的繼承:子承父業,比如我們都繼承了父親的姓。
程式中的繼承:子類可以繼承父類的一些屬性和方法。
4.1使用extends關鍵字實現繼承
//創建父類類 class Father { constructor(name, sex) { this.name = name; this.sex = sex; } money() { console.log(1000); } } // 創建子類並繼承父類 class children extends Father { } const oldestSon = new children("伯邑考", "男"); const middleSon = new children("姬發", "男"); son.money();
4.2super關鍵字
一個錯誤使用案例:
class Father { constructor(x, y) { this.x = x; this.y = y; } total() { return this.x + this.y; } } class Son extends Father { constructor(x, y) { this.x = x; this.y = y; } } const son = new Son(1, 1); console.log("結果:" + son.total());
此時希望調用父類中的構造函數則需要使用到super
class Father { constructor(x, y) { this.x = x; this.y = y; } total() { return this.x + this.y; } } class Son extends Father { constructor(x, y) { super(x, y); } } const son = new Son(1, 1); console.log("結果:" + son.total()); //結果:2
思考:看到這裡不知道你會不會和我有一樣的困惑,此時沒有super也可以實現 total方法的調用,所以 super存在真正的意義是什麼呢
思考案例1:
class Father { constructor(x, y) { this.x = x; this.y = y; } total() { return this.x + this.y; } } class Son extends Father { } const son = new Son(1, 1); console.log("結果:" + son.total());
那麼就讓我們停下來思考一下:
當我們給Son構造函數傳遞多個參數的時候,這時,輸出的結果就不是我們想要的結果了,此時就必須使用super才能傳遞正確的參數了。
思考案例2:
class Father { constructor(x, y) { this.x = x; this.y = y; } total() { return this.x + this.y; } } class Son extends Father { } const son = new Son(1, "女", "zooey", 1); console.log("結果:" + son.total());//結果:1女
真正意義上:
利用Son類創建實例son時,我們將實例son的參數傳遞給Son類的構造函數,
構造函數通過super()調用Father 的構造函數從而給this.x,this.y賦值,
從而使Father類中的total的方法能夠找到 this.x,this.y,
從而實現了思考案例1的結果。
沒有super時:
super關鍵字用於訪問和調用對象父類上的函數,可以調用父類的構造函數,也可以調用父類的普通函數。
super調用父類的方法:
class Father { constructor(x, y) { this.x = x; this.y = y; } total() { return this.x + this.y; } } class Son extends Father { constructor(x, y, z) { super(x, y); this.z = z; } total() { //子類方法中調用父類方法 return this.z + super.total(); } } const son = new Son(1, 1, 1); console.log("結果:" + son.total());//3
註意:子類的this賦值操作要放在super之後
註意事項:
- 在 ES6 中類沒有變數提升,必須先定義類,再實例化對象
- 類裡面共有是屬性和方法一定要加this使用。
class Star { constructor(name, age) { this.name = name; this.age = age; this.btn = document.querySelector("button"); this.btn.onclick = this.sing; } sing() { console.log(this.name); } } const ldh = new Star("劉德華", 18);
4.3類中this指向
函數中的this一般指向調用者
下麵的代碼debug執行下,可以看到,sing()方法執行時,this是指向button的。
那麼正確的做法是什麼呢?
let _this; class Star { constructor(name, age) { _this = this; this.name = name; this.age = age; this.btn = document.querySelector("button"); //註意:因為這裡是點擊之後再調用,不是立即執行,所以是sing不是sing() this.btn.onclick = this.sing; } sing() { //使用 _this console.log(_this.name); } } const ldh = new Star("劉德華", 18);
總結:在全局聲明一個this,然後調用後將它賦值給調用對象。
舉一反三:在react中,常用的另一個方法是使用bind綁定this。
class Star { constructor(name, age) { this.name = name; this.age = age; this.btn = document.querySelector("button"); this.btn.onclick = this.sing.bind(this); } sing() { console.log(this.name); } } const ldh = new Star("劉德華", 18);