構造函數和class的關系,還有面向對象和原型對象,其實很多人都會很困惑這些概念,這是第二次總結這些概念了,之前一次,沒有class類,其實了解了構造函數,class也就很容易理解了
一. 構造函數和原型
1.function 聲明(創造)了一個函數 Person,叫構造函數
2.原型對象:在聲明了一個函數之后,瀏覽器會自動按照一定的規則創建一個對象,這個對象就叫做原型對象。這個原型對象其實是儲存在了內存當中。
3.在聲明了一個函數后,這個構造函數(聲明了的函數)中會有一個屬性prototype,這個屬性指向的就是這個構造函數(聲明了的函數)對應的原型對象;原型對象中有一個屬性constructor,這個屬性指向的是這個構造函數(聲明了的函數)。
所以說:Person(構造函數)的 prototype 指向原型對象,原型對象的 constructor 屬性,又指向該構造函數本身
1 function Person(name, age) { 2 this.name = name; 3 this.age = age; 4 } 5 console.log(Person); // 構造函數本身 6 console.log(Person.prototype.constructor); // 指向 構造函數本身 7 Person("范順", 18);
二. 構造函數使用new來創建對象
1 function students() { 2 3 } 4 5 var stu = new students();
1. stu 是 new students()(構造函數) 創建出來的對象,這個stu對象中是沒有prototype屬性的,prototype屬性只有在構造函數students中有
2. 但stu(創建出來的對象)有一個__proto__屬性,stu調用這個屬性可以直接訪問到構造函數students的原型對象(也就是說,stu的__proto__屬性指向的是構造函數的原型對象)
所以:stu.__proto__ = students.prototype 都指向原型對象
3. 我們給原型對象添加屬性和方法,那么 new 出來的這個對象就可以訪問到該原型對象的屬性和方法
1 function students() { 2 /* 我就是構造函數 */ 3 } 4 students.prototype.name="shun" 5 var stu = new students(); 6 console.log(stu.__proto__.name); // 訪問到原型對象添加的屬性 7 console.log(stu.name); // 這樣也是能訪問到原型對象添加的屬性,因為stu本身沒有這個name屬性,所以說會向stu對象的__proto__ 屬性指向的原型對象中找,找到就返回,找不到就往上找,就是原型鏈 8 9 stu.name = "fan" // 給stu對象添加屬性 10 console.log(stu.name); // 訪問stu對象的屬性
4. 因為 new 出來的 stu 是一個對象,我們也可以給它直接設置屬性,如果找到直接返回值,如果stu對象本身沒有這個屬性,那么就會向上找stu對象的__proto__屬性指向的原型對象中查找,如果查找到則返回。(如果原型中也沒有找到,則繼續向上找原型的原型 這就是所說的原型鏈。
5. 如果通過stu對象添加了一個屬性name,則stu對象來說就屏蔽了原型中的屬性name。 換句話說:在stu中就沒有辦法訪問到原型的屬性name了。
通過stu對象只能讀取原型中的屬性name的值,而不能修改原型中的屬性name的值。 stu.name = “李四”; 並不是修改了原型中的值,而是在stu對象中給添加了一個屬性name
<script type="text/javascript"> function students () { } // 可以使用students.prototype 直接訪問到原型對象 //給students函數的原型對象中添加一個屬性 name並且值是 "張三" students.prototype.name = "張三"; students.prototype.age = 20; var stu = new students(); /* 訪問stu對象的屬性name,雖然在stu對象中我們並沒有明確的添加屬性name,但是 stu的__proto__屬性指向的原型中有name屬性,所以這個地方可以訪問到屬性name 就值。 注意:這個時候不能通過stu對象刪除name屬性,因為只能刪除在stu中刪除的對象。 */ alert(stu.name); // 張三 var stu1 = new students(); alert(stu1.name); // 張三 都是從原型中找到的,所以一樣。 alert(stu.name === stu1.name); // true // 由於不能修改原型中的值,則這種方法就直接在stu中添加了一個新的屬性name,然后在stu中無法再訪問到 //原型中的屬性。 stu.name = "李四"; alert(stu.name); //李四 // 由於stu1中沒有name屬性,則對stu1來說仍然是訪問的原型中的屬性。 alert(stu1.name); // 張三 </script>
三. 與原型有關的幾個方法
**1. prototype屬性**
prototype 存在於構造函數中 (其實任意函數中都有,只是不是構造函數的時候prototype我們不關注而已) ,他指向了這個構造函數的原型對象。
**2.constructor屬性**
constructor屬性存在於原型對象中,他指向了構造函數
如下面代碼:
<script type="text/javascript"> function students () { } alert(students.prototype.constructor === students); // true </script>
我們根據需要,可以students.prototype 屬性指定新的對象,來作為students的原型對象。但是這個時候有個問題,新的對象的constructor屬性則不再指向students構造函數了。
**3.__proto__ 屬性(注意:左右各是2個下划線)**
用構造方法創建一個新的對象之后,這個對象中默認會有一個屬性__proto__, 這個屬性就指向了構造方法的原型對象。
四. class(類)
1 class Point { 2 constructor() { 3 // ... 4 } 5 toString() { 6 return '類似於夠贊函數中的原型鏈,訪問到這個原型身上的方法' 7 } 8 toValue() { 9 // ... 10 } 11 } 12 13 14 // 等同於 15 // Point.prototype = { 16 //toString(){}, 17 //toValue(){} 18 //}; 19 20 // 所以說 21 const p1 = new Point() 22 console.log(p1.toString()) //類似於夠贊函數中的原型鏈,訪問到這個原型身上的方法
實例屬性和靜態屬性(實例方法和靜態方法)
這里我是進入了一個誤區,但是通過打印發現
拿下面的例子舉例:我錯誤的認為 P1.info 是有值的,雖然構造函數身上追加了一個屬性 info,顯而易見,這是原型對象的 constructor 身上的屬性,並不是原型對象身上的屬性
,因為 原型對象的 constructor指向構造函數本身,這個info是構造函數身上的方法,而通過new出來的實例對象 P1.info 其實是通過原型鏈的形式訪問原型對象身上的info屬性,所以說訪問的是 undefined,這里的 constructor屬性和 P1 的 info屬性都屬於原型對象身上的屬性,他倆是平級關系
只要是通過new出來的實例能訪問到的方法(屬性),叫做實例方法,靜態方法只存在構造函數本身
實例屬性:通過new出來的實例訪問到的屬性叫做實例屬性
靜態屬性:在構造函數中構造函數添加的屬性 或者說 在class內部通過static 修飾的屬性叫做靜態屬性
1 function Person(name, age) { 2 this.name = name; 3 this.age = age; 4 } 5
Person.show = function() { //靜態方法
console.log('這是靜態方法')
} 6 Person.info = '哈哈' //靜態屬性
Person.prototype.say = function() { //這是實例方法,掛載到的是構造函數的原型對象身上,P1可以訪問到
console.log("這是實例方法,掛載到的是構造函數的原型對象身上,P1可以訪問到")
} 7 const P1 = new Person("范順", 18); 8 console.log(P1.name) //通過new出來的實例訪問到的屬性叫做實例屬性 9 console.log(P1.age) 10 11 console.log(P1.info) // undefined 12 console.log(Person.info) //靜態屬性
P1.say() //這是實例方法,掛載到的是構造函數的原型對象身上,P1可以訪問到
13 14 15 class Anmin { 16 constructor(name, age) { 17 this.name = name; 18 this.age = age; 19 } 20 21 // 在class內部通過static 修飾的屬性叫做靜態屬性,只能被構造函數名或者類名讀取,new出來的對象讀不到 22 static info = '哈哈'
say() {
console.log('實例方法')
}
static show() {
console.log('靜態方法')
}
23 } 24 25 const A1 = new Anmin('大黃', 9) 26 27 console.log(A1.name) //實例屬性 28 console.log(A1.age) 29 30 console.log(A1.info) // undefined 31 console.log(Anmin.info) //靜態屬性
console.log(A1.say()) //實例方法 say
class類的extends繼承
繼承父類的屬性和方法
1 class Father { 2 constructor(name, age) { 3 this.name = name 4 this.age = age 5 } 6 } 7 // 子類 8 class Person extends Father { 9 constructor(name, age, yellow) { //yellow 是子類定義的屬性 10 super(name, age, yellow) 11 this.yellow = yellow 12 } 13 } 14 15 const P1 = new Person("范順", 18, "紅色") 16 console.log(P1) 17 18 // 子類 19 // class Anmainl { 20 // } 21 22 // const A1 = new Anmainl('二哈', 9) 23 // console.log(A1)