在 Javascript 中,對象是一組無序的相關屬性和方法的集合,所有的事物都是對象,例如字符串、數值、數組、函數等。
對象是由屬性和方法組成的:
屬性:事物的特征,在對象中用屬性來表示(常用名詞)
方法:事物的行為,在對象中用方法來表示(常用動詞)
1.2 類 class
類抽象了對象的公共部分,它泛指某一大類(class)
對象特指某一個,通過實例化一個具體的對象
面向對象的思維特點:
1、抽取(抽象)對象共用的屬性和行為組織(封裝)成一個類(模板)
2、對類進行實例化,獲取類的對象
1.3 創建類
語法:
class name { // class body }
創建實例:
var xx = new name();
注意:類必須使用 new 實例化對象
1.4 類 constructor 構造函數
constructor() 方法是類的構造函數(默認方法),用於傳遞參數,返回實例對象,通過 new 命令生成對象實例時,自動調用該方法。如果沒有顯示定義,類內部會自動創建一個 constructor()
注意:
1、通過 class 關鍵字創建類,類名習慣性定義首字母大寫
2、類里面有個 constructor 構造函數,可以接收傳遞過來的參數,同時返回實例對象
3、constructor 函數只要 new 生成實例時,就會自動調用這個函數,如果不寫這個函數,類也會自動生成這個函數
4、生成實例 new 不能省略
5、語法規范:創建類 類名后面沒有小括號,生成實例 類名后面加小括號,構造函數不需要加 function
1.5 類添加方法
語法:
class Person { constructor(name, age) { this.name = name; this.age = age } say() { console.log('hello, ' + this.name) } }
function Father(name, age) { this.name = name; this.age = age } Father.prototype.say = function() { console.log('I have lots of experience.') }
類的繼承語法:
class Father{ // 父類 } class Son extends Father { // 子類繼承父類 }
繼承中,如果實例化子類輸出一個方法,先看子類有沒有這個方法,如果有就先執行子類的,如果子類里面沒有,就去查找父類有沒有這個方法,如果有就執行父類的這個方法(就近原則)
實例:
class Father { say() { return 'this is father' } } class Son extends Father { // 這樣子類就繼承了父類的屬性和方法 say() { return 'this is son' // return super.say(); //super調用父類的方法,則輸出'this is father' } } let son = new Son(); son.say(); // 'this is son'
2.2 super 關鍵字
super 關鍵字用於訪問和調用對象父類上的函數。可以調用父類的構造函數,也可以調用父類的普通函數。
class Father { constructor(x, y){ this.x = x; this.y = y; } sum(){ console.log(this.x + this.y) } } class Son extends Father { constructor(x, y) { super(x, y); // 調用了父類中的構造函數constructor this.z = z; //定義子類獨有的屬性 } } let son = new Son(1, 2); // 實例化了子類 son.sum();
注意: 子類在構造函數中使用super, 必須放到 this 前面 (必須先調用父類的構造方法,再使用子類構造方法),否則新建實例時會報錯(this is not defined)
因為子類沒有自己的this對象,而是繼承父類的this對象。如果不調用super方法,子類就得不到this對象。
注意:
1、在 ES6 中沒有變量提升,所以必須先定義類,才能通過類實例化對象
2、類里面的共有屬性和方法一定要加 this 使用
3、類里面的 this 指向問題
4、constructor 里面的 this 指向實例對象,方法里面的 this 指向這個方法的調用者
let that; let _that; class Star { constructor(uname, age) { console.log(this); // this 指向創建的實例對象 Star{} that = this; this.uname = uname; this.age = age; } sing() { // 這個里面的 this 指向的是實例對象 ldh, 因為ldh調用了這個函數 _that = this; console.log(_that); //實例對象,ldh } } var ldh = new Star('劉德華'); console.log(that === ldh); //true ldh.sing(); console.log(_that === ldh); //true
2.3 call()
fun.call(thisArg, arg1, arg2, ...)
-
thisArg:當前調用函數 this 的指向對象
-
arg1, arg2:傳遞的其他參數
-
返回值就是函數的返回值,因為它就是調用函數
-
因此當我們想改變 this 指向,同時想調用這個函數的時候,可以使用 call,比如繼承
核心原理:通過 call() 把父類型的 this 指向子類型的 this,這樣就可以實現子類型繼承父類型的屬性。
function Father(name, age) { this.name = name; this.age = age; } function Son(name, age) { // this 指向Son構造函數的實例對象 Father.call(this, name, age) } const son = new Son('劉德華', 18); console.log(son);
核心原理:
① 將子類所共享的方法提取出來,讓子類的 prototype 原型對象 = new 父類()
② 本質:子類原型對象等於是實例化父類,因為父類實例化之后另外開辟空間,就不會影響原來父類原型對象
③ 將子類的 constructor 重新指向子類的構造函數
function Father(name, age) { this.name = name; this.age = age; } Father.prototype.money = function() { console.log('father money'); } function Son(name, age) { // this 指向Son構造函數的實例對象 Father.call(this, name, age) } // Son.prototype = Father.prototype; 這樣直接賦值,如果修改了子原型對象,父原型對象也會跟着一起變化 即 console.log(Father.prototype) 也會有 exam 方法 Son.prototype = new Father(); // 此時 son的原型對象指向Father, 即console.log(Son.prototype.constructor); 指向 Father, 所以需要利用 constructor 指回原來的構造函數 Son.prototype.constructor = Son; Son.prototype.exam = function() { console.log('son exam'); } const son = new Son('劉德華', 18); console.log(son); console.log(Father.prototype); console.log(Son.prototype.constructor);
圖解:
-
類的所有方法都定義在類的 prototype 屬性上;
-
類創建的實例,里面也有 __proto__ 指向類的 prototype 原型對象;
-
所以 ES6 的類它的絕大部分功能,ES5都可以做到,新的 class 寫法只是讓對象原型的寫法更加清晰、更像面向對象編程的語法而已;
-
所以 ES6 的類其實就是語法糖;
-
語法糖:語法糖就是一種便捷寫法。簡單理解,有兩種方法可以實現同樣的功能,但是一種寫法更加清晰、方便,那么這個方法就是語法糖。