傳統的javascript中只有對象,沒有類的概念。它是基於原型的面嚮對象語言。原型對象特色就是將自身的屬性共享給新對象。這樣的寫法相對於其餘傳統面嚮對象語言來說,頗有一種獨樹一幟的感受,很是容易讓人困惑!
若是要生成一個對象實例,須要先定義一個構造函數,而後經過new操做符來完成。構造函數示例:javascript
//函數名和實例化構造名相同且大寫(非強制,但這麼寫有助於區分構造函數和普通函數) function Person(name,age){ this.name = name this.age = age } Person.prototype.say = function(){ return "個人名字叫"+this.name+"今年"+this.age+"歲了" } var obj = new Person("hebei",23) //經過構造函數建立對象,必須使用new運算符 console.log(obj.say())//個人名字叫hebei今年23歲了
構造函數生成實例的執行過程:java
一、當使用了構造函數,而且new 構造函數(),後臺會隱式執行new Object()建立對象 二、將構造函數的做用域給新對象,即 new Object()建立出來的對象,而函數體內的this就表明new Object()出來的對象 三、執行構造函數的代碼 四、返回新對象(後臺直接返回)
es6引入了Class(類)這個概念,經過class關鍵字能夠定義類。該關鍵字的出現使得其在對象寫法上更加清晰,更像是一種面向對象的語音。若是將以前的代碼該寫文es6的寫法就會是這樣子:es6
class Person{ //定義了一個名字爲Person的類 constructor(name,age){ //constructor是一個構造方法,用來接收參數 this.name = name this.age = age } say(){// 這是一個類的方法,注意千萬不要加上function return "個人名字叫"+this.name+"今年"+this.age+"歲了" } } var obj = new Person("hebei",23) console.log(obj.say())//個人名字叫hebei今年23歲了
注意項:
一、在類中聲明方法的時候,千萬不要給該方法加上function關鍵字
二、方法之間不要用逗號分隔,不然會報錯編程
由下面代碼能夠看出類實質上就是一個函數。類自身指向的就是構造函數。因此能夠認爲es6中的類其實就是構造函數的另一種寫法!app
console.log(typeof Person)//function console.log(Person === Person.prototype.constructor)//true
以上代碼說明構造函數的prototype屬性,在es6的類中依然存在着
實際上類的全部方法都定義在類的prototype屬性上。代碼證實下:函數
Person.prototype.say=function(){//定義與類中相同名字的方法,成功實現了覆蓋! return "我是來證實的,你叫"+this.name+"今年"+this.age+"歲了" } var obj = new Person("hebei",23) console.log(obj.say())//我是來證實的,你叫hebei今年23歲了
固然也能夠經過prototype屬性對類添加方法。以下:工具
Person.prototype.addFn = function (){ return "我是經過prototype新增長的方法,名字叫作addFn" } var obj = new Person("hebei",23) console.log(obj.addFn())//我是經過prototype新增長的方法,名字叫作hebei
還能夠經過Object.assign方法來爲對象動態增長方法ui
Object.assign(Person.prototype,{ getName:function(){ return this.name }, getAge:function(){ return this.age } }) var obj = new Person("hebei",23) console.log(obj.getName())//hebei console.log(obj.getAge())//23
constructor方法是類的構造函數的默認方法,經過new命令生成對象實例時,自動調用該方法this
class Box{ constructor(){ console.log("哈哈哈")//當實例化對象時該行代碼會執行 } } var obj = new Box()
constructor方法若是沒有顯式定義,會隱式生成一個constructor方法。因此即便你沒有添加構造函數,構造函數也是存在的。constructor方法默認返回實例對象this,可是也能夠指定constructor方法返回一個全新的對象,讓返回的實例對象不是該類的實例。es5
class Desk{ constructor(){ this.xixi = "小鳥" } } class Box{ constructor(){ return new Desk()//這裏沒有用this,直接返回一個全新的對象 } } var obj = new Box() console.log(obj.xixi)//小鳥
constructor中定義的屬性能夠稱爲實例屬性(即定義在this對象上),constructor外聲明的屬性都是定義在原型上的,能夠稱之爲原型屬性(即定義在class上)
hasOwnProperty()函數用於判斷屬性是不是實例屬性。其結果是一個布爾值,true說明是實例屬性,false說明不是實例屬性。in操做符會在經過對象可以訪問給定屬性時返回true,不管該屬性存在於實例中仍是原型中。
class Box{ constructor(num1,num2){ this.num1 = num1 this.num2 = num2 } sum(){ return this.num1+this.num2 } } var box = new Box(12,88) console.log(box.hasOwnProperty("num1"))//true console.log(box.hasOwnProperty("num2"))//true console.log(box.hasOwnProperty("sum"))//false console.log("num1" in box)//true console.log("num2" in box)//true console.log("sum" in box)//true console.log("say" in box)//false
類的全部實例共享一個原型對象,它們的原型都是Personn.prototype,因此proto屬性是相等的
class Box{ constructor(num1,num2){ this.num1 = num1 this.num2 = num2 } sum(){ return this.num1+this.num2 } } box1與box2都是Box的實例。它們的__proto__都指向Box的prototype var box1 = new Box(12,88) var box2 = new Box(40,60) console.log(box1.__proto__===box2.__proto__)//true
由此,也能夠經過proto來爲類增長方法。使用實例的proto屬性改寫原型,會改變Class的原始定義,影響到全部實例,因此不推薦使用!
class Box{ constructor(num1,num2){ this.num1 = num1 this.num2 = num2 } sum(){ return this.num1+this.num2 } } var box1 = new Box(12,88) var box2 = new Box(40,60) box1.__proto__.sub=function(){ return this.num2-this.num1 } console.log(box1.sub())//76 console.log(box2.sub())//20
class不存在變量提高,因此須要先定義再使用。由於es6不會把類的聲明提高到代碼頭部,可是es5就不同,es5存在變量提高,能夠先使用,而後再定義
//es5能夠先使用再定義,存在變量提高 new A() function A(){} //es6不能先使用再定義,不存在變量提高,會報錯 new B()//B is not defined class B{}
Es6類class的關鍵super、static、constructor、new.target
es6引入了Class類這個概念,做爲對象的模版,經過class關鍵字,能夠定義類。基本上,es6的class能夠看做只是一個語法糖,它的絕大部分功能,es5均可以作到,新的class寫法只是讓對象原型的寫法更加清晰、更加面向對象編程而已。
一、super關鍵字
super用在調用的時候有兩種狀況:
第一種:super做爲函數調用時,表明父類的構造函數
第二種:super做爲對象時,在普通方法中,指向父類的原型對象;在靜態方法中,指向父類。
class Person{ constructor(name){ this.name = name } height(){ console.log(1) } static weight(){ console.log(2) } } class Student extends Person{ constructor(name,age){ super()//表明父類的構造函數 this.age = age } height(){ super.height()//指向父類的原型對象 } static weight(){ super.weight()//指向父類 } }
若是子類調用constructor,那麼子類必須在coonstructor方法中調用super方法,不然新建實例時會報錯。這是由於子類沒有本身的this對象,而是繼承父類的this對象,而後對其進行加工。若是不調用super方法,子類就得不到this對象
總結下:
super關鍵字用於調用一個對象的父對象上的函數
super.prop和super[expr]表達式在類和對象字面量任何方法定義中都是有效的。
在構造函數中使用時,super關鍵字單獨出現,必須在可使用this關鍵字以前使用。此關鍵字也可用於調用父對象上的函數
二、static關鍵字
類至關於實例的原型,全部在類中定義的方法,都會被實例繼承。若是在一個方法前,加上static關鍵字,就表示該方法不會被實例繼承,而是直接經過類來調用,這就稱爲靜態方法
靜態方法調用直接在類上進行,而在類的實例上不可被調用
靜態方法一般用於建立 實用/工具 函數
經過例子咱們能夠發現,靜態方法是經過類名直接調用的
從另外一個靜態方法爲了在同一個類的另外一個靜態方法中調用一個靜態方法,你可使用this關鍵字
從類的構造函數和其餘方法靜態方法不能直接在非靜態方法中使用this關鍵字來訪問。你須要使用類名來調用他們:
三、new.target關鍵字
new.target屬性容許你檢測函數或構造方法是否經過是經過new運算符被調用的。在經過new運算符被初始化的函數或構造方法中,new.target返回一個指向構造方法或函數的引用。在普通的函數調用中,new.target 的值是undefined。
怎麼理解這段話,也就是說new.target的功能就是用來檢測函數的調用是否是經過 new 去建立一個新對象的,並且new.target返回的是一個指向函數的引用,也就是說咱們可以肯定是哪一個函數進行了new操做
class A { constructor() { console.log(new.target.name); } } class B extends A { constructor() { super(); } } var a = new A(); // logs "A" var b = new B(); // logs "B"
new.target 最大的做用就是讓構造器知道當前到底 new 的是哪一個類。
延伸下。ES6以前怎麼實現這個功能?
var A = function A() { if(!(this instanceof A)) throw 'Constructor A requires "new"'; // ··· };
然而這依然能夠經過 call 或 apply 來調用。好比:
var a = A.call(Object.create(A.prototype));
那麼用ES6就是下面這樣操做了
var A = function A() { if(!new.target) throw 'Constructor A requires "new"'; // ··· };
四、constructor關鍵字
構造方法是建立和初始化使用類建立的一個對象的一種特殊方法。
class Square extends Polygon { constructor(length) { // 在這裏調用父類的"length",賦值給矩形的"width"和"height"。 super(length, length); // 注意:子類必須在constructor方法中調用super方法,不然新建實例時會報錯。 this.name = 'Square'; } get area() { return this.height * this.width; } set area(value) { this.area = value; } }
若是沒有顯式定義,會默認添加一個空的constructor方法。對於基類"Base classes",默認構造方法以下:
constructor() {}
對於派生類"Derived classes" ,默認構造方法以下:
constructor(...args) { super(...args); }