一、構造函數
1. 用new調用函數的四步走
new 函數();
-
JS規定,使用new操作符調用函數會進行"四步走":
- 函數體內會自動創建出一個空白對象
- 函數的上下文(this)會指向這個對象
- 函數體內的語句會執行
- 函數會自動返回上下文對象,即使函數沒有
return
語句
-
四步走詳解
function fun() {this.a = 3;this.b = 5;}var obj = new fun();console.log(obj);
-
第1步:函數體內會自動創建出一個空白對象。
-
第2步:函數的上下文(this)會指向這個對象。
-
第3步:執行函數體中的語句
-
第4步:函數會自動返回上下文對象,即使函數沒有return語句。
-
2. 構造函數
-
用new調用一個函數,這個函數就被稱為
"構造函數"
,任何函數都可以是構造函數,只需要用new調用它。 -
顧名思義,構造函數用來“構造新對象”,它內部的語句將 為新對象添加若干屬性和方法,
完成對象的初始化
。 -
構造函數必須用new關鍵字調用,否則不能正常工作,正因 如此,開發者約定
構造函數命名時首字母要大寫
。 -
一個函數是不是構造函數,要看它是否用
new
調用,而至于名稱首字母大寫,完全是開發者的習慣約定。 -
如果不用
new
調用構造函數function People(name, age, sex) {this.name = name;this.age = age;this.sex = sex;} People('小明', 12, '男');People('小紅', 10, '女');People('小剛', 13, '男');
- 結果:都會成為全局的變量,且變量的值會依次覆蓋,就是小剛 13 男
-
使用
new
構建function People(name, age, sex) {this.name = name;this.age = age;this.sex = sex;}var xiaoming = new People('小明', 12, '男');var xiaohong = new People('小紅', 10, '女');var xiaogang = new People('小剛', 13, '男');
-
為對象添加方法
function People(name, age, sex) { this.name = name;this.age = age;this.sex = sex;this.sayHello = function () {console.log('我是' + this.name + ',我' + this.age + '歲了');}; }var xiaoming = new People('小明', 12, '男'); var xiaohong = new People('小紅', 10, '女'); var xiaogang = new People('小剛', 13, '男'); xiaoming.sayHello();xiaohong.sayHello();xiaogang.sayHello();
-
構造函數中的this不是函數本身
3. 類和實例
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-1cNSmlDn-1692020300179)(https://raw.githubusercontent.com/yangdong520/drawing-bed-workspace/main/js/obj-5.png)]
- Java、C++等是"面向對象"(object-oriented)語言。
- JavaScript是"基于對象"(object-based)語言。
- JavaScript中的
構造函數可以類比于OO語言中的"類"
, 寫法的確類似,但和真正OO語言還是有本質不同
,
JS和其他OO語言完全不同的、特有的原型特性。
4. prototype和原型鏈查找
-
什么是prototype
- 任何函數都有
prototype屬性
,prototype是英語"原型"的意思。 - prototype屬性值是個對象,它默認擁有
constructor屬性指回函數
。
- 普通函數來說的prototype屬性沒有任何用處,而
構造函數的prototype屬性非常有用。
構造函數的prototype屬性是它的實例的原型
- 任何函數都有
-
構造函數的prototype是實例的原型
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title> </head> <body><script>function People(name, age, sex) {this.name = name;this.age = age;this.sex = sex;}// 實例化var xiaoming = new People('小明', 12, '男');// 測試三角關系是否存在console.log(xiaoming.__proto__ === People.prototype);</script> </body> </html>
-
原型鏈查找
-
每個對象都可以有一個原型_proto_,這個原型還可以有它自己的原型,以此類推,形成一個原型鏈。
查找特定屬性的時候,先去這個對象里去找,如果沒有的話就去它的原型對象里面去,如果還是沒有的話再去向原型對象的原型對象里去尋找…
這個操作被委托在整個原型鏈上,這個就是我們說的原型鏈了。 -
JavaScript規定
:實例可以打點訪問它的原型的屬性和方法,這被稱為"原型鏈查找"。function People(name, age, sex) {this.name = name;this.age = age;this.sex = sex;}People.prototype.nationality = '中國';var xiaoming = new People('小明', 12, '男');console.log(xiaoming.nationality);
People.prototype.nationality
在構造函數的prototype
上添加nationality
屬性。xiaoming.nationality
實例可以打點訪問原型的屬性和方法。
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-19GzAMjt-1692020300180)(https://raw.githubusercontent.com/yangdong520/drawing-bed-workspace/main/js/obj-9.png)]
-
原型的遮蔽效應
function People(name, age, sex) {this.name = name;this.age = age;this.sex = sex; } // 往原型上添加nationality屬性 People.prototype.nationality = '中國';// 實例化 var xiaoming = new People('小明', 12, '男'); var tom = new People('湯姆', 10, '男'); tom.nationality = '美國';console.log(xiaoming.nationality); // 中國 console.log(xiaoming);console.log(tom.nationality); // 美國 //tom本身有nationality時,就不找原型上的nationality屬性了,原型鏈的遮蔽效應 //跟局部變量全局變量差不多
-
hasOwnProperty
方法可以檢查對象是否真正"自己擁有"某屬性或者方法。xiaoming.hasOwnProperty('name'); // truexiaoming.hasOwnProperty('age'); // truexiaoming.hasOwnProperty('sex'); // truexiaoming.hasOwnProperty('nationality'); // false
-
in
運算符只能檢查某個屬性或方法是否可以被對象訪問,不能檢查是否是自己的屬性或方法。'name' in xiaoming // true'age' in xiaoming // true'sex' in xiaoming // true'nationality' in xiaoming // true
-
5. 在prototype
上添加方法
- 把方法直接添加到實例身上的缺點:每個實例和每個實例的方法函數
都是內存中不同的函數,造成了內存的浪費
。 - 解決方法:
將方法寫到prototype上
。
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-KfgSpKFg-1692020300180)(https://raw.githubusercontent.com/yangdong520/drawing-bed-workspace/main/js/obj-11.png)]
6. 原型鏈的終點
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-YC2Sl5h4-1692020300180)(https://raw.githubusercontent.com/yangdong520/drawing-bed-workspace/main/js/obj-12.png)]
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head>
<body><script>function People(name, age) {this.name = name;this.age = age;}var xiaoming = new People('小明', 12);console.log(xiaoming.__proto__.__proto__ === Object.prototype); // trueconsole.log(Object.prototype.__proto__); // nullconsole.log(Object.prototype.hasOwnProperty('hasOwnProperty')); // trueconsole.log(Object.prototype.hasOwnProperty('toString')); // true</script>
</body>
</html>
-
關于數組的原型鏈
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title> </head> <body><script>var arr = [344, 45, 34, 23];console.log(arr.__proto__ === Array.prototype); // trueconsole.log(arr.__proto__.__proto__ === Object.prototype); // trueconsole.log(Array.prototype.hasOwnProperty('push')); // trueconsole.log(Array.prototype.hasOwnProperty('splice')); // true</script> </body> </html>
6. 繼承
-
JavaScript中如何實現繼承?
- 實現繼承的關鍵在于:子類必須擁有父類的
全部屬性和方法
,同時子類還應該能定義自己特有的屬性和方法。 使用JavaScript特有的原型鏈特性來實現繼承,是普遍的做法
- 實現繼承的關鍵在于:子類必須擁有父類的
-
通過原型鏈實現繼承
<!DOCTYPE html> <html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title> </head><body><script>// 父類,人類function People(name, age, sex) {this.name = name;this.age = age;this.sex = sex;}People.prototype.sayHello = function () {console.log('你好,我是' + this.name + '我今年' + this.age + '歲了');};People.prototype.sleep = function () {console.log(this.name + '開始睡覺,zzzzz');};// 子類,學生類function Student(name, age, sex, scholl, studentNumber) {this.name = name;this.age = age;this.sex = sex;this.scholl = scholl;this.studentNumber = studentNumber;}// 關鍵語句,實現繼承Student.prototype = new People();Student.prototype.study = function () {console.log(this.name + '正在學習');}Student.prototype.exam = function () {console.log(this.name + '正在考試,加油!');}// 重寫、復寫(override)父類的sayHelloStudent.prototype.sayHello = function () {console.log('敬禮!我是' + this.name + '我今年' + this.age + '歲了');}// 實例化var hanmeimei = new Student('韓梅梅', 9, '女', '慕課小學', 100556);hanmeimei.study();hanmeimei.sayHello();hanmeimei.sleep();var laozhang = new People('老張', 66, '男');laozhang.sayHello();</script> </body></html>