一、面向物件
1.繼承
只要將方法定義放在建構式中,那么每次new時都會執行function,這樣就會反復創建相同函式的多個副本,導致浪費記憶體,如果將來發現多個子物件都要使用相同的功能和屬性值時,都可以用繼承來解決,
父物件中的成員,子物件無需重復創建就可直接使用,就像使用自己的成員一樣,這就是繼承,js中的繼承都是通過原型物件實作的,原型物件就是替所有子物件集中保存共有屬性值和方法的特殊父物件,當多個子物件需要使用相同的功能和屬性值時,都可將相同的功能和屬性值集中定義在原型物件中,
原型物件: 不用自己創建,在定義建構式時,程式自動附贈我們一個空的原型物件,建構式中都有一個自帶的屬性prototype,指向自己配對的原型物件——建構式.prototype,
| 補充:new的四個作用 |
| 1. 創建一個新的空物件等待 |
| 2. 讓子物件繼承建構式的原型物件(繼承專用) |
| 3. 呼叫建構式,將this替換為新物件,通過強行賦值方式為新物件添加規定的屬性 |
| 4. 回傳新物件地址 |
new的第二步自動讓新創建的子物件,繼承建構式的原型物件,new會自動為子物件添加_ _proto_ _屬性,指向建構式的原型物件,
向原型物件中添加新的共有屬性和方法時,只能是強行賦值:
| 建構式.prototype.共有方法=function(){ ... } |
| 建構式.prototype.共有屬性=屬性值 |
添加新屬性或方法后,用子物件訪問物件的成員時,js引擎先在子物件內部查找自有的屬性;如果子物件沒有,則js引擎會自動延_ _proto_ _屬性去父元素查找,如果在父元素中找到了想要的屬性或方法,則和訪問子物件的方法一樣呼叫,
示例: 將所有子物件共用的方法保存進建構式里
<script>
// 建構式
function Student(sname, sage) {
this.sname = sname;
this.sage = sage;
}
// 強行向student型別的原型物件(prototype)中添加一個所有子物件共用的的方法intr
Student.prototype.intr = function () {
console.log(`我叫${this.sname},我今年${this.sage}歲`);
}
// 用建構式反復創建多個相同結構但內容不同的物件
var lilei = new Student("李雷", 45);
var hmm = new Student("韓梅梅", 30);
console.log(lilei);
console.log(hmm);
lilei.intr();
hmm.intr();
console.log(lilei.__proto__ == Student.prototype); //true說明原型物件是子元素的父級
console.log(hmm.__proto__ == Student.prototype); //true
</script>
· 自有屬性和共有屬性
| 自有屬性:保存在子物件內部,只歸當前子物件自有的屬性; |
| 共有屬性:保存在父物件(原型物件)中,歸多個子物件共有的屬性; |
(1)獲取屬性值時,毫無差別,都可用: 子物件.屬性名,如果js引擎發現要使用的屬性不在子物件中,則自動延_ _proto_ _屬性向父物件繼續查找要用屬性,
(2)修改屬性值
| 自有屬性: | 子物件.屬性名=屬性值; |
| 共有屬性: | 正確方法 | 錯誤方法 |
| 建構式.prototype.共有屬性=新值 | 子物件.共有屬性=新值 | |
| 共有屬性,必須用原型物件修改 | 此方法不但不會修改原型物件中的共有屬性,而且還會給當前子物件添加一個同名的自有屬性,從此,這個子物件,在這個屬性的使用上,與其他子物件,再無法保持同步! |
示例: 為所有學生添加共有的班級名屬性,并修改
<script>
function Student(sname, sage) {
this.sname = sname;
this.sage = sage;
}
Student.prototype.className = "初一二班";
var lilei = new Student("lilei", 11);
var hmm = new Student("hmm", 12);
// 修改自有屬性
lilei.sname = "zhangsan";
console.log(lilei);
console.log(hmm);
console.log(lilei.className, hmm.className);
// 一年后,兩位同學一起升級(修改共有屬性)
// 1.錯誤方式
// lilei.className = "初二二班";
// 此方式不會修改原型物件中的共有屬性,而會給當前子物件添加一個同名的自有屬性,導致該子物件與其他子物件無法繼續保持同步,
// 2.正確方式,必須修改原型物件
Student.prototype.className = "初二二班";
console.log(lilei.className, hmm.className);
</script>
· 內置型別的原型物件
內置型別就是ES標準中規定的,瀏覽器已經實作,我們可以直接使用的型別,包括十一種String、Number、Boolean、Array、Date、RegExp、Error、Function、Object、Math(不是型別,而是一個{ }物件)、global(全域作用域物件,在瀏覽器中被window代替),
每種型別一定有2部分組成:建構式,負責創建該型別的子物件;原型物件,負責為該型別所有子物件集中保存共有的屬性值和方法定義,除Math和global之外,其余也都可以通過new創建子物件,
想要查看該型別中有哪些API,可以使用-- 型別名.prototype --,

如果經常使用的一個功能,但是原型物件中沒有提供,我們可以自定義一個函式保存到原型物件中,
| 建構式.prototype.新方法=function(){...} |
舉例:為陣列型別添加求和的方法
<script>
var array1 = [1, 2, 4, 5, 8, 8];
var array2 = [125, 48, 48, 478, 2584];
// 創建自定義函式
Array.prototype.sum = function () {
console.log("呼叫自定義函式sum");
// 陣列求和套路
// 1.定義變數臨時保持求和的值
var sum = 0;
// 2.遍歷陣列元素
for (i = 0; i < this.length; i++) {
// 3.將遍歷出的元素值累加
sum += this[i];
}
// 4.回傳累加結果
return sum;
}
// 呼叫sum
console.log(array1.sum());
console.log(array2.sum());
</script>
· 原型鏈
原型鏈是由多級父物件逐級繼承形成的鏈式結構,保存著:一個物件可用的所有屬性和方法,控制著屬性和方法的使用順序,采用就近原則,先子級后父級,
<script>
function Student(sname, sage) {
this.sname = sname;
this.sage = sage;
}
// 向Student原型物件中添加一個toString方法
Student.prototype.toString = function () {
// 此處this指將來呼叫這個toString()的點前的某個學生的型別的子物件.
return `{sname:${this.sname},sage:${this.sage}}`;
}
var lilei = new Student("lilei", 12);
var arr = [1, 2, 3];
var now = new Date();
console.log(lilei.toString());
console.log(arr.toString());
console.log(now.toString);
</script>
2.多型
多型指同一個函式,在不同情況下表現出不同的狀態,包括多載和重寫,
| 多載overload | 同一個函式,輸入不同的引數,執行不同的邏輯, |
| 重寫override | 在子物件中定義一個和父物件中的成員同名的自有成員,當從父物件繼承來的個別成員不好用時,就可以在子物件中定義同名成員,來覆寫父物件中的同名成員, |
3.自定義繼承
(1)只更換一個物件的父物件,兩種方法:
| 子物件._ _proto_ _=新父物件 | 不推薦 |
| Object.setPrototypeOf(子物件, 新父物件) | 推薦 |
//示例:更換一個物件的父物件
<script>
function Student(sname, sage) {
this.sname = sname;
this.sage = sage;
}
var lilei = new Student("李磊", 11);
var hmm = new Student("韓梅梅", 12);
var father = {
money: 1000000000000,
car: "infiniti"
}
// 1.只更換一個物件的父物件
// 更換hmm的繼承父物件為father
// hmm.__proto__ = father;//不推薦
Object.setPrototypeOf(hmm, father); //推薦
console.log(hmm.money, hmm.car);
console.log(lilei.money, lilei.car);
console.log(lilei);
console.log(hmm);
</script>
(2) 批量更換多個子物件的父物件,只需要更換建構式的prototype屬性就可以,但必須在創建子物件之前更換!
//示例: 批量更換兩個子物件的父物件
<script>
function Student(sname, sage) {
this.sname = sname;
this.sage = sage;
}
var father = {
money: 1000000000000,
car: "infiniti"
}
// 2.批量更換多個子物件的父物件
// 必須在創建子物件之前更換建構式的原型物件
Student.prototype = father;
var lilei = new Student("李磊", 11);
var hmm = new Student("韓梅梅", 12);
console.log(hmm.money, hmm.car);
console.log(lilei.money, lilei.car);
console.log(lilei);
console.log(hmm);
</script>
二、ES5(ECMAScript 第5個版本)
1.嚴格模式
在舊的js中有很多廣受詬病的缺陷,嚴格模式就是比舊的js運行機制要求更嚴格的新運行機制,今后企業中所有代碼都要運行在嚴格模式下,啟用嚴格模式只需在當前代碼段的頂部添加: "use strict";即可,嚴格模式有四個新規定:
a:禁止給未宣告過的變數賦值;舊的js中,如果強行給未宣告過的變數賦值,不會報錯,而是,自動在全域設定該變數,這就造成了全域污染,而嚴格模式中,強行給未宣告過的變數賦值,會報錯,這樣就減少了因為寫錯變數名造成的全域污染!
舉例:給未宣告的區域變數賦值
<script>
function send() {
var gf;
// 假設不小心寫錯了變數名
qgf = "今晚308,w84u"; //直接報錯qgf is not defined
console.log(`女朋友收到${gf}`);
}
send();
console.log(`全域中:${qgf}`);
</script>
未使用嚴格模式,列印如下,本來想給“女朋友”發送的訊息,卻發到了全域,但系統不報錯,

啟用嚴格模式,在代碼段首行添加"use strict";
<script>
// 啟用嚴格模式
"use strict";
// 禁止給未宣告過的變數賦值
function send() {
var gf;
// 假設不小心寫錯了變數名
qgf = "今晚308,w84u"; //直接報錯qgf is not defined
console.log(`女朋友收到${gf}`);
}
send();
console.log(`全域中:${qgf}`);
</script>
列印如下,此時直接報錯,便于我們發現程式中的問題并修改,

b. 靜默失敗升級為錯誤;靜默失敗指程式運行不成功,但是也不報錯,這樣極其不利于除錯程式,嚴格模式:會將絕大部分靜默失敗都升級為報錯,舉例:
<script>
// 靜默失敗升級為錯誤
var eric = {
aid: 1001,
sanme: "斯塔克"
}
// 這里將eid屬性設定為只讀
Object.defineProperty(eric, "eid", {
writable: false
})
// 試圖篡改
eric.eid = 1002;
console.log(eric);
</script>
列印如下:即使將eid設定為只讀,屬性值仍被修改,但不報錯,

啟動嚴格模式后列印如下:

c. 普通函式呼叫中的this不再指window,而是指undefined,在舊js中普通函式呼叫中的this默認指window,極容易造成全域污染,啟用嚴格模式后普通函式呼叫中的this指向undefined,不再指window,可防止因為錯誤使用this而導致的全域污染,舉例:
<script>
// 啟用嚴格模式
"use strict";
// 普通函式呼叫中的this不再指window,而是指undefined
function Student(sname, sage) {
console.log(this);
this.sname = sname; //報錯 Cannot set property 'sname' of undefined
this.sage = sage;
}
var lilei = new Student("李蕾", 12);
// 假設忘記寫new
var hmm = Student("韓梅梅", 13);
console.log(lilei);
console.log(hmm);
console.log(window);
</script>
d. 禁用了arguments.callee,arguments.callee;是在一個函式內,獲得當前函式本身的一種特殊關鍵字(遞回),在函式內寫死當前函式名,一旦外部函式名改變,內部函式名忘記修改,則程式立刻報錯造成緊耦合,所以在函式內使用arguments.callee代替寫死的函式名,在運行時,自動獲得當前函式本身(松耦合),
而且遞回重復計算量太大,效率極低,如果遞回呼叫嚴重影響程式的性能時,就要用回圈來代替遞回,舉例:分別使用遞回和回圈實作計算斐波那契數列第n個數
//遞回方式
<script>
// 禁用了arguments.callee
// 斐波那契數列
// 前兩個數是都是1,從第三個數開始,每個數都是它相鄰的前兩個數的和
function f(n) {
if (n < 3) {
return 1;
} else {
return arguments.callee(n - 1) + arguments.callee(n - 2);
}
}
console.log(f(10));//55
</script>
//回圈方式
<script>
function f(n) {
if (n < 3) {
return 1;
} else {
var f1 = 1,
f2 = 1,
fn;
for (var i = 3; i <= n; i++) {
fn = f1 + f2;
f1 = f2;
f2 = fn;
}
return fn;
}
}
console.log(f(10)); //55
</script>
補充:this 4種
(1)obj.fun() fun中的this指 .前的obj物件(誰呼叫指誰);
(2)new Fun() Fun中的this指new創建的新物件;
(3)fun() 或 (function(){ ... })() 或 回呼函式 thisz默認指windozw;
(4)原型物件(prototype)中的this指將來呼叫這個共有函式的.前的某個子物件(誰呼叫指誰),
往期JavaScript高級博文鏈接:
JavaScript高級(一)
JavaScript高級(二)
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/298683.html
標籤:其他
上一篇:?【爆肝萬字】手把手教你SpringBoot+MyBatis+jQuery+HTML5從0開始寫網頁一學就會!(內附原始碼)?
下一篇:cgb2107-day11
