面向?qū)ο缶幊探榻B
面向過程
- 概念:面向過程就是分析出解決問題所需的步驟厂僧,然后用函數(shù)把這些步驟一步一步的實現(xiàn)扣草,使用的時候再依次調(diào)用這些函數(shù)。
- 優(yōu)點:性能比面向?qū)ο蟾撸m合跟硬件聯(lián)系很緊密的東西德召。
- 缺點:沒有面向?qū)ο笠拙S護(hù)白魂、易復(fù)用、易擴(kuò)展上岗。
面向?qū)ο?/h4>
- 概念:面向?qū)ο缶褪前咽聞?wù)分解成一個一個的對象福荸,然后由對象之間分工合作
- 優(yōu)點:易維護(hù)、易復(fù)用肴掷、易擴(kuò)展敬锐,由于面向?qū)ο笥蟹庋b,繼承呆瞻,多態(tài)的特性台夺,可以設(shè)計出低耦合的系統(tǒng),使系統(tǒng)更加靈活痴脾,更加易于維護(hù)颤介。
- 缺點:性能比面向過程低。
ES6中的類和對象
對象
- 在JavaScript中赞赖,對象是一組無序的相關(guān)屬性和方法的集合滚朵,所有的事務(wù)都是對象,例如字符串前域,數(shù)字辕近,數(shù)組,函數(shù)等匿垄。
- 對象是由屬性和方法組成的移宅。
- 屬性:事物的特征,在對象中用屬性來表示椿疗。
- 行為:事物的行為漏峰,在對象中用方法來表示。
- 在實際開發(fā)中变丧,對象是一個抽象的概念芽狗,可以將其簡單的理解為:數(shù)據(jù)集或功能集绢掰。
-
類
- 在ES6中增加了類的概念痒蓬,可以使用
class
關(guān)鍵字聲明一個類,之后用這個類來實例化對象滴劲。
- 類抽象了對象的公共部分攻晒,它泛指某一大類。
- 對象特指某一個班挖,通過類實例化一個具體的對象鲁捏。
- 注意:
- 在 ES6 中類沒有變量提升,所以必須先定義類萧芙,才能通過類實例化對象给梅。
- 類里面的共有的屬性和方法一定要加this使用假丧。
- 類中的
constructor
構(gòu)造函數(shù)中的 this
指向的是創(chuàng)建的實例對象。
- 類中的方法中的
this
指向的是創(chuàng)建的實例對象动羽,因為是創(chuàng)建的實例對象調(diào)用了這個方法包帚,所以指向的是它的調(diào)用者。
創(chuàng)建類
- 語法:
class name {}
;
- 創(chuàng)建實例:
var className = new name()
;
- 注意:類必須使用 new 實例化對象运吓。
- 總結(jié):
- 通過
class
關(guān)鍵字創(chuàng)建類渴邦,類名的首字母需要大寫。
- 類中有一個
constructor
函數(shù)拘哨,可以接收傳遞過來的參數(shù)谋梭,同時返回實例對象。
-
constructor
這個函數(shù)只要 new 生成實例時倦青,就會自動調(diào)用這個函數(shù)瓮床,如果我們不寫這個函數(shù),類也會自動生成這個函數(shù)产镐。
- 生成實例不能省略關(guān)鍵字
new
纤垂。
- 在創(chuàng)建類的時候,類名后面不要加小括號磷账,生成實例的時候峭沦,類名后面要添加小括號,
- 構(gòu)造函數(shù)的時候不需要添加 function逃糟,在類中多個函數(shù)中間不需要添加逗號分隔吼鱼。
- 代碼示例:
-
class Rabbit {
constructor(type) {
this.type = type;
}
spark(line) {
// 構(gòu)造函數(shù),不需要加 function
console.log(`The ${this.type} rabbit says '${line}'`)
};
};
let object = new Rabbit("killer")
console.log(object)
object.spark("skr")
類中的靜態(tài)方法
- 在ES6中绰咽,可以直接使用
static
關(guān)鍵字創(chuàng)建一個靜態(tài)方法菇肃。
- 使用
static
關(guān)鍵字創(chuàng)建的靜態(tài)方法,方法中的類型指向的是這個對象自己取募。
<script>
class Person {
constructor(name) {
this.name = name;
}
sayHi() {
console.log(`${this.name}, Hi`);
}
// 靜態(tài)方法
static create(name) {
return new Person(name);
}
}
const p = Person.create("tom");
p.sayHi()
</script>
類的繼承
- 描述:子類可以繼承父類的一些屬性和方法琐谤。
- 語法:
class Father {} // 父類
class Son extends Father {} //子類繼承父類
- 注意:
- 繼承中,如果實例化子類輸出一個方法玩敏,先看子類有沒有這個方法斗忌,如果有就先執(zhí)行子類的。
- 繼承中旺聚,如果子類里面沒有這個方法织阳,就去查找父類中有沒有這個方法,如果有砰粹,就執(zhí)行父類的這個方法(就近原則)唧躲。
super 關(guān)鍵字
-
super
關(guān)鍵字用于訪問和調(diào)用對象父類上的函數(shù)。可以調(diào)用父類的構(gòu)造函數(shù)弄痹,也可以調(diào)用父類的普通函數(shù)饭入。
- 語法:
class Father {
constructor(x, y) {
this.x = x;
this.y = y;
}
sum() {
// console.log(this.x + this.y);
console.log(x + y);
}
};
class Son extends Father {
constructor(x, y) {
super(x, y); // 調(diào)用父類中的構(gòu)造函數(shù)
}
}
// Son 繼承自父類,當(dāng)實例化 Son 類傳入?yún)?shù)的的時候肛真, Son 類中的 constructor 構(gòu)造函數(shù)會接受參數(shù)圣拄,并且將參數(shù)傳遞到 super中,進(jìn)而傳遞到父類的構(gòu)造函數(shù)中毁欣,所以可以調(diào)用父類的sum()方法庇谆,
var s = new Son(1, 2);
s.sum();
- 繼承中,在子類中調(diào)用父類的方法凭疮,使用
super.方法名()
饭耳,就可以調(diào)用父類中的方法。
class Father {
say() {
return "這是父類";
}
};
class Son extends Father {
say() {
// super.方法名 表示調(diào)用父類中的方法
console.log(super.say() + "的子類");
}
};
- 在繼承中执解,子類中構(gòu)造新的方法寞肖,同時還繼承父類中的方法,此時在使用
super
的時候衰腌,必須在子類this
之前調(diào)用新蟆。
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 必須在子類this之前調(diào)用
super(x, y);
this.x = x;
this.y = y;
}
subtract() {
console.log(this.x - this.y);
}
};
var s = new Son(5, 3);
s.subtract(); // 2
s.sum(); // 8
構(gòu)造函數(shù)和原型
- 在ES6之前,對象不是基于類創(chuàng)建的右蕊,而是用一種稱為
構(gòu)建函數(shù)
的特殊函數(shù)來定義對象和他們的特征琼稻。
構(gòu)造函數(shù)
構(gòu)造函數(shù)是一種特殊的函數(shù),主要用來初始化對象饶囚,即為對象成員變量賦初始值帕翻,它總與 new 一起使用。我們可以把對象中的一些公共的屬性和方法抽取出來萝风,然后封裝到這個構(gòu)造函數(shù)中嘀掸。
-
注意:
- 構(gòu)造函數(shù)用于創(chuàng)建某一類對象,其首字母要大寫规惰。
- 構(gòu)造函數(shù)要和
new
關(guān)鍵字一起使用才有意義睬塌。
-
new 在執(zhí)行時所做的四件事
- 在內(nèi)存中創(chuàng)建一個新的空對象。
- 讓 this 指向這個對象歇万。
- 執(zhí)行構(gòu)造函數(shù)中的代碼揩晴,給這個新對象添加屬性和方法。
- 返回這個新對象(所以構(gòu)造函數(shù)里面不需要return)堕花。
代碼示例:
<script>
function Start(name, age) {
this.name = name;
this.age = age;
this.sing = function () {
console.log("這是一個方法");
}
}
var start = new Start();
</script>
構(gòu)造函數(shù)和實例對象的關(guān)系
- 構(gòu)造函數(shù)是根據(jù)具體的事物抽象出來的抽象模板文狱。
- 實例對象是根據(jù)抽象的構(gòu)造函數(shù)模板得到的具體實例對象粥鞋。
- 每一個實例對象都有一個
constructor
屬性缘挽,它指向創(chuàng)建該實例對象的構(gòu)造函數(shù)。
- 可以通過
constructor
屬性判斷實例對象和構(gòu)造函數(shù)之間的關(guān)系,但是不推薦使用這種方法壕曼,更推薦使用 instanceof
操作符苏研。
構(gòu)造函數(shù)的成員
- JavaScript 的構(gòu)造函數(shù)中可以添加一些成員,可以在構(gòu)造函數(shù)本身添加腮郊,也可以在構(gòu)造函數(shù)內(nèi)部的 this 上添加摹蘑,通過這兩種方式添加的成員,分別稱為
靜態(tài)成員
和實例成員
轧飞。
- 靜態(tài)成員:在構(gòu)造函數(shù)本身上添加的成員稱為
靜態(tài)成員
衅鹿,只能由構(gòu)造函數(shù)本身來訪問。
- 實例成員:在構(gòu)造函數(shù)內(nèi)部創(chuàng)建的對象成員(通過
this
添加的成員)稱為實例成員
过咬,只能由實例化的對象來訪問大渤,不可以通過構(gòu)造函數(shù)來訪問實例成員。
- 代碼示例:
<script>
function Start(name, age) {
// 實例成員掸绞,只能通過構(gòu)造函數(shù)的對象來訪問泵三,不可以通過構(gòu)造函數(shù)訪問。
this.name = name;
this.age = age;
this.sing = function() {
console.log("這是一個方法");
}
}
var start = new Start();
// 靜態(tài)成員衔掸,通過構(gòu)造函數(shù)本身添加的成員烫幕。只能通過構(gòu)造函數(shù)本身來訪問,不能通過構(gòu)造函數(shù)的實例對象來訪問敞映。
Start.sex = "男"
</script>
構(gòu)造函數(shù)存在的問題
- 存在問題:
- 構(gòu)造函數(shù)存在浪費內(nèi)存的問題较曼。當(dāng)構(gòu)造函數(shù)中有靜態(tài)方法的時候,當(dāng)實例化一個構(gòu)造函數(shù)振愿,會在內(nèi)存中開辟一個新的內(nèi)存空間诗芜,此時會根據(jù)構(gòu)造函數(shù)中的靜態(tài)方法,再次開辟新的內(nèi)存空間埃疫,所以會造成內(nèi)存空間浪費的問題伏恐。
- 解決方法:使用構(gòu)造函數(shù)的原型對象
構(gòu)造函數(shù)原型
-
構(gòu)造函數(shù)原型 prototype
- 每一個構(gòu)造函數(shù)都有一個原型
prototype
屬性,指向另一個對象栓霜,這個prototype就是一個對象翠桦,這個對象的所有屬性和方法,都會被構(gòu)造函數(shù)所擁有胳蛮。
- 我們可以把一些不變的方法销凑,直接定義在prototype對象上,這樣所有對象的實例就可以共享這個方法仅炊。
- 一般情況下斗幼,我們會將公共的屬性定義到構(gòu)造函數(shù)里面,公共的方法會放到原型對象身上抚垄。
代碼示例
<script>
function Start(name, age) {
this.name = name;
this.age = age;
};
Start.prototype.sing = function() {
console.log("這是定義在prototype對象中的公共方法");
}
var start = new Start();
start.sing()
</script>
對象原型 _proto_
- 所有的對象都會有一個屬性
__proto__
蜕窿,它是一個指針谋逻,指向構(gòu)造函數(shù)中的protottype原型對象,之所有我們對象可以使用構(gòu)造函數(shù)中的prototype
原型對象的屬性和方法桐经,就是應(yīng)為有__proto__
原型的存在毁兆。
- 對象身上系統(tǒng)添加了一個
__proto__
屬性,指向我們構(gòu)造函數(shù)的原型對象阴挣。
-
__proto__
對象原型和原型對象 prototyte
是等價的气堕。
-
__proto__
對象原型的意義就在于為對象的查找機(jī)制提供一個方法,或者說一條路線畔咧,但是他是一個非標(biāo)準(zhǔn)屬性茎芭,因為實際開發(fā)中,不可以使用這個屬性誓沸,它只是內(nèi)部指向原型對象 prototype
骗爆。
constructor 構(gòu)造函數(shù)
- 屬性:事物的特征,在對象中用屬性來表示椿疗。
- 行為:事物的行為漏峰,在對象中用方法來表示。
class
關(guān)鍵字聲明一個類,之后用這個類來實例化對象滴劲。- 在 ES6 中類沒有變量提升,所以必須先定義類萧芙,才能通過類實例化對象给梅。
- 類里面的共有的屬性和方法一定要加this使用假丧。
- 類中的
constructor
構(gòu)造函數(shù)中的this
指向的是創(chuàng)建的實例對象。 - 類中的方法中的
this
指向的是創(chuàng)建的實例對象动羽,因為是創(chuàng)建的實例對象調(diào)用了這個方法包帚,所以指向的是它的調(diào)用者。
class name {}
;var className = new name()
;- 通過
class
關(guān)鍵字創(chuàng)建類渴邦,類名的首字母需要大寫。 - 類中有一個
constructor
函數(shù)拘哨,可以接收傳遞過來的參數(shù)谋梭,同時返回實例對象。 -
constructor
這個函數(shù)只要 new 生成實例時倦青,就會自動調(diào)用這個函數(shù)瓮床,如果我們不寫這個函數(shù),類也會自動生成這個函數(shù)产镐。 - 生成實例不能省略關(guān)鍵字
new
纤垂。 - 在創(chuàng)建類的時候,類名后面不要加小括號磷账,生成實例的時候峭沦,類名后面要添加小括號,
- 構(gòu)造函數(shù)的時候不需要添加 function逃糟,在類中多個函數(shù)中間不需要添加逗號分隔吼鱼。
class Rabbit {
constructor(type) {
this.type = type;
}
spark(line) {
// 構(gòu)造函數(shù),不需要加 function
console.log(`The ${this.type} rabbit says '${line}'`)
};
};
let object = new Rabbit("killer")
console.log(object)
object.spark("skr")
static
關(guān)鍵字創(chuàng)建一個靜態(tài)方法菇肃。static
關(guān)鍵字創(chuàng)建的靜態(tài)方法,方法中的類型指向的是這個對象自己取募。<script>
class Person {
constructor(name) {
this.name = name;
}
sayHi() {
console.log(`${this.name}, Hi`);
}
// 靜態(tài)方法
static create(name) {
return new Person(name);
}
}
const p = Person.create("tom");
p.sayHi()
</script>
class Father {} // 父類
class Son extends Father {} //子類繼承父類
- 繼承中,如果實例化子類輸出一個方法玩敏,先看子類有沒有這個方法斗忌,如果有就先執(zhí)行子類的。
- 繼承中旺聚,如果子類里面沒有這個方法织阳,就去查找父類中有沒有這個方法,如果有砰粹,就執(zhí)行父類的這個方法(就近原則)唧躲。
super
關(guān)鍵字用于訪問和調(diào)用對象父類上的函數(shù)。可以調(diào)用父類的構(gòu)造函數(shù)弄痹,也可以調(diào)用父類的普通函數(shù)饭入。class Father {
constructor(x, y) {
this.x = x;
this.y = y;
}
sum() {
// console.log(this.x + this.y);
console.log(x + y);
}
};
class Son extends Father {
constructor(x, y) {
super(x, y); // 調(diào)用父類中的構(gòu)造函數(shù)
}
}
// Son 繼承自父類,當(dāng)實例化 Son 類傳入?yún)?shù)的的時候肛真, Son 類中的 constructor 構(gòu)造函數(shù)會接受參數(shù)圣拄,并且將參數(shù)傳遞到 super中,進(jìn)而傳遞到父類的構(gòu)造函數(shù)中毁欣,所以可以調(diào)用父類的sum()方法庇谆,
var s = new Son(1, 2);
s.sum();
super.方法名()
饭耳,就可以調(diào)用父類中的方法。class Father {
say() {
return "這是父類";
}
};
class Son extends Father {
say() {
// super.方法名 表示調(diào)用父類中的方法
console.log(super.say() + "的子類");
}
};
super
的時候衰腌,必須在子類this
之前調(diào)用新蟆。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 必須在子類this之前調(diào)用
super(x, y);
this.x = x;
this.y = y;
}
subtract() {
console.log(this.x - this.y);
}
};
var s = new Son(5, 3);
s.subtract(); // 2
s.sum(); // 8
構(gòu)建函數(shù)
的特殊函數(shù)來定義對象和他們的特征琼稻。構(gòu)造函數(shù)是一種特殊的函數(shù),主要用來初始化對象饶囚,即為對象成員變量賦初始值帕翻,它總與 new 一起使用。我們可以把對象中的一些公共的屬性和方法抽取出來萝风,然后封裝到這個構(gòu)造函數(shù)中嘀掸。
注意:
- 構(gòu)造函數(shù)用于創(chuàng)建某一類對象,其首字母要大寫规惰。
- 構(gòu)造函數(shù)要和
new
關(guān)鍵字一起使用才有意義睬塌。
new 在執(zhí)行時所做的四件事
- 在內(nèi)存中創(chuàng)建一個新的空對象。
- 讓 this 指向這個對象歇万。
- 執(zhí)行構(gòu)造函數(shù)中的代碼揩晴,給這個新對象添加屬性和方法。
- 返回這個新對象(所以構(gòu)造函數(shù)里面不需要return)堕花。
代碼示例:
<script>
function Start(name, age) {
this.name = name;
this.age = age;
this.sing = function () {
console.log("這是一個方法");
}
}
var start = new Start();
</script>
constructor
屬性缘挽,它指向創(chuàng)建該實例對象的構(gòu)造函數(shù)。constructor
屬性判斷實例對象和構(gòu)造函數(shù)之間的關(guān)系,但是不推薦使用這種方法壕曼,更推薦使用 instanceof
操作符苏研。靜態(tài)成員
和實例成員
轧飞。靜態(tài)成員
衅鹿,只能由構(gòu)造函數(shù)本身來訪問。this
添加的成員)稱為實例成員
过咬,只能由實例化的對象來訪問大渤,不可以通過構(gòu)造函數(shù)來訪問實例成員。<script>
function Start(name, age) {
// 實例成員掸绞,只能通過構(gòu)造函數(shù)的對象來訪問泵三,不可以通過構(gòu)造函數(shù)訪問。
this.name = name;
this.age = age;
this.sing = function() {
console.log("這是一個方法");
}
}
var start = new Start();
// 靜態(tài)成員衔掸,通過構(gòu)造函數(shù)本身添加的成員烫幕。只能通過構(gòu)造函數(shù)本身來訪問,不能通過構(gòu)造函數(shù)的實例對象來訪問敞映。
Start.sex = "男"
</script>
- 構(gòu)造函數(shù)存在浪費內(nèi)存的問題较曼。當(dāng)構(gòu)造函數(shù)中有靜態(tài)方法的時候,當(dāng)實例化一個構(gòu)造函數(shù)振愿,會在內(nèi)存中開辟一個新的內(nèi)存空間诗芜,此時會根據(jù)構(gòu)造函數(shù)中的靜態(tài)方法,再次開辟新的內(nèi)存空間埃疫,所以會造成內(nèi)存空間浪費的問題伏恐。
- 解決方法:使用構(gòu)造函數(shù)的原型對象
構(gòu)造函數(shù)原型 prototype
- 每一個構(gòu)造函數(shù)都有一個原型
prototype
屬性,指向另一個對象栓霜,這個prototype就是一個對象翠桦,這個對象的所有屬性和方法,都會被構(gòu)造函數(shù)所擁有胳蛮。 - 我們可以把一些不變的方法销凑,直接定義在prototype對象上,這樣所有對象的實例就可以共享這個方法仅炊。
- 一般情況下斗幼,我們會將公共的屬性定義到構(gòu)造函數(shù)里面,公共的方法會放到原型對象身上抚垄。
代碼示例
<script>
function Start(name, age) {
this.name = name;
this.age = age;
};
Start.prototype.sing = function() {
console.log("這是定義在prototype對象中的公共方法");
}
var start = new Start();
start.sing()
</script>
__proto__
蜕窿,它是一個指針谋逻,指向構(gòu)造函數(shù)中的protottype原型對象,之所有我們對象可以使用構(gòu)造函數(shù)中的prototype
原型對象的屬性和方法桐经,就是應(yīng)為有__proto__
原型的存在毁兆。__proto__
屬性,指向我們構(gòu)造函數(shù)的原型對象阴挣。__proto__
對象原型和原型對象 prototyte
是等價的气堕。__proto__
對象原型的意義就在于為對象的查找機(jī)制提供一個方法,或者說一條路線畔咧,但是他是一個非標(biāo)準(zhǔn)屬性茎芭,因為實際開發(fā)中,不可以使用這個屬性誓沸,它只是內(nèi)部指向原型對象 prototype
骗爆。對象原型(__proto__
)和構(gòu)造函數(shù)(prototype
)原型對象里面都有一個 consturctor
屬性,consturctor
我們稱為構(gòu)造功函數(shù)蔽介,因為他返回構(gòu)造函數(shù)本身摘投。
consturctor
主要用于記錄該對象引用了那個構(gòu)造函數(shù),它可以讓原型對象重新指向原來的構(gòu)造函數(shù)虹蓄。
構(gòu)造函數(shù)犀呼、實例、原型對象三者之間的關(guān)系
- 構(gòu)造函數(shù)有一個屬性
prototype
薇组,屬性值是prototype原型對象
- 原型對象
prototype
中有一個consturctor
屬性外臂,它的屬性值是構(gòu)造函數(shù)本身。 - 通過
new
創(chuàng)建的實例對象律胀,有一個__proto__
屬性宋光,它是一個指針,指向的是構(gòu)造函數(shù)中的prototype
屬性炭菌。但是他是一個非標(biāo)準(zhǔn)的屬性罪佳,所以在實際開發(fā)中一般不寫這個屬性名,而是直接使用它指向的prototype
中的方法黑低。所以可以直接使用通過 實例對象打點調(diào)用 構(gòu)造函數(shù)中prototype
中的方法赘艳。
解決構(gòu)造函數(shù)內(nèi)存浪費的方法
- JavaScript規(guī)定,每一個構(gòu)造函數(shù)都有一個
prototype
屬性克握,指向構(gòu)造函數(shù)的原型對象蕾管,這個原型對象中所擁有的屬性和方法,都會被構(gòu)造函數(shù)的實例對象所擁有菩暗。因為實例對象__proot__
屬性的存在掰曾。 - 因此我們可以把所有對象實例所需要共享的屬性和方法直接定義在
prototype
對象上,以此來解決內(nèi)存浪費問題停团。
原型鏈查找機(jī)制
- 當(dāng)實例對象調(diào)用構(gòu)造函數(shù)中的方法的時候旷坦,首先會看實例對象本身是否具有該方法掏熬,如果有,就執(zhí)行實例對象身上的方法契讲。
- 如果實例對象本身沒有要執(zhí)行的方法铅歼,因為有
__proto__
屬性的存在,會指向到構(gòu)造函數(shù)中的prototype
對象,就會去構(gòu)造函數(shù)中的prototype
對象中查找要執(zhí)行的方法容达。
實例對象讀寫原型對象成員
讀取:
- 先在自己身上找测砂,找到即返回苇羡。
- 如果在自己身上找不到,就會沿著原型鏈向上查找婉烟,找到即返回娩井。
- 如果一直查找到原型鏈的末端還沒有找到,則返回undefined似袁。
添加:
- 通過實例對象打點的方式添加新成員洞辣,會直接添加給自己,會屏蔽對原型對象的訪問昙衅。
- 如果通過實例對象直接打點修改原型對象上的屬性和方法的時候扬霜,也是會直接添加給自己,屏蔽掉對原型對象的訪問而涉。
- 更改復(fù)雜數(shù)據(jù)內(nèi)容的時候著瓶,則會按照原型鏈的查找機(jī)制進(jìn)行查找數(shù)據(jù)和更改。比如原型對象中有一個 address 屬性啼县,他的屬性值是一個對象材原,對象中有 city屬性,通過是對象打點修改city屬性的時候季眷,會按照原型鏈的查找機(jī)制查找該屬性余蟹,如果能夠查到就進(jìn)行修改,如果查到就返回一個undefined子刮。
實例代碼:
function Person(name, age) {
this.name = name;
this.age = age;
};
Person.prototype.type = "human";
Person.prototype.address = {
"city": "北京"
};
var p = new Person("mike", 20);
// 修改type屬性
p.type = "dog"; // 不會修改原型對象中的type屬性客叉,會在構(gòu)造函數(shù)中添加一個type的屬性,屬性值為dog
// 添加屬性
p.sex = "man"; // 直接在構(gòu)造函數(shù)中添加一個sex屬性话告,屬性值為 man兼搏。
// 修改復(fù)雜的屬性
p.address.city = "上海"; // 會按照原型鏈的查找機(jī)制,在原型對象中找到address屬性沙郭,并且修改其中的city屬性佛呻。
原型對象添加屬性和方法的簡單語法
- 像原型對象中Tina加屬性和方法可以通過
構(gòu)造函數(shù).prototype.屬性/方法()
的方式添加,如果有多個屬性和放發(fā)的話病线,就會添加書寫多次吓著。為了減少不必要的輸入鲤嫡,可以通過對象字面量
的方法重寫原型對象。將構(gòu)造函數(shù).prototype
重置到一個新的對象绑莺。 - 注意:重寫原型對象會丟失
constructor
屬性暖眼,所以需要手動將constructor
指向正確的構(gòu)造函數(shù)。 - 代碼實例
- 一般在定義構(gòu)造函數(shù)的時候纺裁,可以根據(jù)成員的功能不同诫肠,分別進(jìn)行設(shè)置:
- 私有成員(一般是非函數(shù)):放到構(gòu)造函數(shù)中。
- 共享成員(一般就是函數(shù)):放到原型對象中欺缘。
function Person(name, age) {
this.name = name;
this.age = age;
};
// 重寫構(gòu)造函數(shù)中的原型對象栋豫。
Person.prototype = {
// 注意將 constructor 指向正確的構(gòu)造函數(shù)。
constructor: Person,
type : "human",
sayName : function() {
console.log(this.name);
}
};
var p = new Person("mike", 20);
對象中的繼承
構(gòu)造函數(shù)的屬性繼承
- 對象中的屬性繼承可以通過在子類中通過
call()
方法調(diào)用父類谚殊,通過改變 this 的指向丧鸯,將父類中的參數(shù)傳遞到子類中,完成對象中的屬性的繼承嫩絮。 - 示例代碼:
<script>
// 父類構(gòu)造函數(shù)
function Person(name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
};
// 子類構(gòu)造函數(shù)
function Student(name, age, sex, score) {
// 通過 call() 方法調(diào)用父類構(gòu)造函數(shù)丛肢,call() 方法中傳遞的this指向通過 new 關(guān)鍵字調(diào)用 Student 構(gòu)造函數(shù)時候生成的實例對象
// 所以當(dāng)通過 new 關(guān)鍵字 調(diào)用構(gòu)造函數(shù)的時候,Person 構(gòu)造函數(shù)中指向的this是指向的 Student 實例對象
// 所以 Student 構(gòu)造函數(shù)中 包含 父類構(gòu)造函數(shù)中的屬性剿干。
Person.call(this, name, age, sex);
this.score = score
};
console.log(new Student("zs", 18, "男", 100))
// 返回一個Student對象蜂怎,對象中包含 name, age,sex, score屬性
</script>
構(gòu)造函數(shù)中的方法繼承
在子類的原型對象上,繼承父類原型對象的方法可以通過對象拷貝繼承和原型繼承兩種方法進(jìn)行繼承怨愤。
-
方法一:對象拷貝繼承
- 通過對象拷貝的方法實際上通過
for in
循環(huán)派敷,將父類構(gòu)造函數(shù)中的perototype
對象中的方法賦值到 子類的perototype
對象中,但是在繼承的時候需要注意不能繼承 父類的constructor
撰洗。
- 通過對象拷貝的方法實際上通過
-
方法二:原型繼承
- 原型繼承實際是讓子類實例對象的
prototype
對象指向父類的實例對象篮愉,當(dāng)在子類中調(diào)用父類的方法的時候,會先在子類中查找該方法差导,如果子類中沒有該方法试躏,則會通過原型鏈查找原型對象中的方法。所以可以繼承父類的方法设褐。 - 需要注意的是颠蕴,當(dāng)使用原型繼承的時候,需要重新修改子類的
constructor
屬性助析,因為如果修改子類的原型對象為父類的實例對象犀被,則只有父類中的constructor
,并且指向的是父類的構(gòu)造函數(shù)外冀。所以需要自己添加寡键。
- 原型繼承實際是讓子類實例對象的
實例代碼:
<script>
function Person(name, age, sex) {
this.name = name;
this.age = age;
this.sex = sex;
};
Person.prototype.sayHi = function() {
console.log("Hi");
};
function Student(name, age, sex, score) {
// 繼承父類的屬性
Person.call(this, name, age, sex);
// 設(shè)置自己的屬性
this.socre = score;
};
// 繼承父類的方法一:對象拷貝繼承
// for (let key in Person.prototype) {
// if (key === "constructor") {
// continue;
// };
// Student.prototype[key] = Person.prototype[key];
// };
// 繼承父類方法二:原型繼承
Student.prototype = new Person();
Student.prototype.constructor = Student;
let s = new Student();
console.log(s);
</script>
通過構(gòu)造函數(shù)定義 函數(shù)
- 函數(shù)本身也是一種對象,可以調(diào)用屬性和方法雪隧∥餍可以使用
Function
構(gòu)造函數(shù)员舵,通過 new 關(guān)鍵字的方法定義一個函數(shù)。 - 語法:let fun = new Function("參數(shù)1", "參數(shù)2")
- 參數(shù):通過構(gòu)造函數(shù)定義的函數(shù)藕畔,在傳遞參數(shù)的時候马僻,如果函數(shù)本身需要有參數(shù)傳遞,則在構(gòu)造函數(shù)中前面 參數(shù)1 的地方傳遞參數(shù)注服,如果需要傳遞多個參數(shù)韭邓,則依次在后面?zhèn)鬟f。如果函數(shù)本身不需要參數(shù)祠汇,則在構(gòu)造函數(shù)中只需要傳遞一個函數(shù)執(zhí)行式仍秤。注意熄诡,不管構(gòu)造函數(shù)中傳遞了多少參數(shù)可很,最后一個參數(shù)始終是函數(shù)的執(zhí)行式。
- 實例代碼:
let fun = new Function("a", "b", "let a = 1; console.log(a + b)")
fun("3", "4");
// 輸出結(jié)果:"14"
函數(shù)調(diào)用和 this 指向
- 普通函數(shù)凰浮,通過給函數(shù)名或者變量名添加 () 方式執(zhí)行我抠。
- 函數(shù)中的 this,默認(rèn)指向的是 window 對象袜茧。
function fun () {
console.log("1");
};
- 構(gòu)造函數(shù)菜拓,通過 new 關(guān)鍵字進(jìn)行調(diào)用。
- 構(gòu)造函數(shù)中的 this笛厦,指向的是調(diào)用構(gòu)造函數(shù)的實例對象纳鼎。
function Person(name) {
this.name = name;
};
let p = new Person("js");
- 對象中的方法,通過對象打點調(diào)用函數(shù)裳凸,然后加小括號贱鄙。
- 內(nèi)部的this 默認(rèn)指向的是調(diào)用的對象自己。
let o = {
sayHi = function() {
console.log("Hi");
};
};
- 事件函數(shù)姨谷,不需要加特殊的符號逗宁,只要事件被觸發(fā),會自動執(zhí)行函數(shù)梦湘。
- 事件函數(shù)的內(nèi)部瞎颗,this 指向的是調(diào)用事件函數(shù)的事件源。
document.onclick = function () {
console.log("事件函數(shù)")
};
- 定時器和延時器中的函數(shù)捌议,不需要加特殊的符號哼拔,只要執(zhí)行后,在規(guī)定的時間自動執(zhí)行瓣颅。
- 定時器和延時器中的 this倦逐,默認(rèn)指向的是 window 對象。
setInterval(function() {
console.log("定時器")
}, 1000);
- 注意:this 的指向是需要聯(lián)系執(zhí)行的上下文弄捕,在調(diào)用的時候僻孝,是按照什么方式調(diào)用导帝,指向是不一樣的。例如在外部定義了一個普通函數(shù)穿铆,在對象中創(chuàng)建了一個方法您单,屬性值是外部定義的函數(shù),當(dāng)通過對象調(diào)用方法的時候荞雏,此時這個函數(shù)中的this指向就是這個調(diào)用對象自己本身虐秦。
函數(shù)中的方法
call 方法
- call 方法可以指定函數(shù)的this,并且可以執(zhí)行函數(shù)并傳參凤优。
- 參數(shù):第一個參數(shù)悦陋,傳入一個指定讓 this 指向的對象,第二個參數(shù)及以后筑辨,是參數(shù)本身所需的函數(shù)俺驶。
- 語法:函數(shù)名.call(參數(shù)1, 參數(shù)2, 參數(shù)n)
- 返回值:返回函數(shù)自己本身的返回值。
- 代碼示例
function fun(a, b) {
console.log(this);
console.log(a + b)
};
o = {name: "zs"};
fun.call(o, 1,2)
// 輸出結(jié)果 this 指向 o棍辕,返回結(jié)果是3
應(yīng)用場景:利用數(shù)組中的push方法暮现,實現(xiàn)讓對象動態(tài)添加元素。
<script>
// 利用 call 方法楚昭,實現(xiàn) object 對象使用 push 方法
let o = {
0: 10,
length: 1
};
// 調(diào)用數(shù)組中的push方法栖袋,使用call方法修改 this 指向
Array.prototype.push.call(o, 20);
console.log(o);
</script>
apply 方法
- call 方法可以指定函數(shù)的this,并且可以執(zhí)行函數(shù)并傳參抚太。
- 參數(shù):第一個參數(shù)塘幅,傳入一個指定讓 this 指向的對象,第二個參數(shù)是函數(shù)的參數(shù)組成的數(shù)組尿贫,只能傳遞兩個參數(shù)电媳。
- 返回值:返回函數(shù)自己本身的返回值。
- 代碼示例
function fun(a, b) {
console.log(this);
console.log(a + b)
};
o = {name: "zs"};
fun.apply(o, [1,2])
// 輸出結(jié)果 this 指向 o帅霜,返回結(jié)果是3
bind 方法
- call 方法可以指定函數(shù)的this匆背,bind 方法不能執(zhí)行函數(shù),但是可以傳遞函數(shù)方法的參數(shù)
- 參數(shù):第一個參數(shù)身冀,傳入一個指定讓 this 指向的對象钝尸,第二個參數(shù)是函數(shù)執(zhí)行所需要的的參數(shù)。如果在執(zhí)行 bind 方法的時候沒有傳遞函數(shù)本身執(zhí)行所需要的參數(shù)搂根,在調(diào)用新的返回函數(shù)體的時候珍促,也可以繼續(xù)傳遞參數(shù)。
- 返回值:返回一個新的指定了 this 的函數(shù)剩愧,也可以叫綁定函數(shù)猪叙。
- 代碼示例
function fun(a, b) {
console.log(this);
console.log(a + b)
};
o = {name: "zs"};
let fn = fun.bind(o,2, 3);
fn();
// 在執(zhí)行fn的時候也可以傳遞參數(shù),如果在使用bind方法的時候傳遞了函數(shù)所需要的參數(shù),在執(zhí)行返回結(jié)果的時候傳遞參數(shù)不會覆蓋掉之前傳遞的參數(shù)穴翩,相當(dāng)于給執(zhí)行函數(shù)傳遞在參數(shù)后面又新增了參數(shù)犬第,也可以在使用bind方法的時候傳遞一部分參數(shù),然后在調(diào)用返回結(jié)果的時候再傳遞剩余參數(shù)芒帕。
// 輸出結(jié)果 this 指向 o歉嗓,返回結(jié)果是5
高階函數(shù)
- 描述:如果一個函數(shù)作為其他函數(shù)的參數(shù)或者返回值,那么稱之為高階函數(shù)背蟆。
閉包
- 函數(shù)在定義的時候鉴分,能夠記住自己生成的作用域環(huán)境和函數(shù)自己,將他們形成一個封閉的環(huán)境带膀,這就是閉包志珍。不論函數(shù)以任何方式任何地方進(jìn)行調(diào)用,都會回到自己定義時的密閉環(huán)境進(jìn)行執(zhí)行垛叨。
- 從廣義上來說伦糯,定義在全局的函數(shù)也是一個閉包,只是沒有辦法將這樣的函數(shù)拿到更外面的作用域進(jìn)行調(diào)用点额,從而觀察閉包的特點舔株。
- 閉包是天生存在的莺琳,不需額外的結(jié)構(gòu)还棱。
閉包的用途
- 可以再函數(shù)外部讀取函數(shù)內(nèi)部的成員。
- 讓函數(shù)內(nèi)成員始終存活在內(nèi)存中惭等。