面向?qū)ο蠡靖拍?/h2>
- 面向?qū)ο?Object Oriented,OO)是軟件開發(fā)方法
- 面向?qū)ο笫且环N對(duì)現(xiàn)實(shí)世界抽象的理解,是計(jì)算機(jī)編程技術(shù)發(fā)展到一定階段后的產(chǎn)物
-
Object Oriented Programming-OOP ——面向?qū)ο缶幊?/p>
面向?qū)ο蠛兔嫦蜻^(guò)程區(qū)別
-
面向過(guò)程
- 強(qiáng)調(diào)的是功能行為
- 關(guān)注的是解決問(wèn)題需要哪些步驟
- 回想下前面我們完成一個(gè)需求的步驟:
- 首先搞清楚我們要做什么
- 然后分析怎么做
- 最后用代碼體現(xiàn)
- 一步一步去實(shí)現(xiàn),而具體的每一步都需要我們?nèi)?shí)現(xiàn)和操作
- 在上面每一個(gè)具體步驟中我們都是參與者担神, 并且需要面對(duì)具體的每一個(gè)步驟和過(guò)程, 這就是面向過(guò)程最直接的體現(xiàn)
面向?qū)ο笫腔诿嫦蜻^(guò)程而言
面向?qū)ο蠛兔嫦蜻^(guò)程都是一種思想
-
面向?qū)ο?/strong>
- 將功能封裝進(jìn)對(duì)象喂饥,強(qiáng)調(diào)具備了功能的對(duì)象
- 關(guān)注的是解決問(wèn)題需要哪些對(duì)象
當(dāng)需求單一消约, 或者簡(jiǎn)單時(shí)肠鲫, 我們一步一步去操作沒問(wèn)題, 并且效率也挺高或粮。 可隨著需求的更改导饲, 功能的增加, 發(fā)現(xiàn)需要面對(duì)每一個(gè)步驟非常麻煩氯材, 這時(shí)就開始思索渣锦, 能不能把這些步驟和功能再進(jìn)行封裝, 封裝時(shí)根據(jù)不同的功能氢哮,進(jìn)行不同的封裝袋毙,功能類似的封裝在一起。這樣結(jié)構(gòu)就清晰多了冗尤, 用的時(shí)候听盖, 找到對(duì)應(yīng)的類就可以了胀溺, 這就是面向?qū)ο笏枷?/p>
- 示例
- 買電腦
- 面向過(guò)程
- 了解電腦
- 了解自己的需求
- 對(duì)比參數(shù)
- 去電腦城
- 砍價(jià),付錢
- 買回電腦
- 被坑
- 面向?qū)ο?
- 找班長(zhǎng)
- 描述需求
- 班長(zhǎng)把電腦買回來(lái)
- 吃飯
- 面向過(guò)程
- 買菜
- 摘菜
- 洗菜
- 切菜
- 炒菜
- 盛菜
- 吃
- 面向?qū)ο?
- 去飯店
- 點(diǎn)菜
- 吃
- 洗衣服
- 面向過(guò)程
- 脫衣服
- 放進(jìn)盆里
- 放洗衣液
- 加水
- 放衣服
- 搓一搓
- 清一清
- 擰一擰
- 曬起來(lái)
- 面向?qū)ο?
- 脫衣服
- 打開洗衣機(jī)
- 丟進(jìn)去
- 一鍵洗衣烘干
- 終極面向?qū)ο?
- 買電腦/吃飯/洗衣服
- 找個(gè)對(duì)象
面向?qū)ο蟮奶攸c(diǎn)
- 是一種符合人們思考習(xí)慣的思想
- 可以將復(fù)雜的事情簡(jiǎn)單化
- 將程序員從執(zhí)行者轉(zhuǎn)換成了指揮者
- 完成需求時(shí):
- 先要去找具有所需的功能的對(duì)象來(lái)用
- 如果該對(duì)象不存在皆看,那么創(chuàng)建一個(gè)具有所需功能的對(duì)象
- 這樣簡(jiǎn)化開發(fā)并提高復(fù)用
類與對(duì)象的關(guān)系
- 面向?qū)ο蟮暮诵木褪菍?duì)象,那怎么創(chuàng)建對(duì)象?
- 現(xiàn)實(shí)生活中可以根據(jù)模板創(chuàng)建對(duì)象,編程語(yǔ)言也一樣,也必須先有一個(gè)模板,在這個(gè)模板中說(shuō)清楚將來(lái)創(chuàng)建出來(lái)的對(duì)象有哪些
屬性
和行為
- JavaScript中的類相當(dāng)于圖紙仓坞,用來(lái)描述一類事物。
- JavaScript中可以自定義類, 但是也提供了一個(gè)默認(rèn)的類叫做Object
使用默認(rèn)類創(chuàng)建對(duì)象
- 通過(guò) new Object() 創(chuàng)建對(duì)象
<script>
// 1.使用默認(rèn)類創(chuàng)建一個(gè)空對(duì)象
var obj = new Object()
// 2.動(dòng)態(tài)給空對(duì)象新增屬性
obj.name = "lnj";
obj.age = 33;
// 3.動(dòng)態(tài)給空對(duì)象新增方法
obj.say = function () {
console.log("hello");
}
// 4.使用對(duì)象的屬性和方法
console.log(obj.name);
console.log(obj.age);
obj.say();
</script>
- 通過(guò)字面量創(chuàng)建對(duì)象
<script>
/*
// 1.使用字面量創(chuàng)建對(duì)象
var obj = {}; // 相當(dāng)于var obj = new Object()
// 2.動(dòng)態(tài)給空對(duì)象新增屬性
obj.name = "lnj";
obj.age = 33;
// 3.動(dòng)態(tài)給空對(duì)象新增方法
obj.say = function () {
console.log("hello");
}
*/
// 1.使用字面量創(chuàng)建對(duì)象
var obj = {
name : 'lnj',
age: 33,
say : function () {
console.log("hello");
}
}
// 2.使用對(duì)象的屬性和方法
console.log(obj.name);
console.log(obj.age);
obj.say();
</script>
- 使用工廠函數(shù)創(chuàng)建對(duì)象
- 上面的創(chuàng)建方式, 沒多創(chuàng)建一個(gè)人都需要將代碼再寫一遍, 冗余代碼太多, 所以我們可以創(chuàng)建創(chuàng)建對(duì)象的代碼封裝到一個(gè)函數(shù)中
- 專門用于創(chuàng)建對(duì)象的函數(shù)我們稱之為工廠函數(shù)
<script>
function createPerson(name, age) {
var obj = new Object();
obj.name = name;
obj.age = age;
obj.say = function () {
console.log("hello");
}
return obj;
}
var obj1 = createPerson("lnj", 33);
var obj2 = createPerson("zq", 18);
console.log(obj1);
console.log(obj2);
</script>
<script>
function createPerson(name, age) {
var obj = {
name: name,
age: age,
say: function () {
console.log("hello");
}
}
return obj;
}
var obj1 = createPerson("lnj", 33);
var obj2 = createPerson("zq", 18);
console.log(obj1);
console.log(obj2);
</script>
函數(shù)中的this關(guān)鍵字
- 每個(gè)函數(shù)中都有一個(gè)this關(guān)鍵字, 誰(shuí)調(diào)用當(dāng)前函數(shù), this關(guān)鍵字就是誰(shuí)
<script>
function test() {
console.log(this);
}
// 默認(rèn)情況下直接調(diào)用的函數(shù)都是由window調(diào)用的
// 所以test函數(shù)中的this是window
test(); // 相當(dāng)于window.test();
var obj = new Object()
obj.name = "lnj";
obj.age = 33;
obj.say = function () {
console.log(this.name, this.age);
}
// 這里的say是一個(gè)方法, 方法必須通過(guò)對(duì)象來(lái)調(diào)用
// 所以這里的say方法是由obj對(duì)象調(diào)用的, 所以say方法中的this是obj對(duì)象
obj.say();
</script>
如何設(shè)計(jì)一個(gè)類
- 生活中描述事物無(wú)非就是描述事物的
屬性
和行為
腰吟。
- 如:人有身高无埃,體重等屬性,有說(shuō)話毛雇,打架等行為嫉称。
事物名稱(類名):人(Person)
屬性:身高(height)、年齡(age)
行為(功能):跑(run)禾乘、打架(fight)
- JavaScript中用類來(lái)描述事物也是如此
- 屬性:對(duì)應(yīng)類中的成員變量澎埠。
- 行為:對(duì)應(yīng)類中的成員方法。
- 定義類其實(shí)在定義類中的成員(成員變量和成員方法)
- 擁有相同或者類似
屬性
(狀態(tài)特征)和行為
(能干什么事)的對(duì)象都可以抽像成為一個(gè)類
如何分析一個(gè)類
- 一般名詞都是類(名詞提煉法)
如何定義一個(gè)類
- 在JavaScript中可以通過(guò)構(gòu)造函數(shù)來(lái)定義一個(gè)類
- 構(gòu)造函數(shù)也是一個(gè)函數(shù), 只不過(guò)函數(shù)的名稱必須大寫(帕斯卡命名)
- 構(gòu)造函數(shù)也是一個(gè)函數(shù), 只不過(guò)調(diào)用時(shí)必須通過(guò)
new
來(lái)調(diào)用
<script>
// 通過(guò)構(gòu)造函數(shù)定義一個(gè)類
// 在構(gòu)造函數(shù)中描述該類事物共性的屬性和行為
function Person(name, age) {
this.name = name;
this.age = age;
this.say = function () {
console.log(this.name, this.age);
}
}
</script>
如何通過(guò)類創(chuàng)建一個(gè)對(duì)象
- 不過(guò)就是創(chuàng)建結(jié)構(gòu)體的時(shí)候, 根據(jù)每個(gè)對(duì)象的特征賦值不同的屬性罷了
<script>
// 通過(guò)構(gòu)造函數(shù)定義一個(gè)類
// 在構(gòu)造函數(shù)中描述該類事物共性的屬性和行為
function Person(name, age) {
this.name = name;
this.age = age;
this.say = function () {
console.log(this.name, this.age);
}
}
// 通過(guò)構(gòu)造函數(shù)創(chuàng)建對(duì)象
// new 的執(zhí)行過(guò)程
// 1 在內(nèi)存中創(chuàng)建了一個(gè)空的對(duì)象
// 2 讓構(gòu)造函數(shù)中的this指向剛剛創(chuàng)建的對(duì)象
// 3 執(zhí)行構(gòu)造函數(shù)始藕,在構(gòu)造函數(shù)中設(shè)置屬性和方法(當(dāng)然也可以做其它事情)
// 4 返回了當(dāng)前對(duì)象
var p1 = new Person("lnj", 33);
var p2 = new Person("zq", 18);
p1.say();
p2.say();
</script>
- 與工廠函數(shù)的不同之處
- 不用我們自己手動(dòng)創(chuàng)建對(duì)象
- 不用我們手動(dòng)返回創(chuàng)建好的對(duì)象
- 構(gòu)造函數(shù)名稱首字母必須大寫
- 構(gòu)造函數(shù)必須通過(guò)new調(diào)用
構(gòu)造函數(shù)作用
- 使用構(gòu)造函數(shù)的好處不僅僅在于代碼的簡(jiǎn)潔性蒲稳,更重要的是我們可以識(shí)別對(duì)象的具體類型了
- 每個(gè)對(duì)象都可以訪問(wèn)一個(gè)名稱叫做
constructor
的屬性, 屬性指向創(chuàng)建該實(shí)例的構(gòu)造函數(shù)
<script>
var obj = new Object();
obj.name = "lnj";
console.log(obj); // Object
function Person(name) {
this.name = name;
}
var p = new Person("zs");
console.log(p); // Person
// 可以通過(guò)對(duì)象的constructor屬性判斷某個(gè)對(duì)象是否是某個(gè)類的實(shí)例
console.log(p.constructor === Person); // true
// 也可以通過(guò)instanceof判斷某個(gè)對(duì)象是否是某個(gè)類的實(shí)例
// 企業(yè)開發(fā)推薦
console.log(p instanceof Person);
</script>
構(gòu)造函數(shù)的內(nèi)存優(yōu)化問(wèn)題
- 每當(dāng)通過(guò)一個(gè)構(gòu)造函數(shù)創(chuàng)建一個(gè)對(duì)象, 就會(huì)在內(nèi)存中開辟一塊存儲(chǔ)空間, 該存儲(chǔ)空間中保存了對(duì)象的所有屬性和方法
- 如果構(gòu)造函數(shù)中的某些屬性或方法是需要變化的, 那么每份存儲(chǔ)空間中都應(yīng)該保存一份獨(dú)有, 但是如果某些屬性和方法是不變的, 那么每份存儲(chǔ)空間中都保存一份則造成了內(nèi)存浪費(fèi)
<script>
function Person(name, age) {
this.name = name;
this.age = age;
this.type = "人";
this.say = function () {
console.log(this.name, this.age, this.type);
}
}
// 所有對(duì)象的type屬性和say方法都是一樣的
// 但是還會(huì)在每個(gè)對(duì)象的存儲(chǔ)空間中都存儲(chǔ)一份
var p1 = new Person("lnj", 33);
var p2 = new Person("zs", 33);
console.log(p1.say === p2.say); // false
</script>
- 對(duì)于這種問(wèn)題我們可以把需要共享的函數(shù)定義到構(gòu)造函數(shù)外部
<script>
function say() {
console.log(this.name, this.age, this.type);
}
function Person(name, age) {
this.name = name;
this.age = age;
this.type = "人";
this.say = say;
}
var p1 = new Person("lnj", 33);
var p2 = new Person("zs", 33);
console.log(p1.say === p2.say); // true
</script>
- 但是如果有多個(gè)需要共享的函數(shù), 就會(huì)造成全局命名空間沖突的問(wèn)題(同一作用域不能出現(xiàn)同名的標(biāo)識(shí)符)
<script>
// 將共享函數(shù)封裝到一個(gè)對(duì)象中, 與外界隔絕, 這樣就不會(huì)污染全局命名空間了
var fns = {
say: function () {
console.log(this.name, this.age, this.type);
},
setName: function (name) {
this.name = name;
},
setAge: function (age) {
this.age = age;
}
}
function Person(name, age) {
this.name = name;
this.age = age;
this.type = "人";
this.say = fns.say;
this.setName = fns.setName;
this.setAge = fns.setAge;
}
var p1 = new Person("lnj", 33);
var p2 = new Person("zs", 33);
console.log(p1.say === p2.say); // true
</script>
構(gòu)造函數(shù)的內(nèi)存優(yōu)化問(wèn)題(更好的方案)
- JavaScript 規(guī)定,每一個(gè)構(gòu)造函數(shù)都有一個(gè)
prototype
屬性伍派,指向另一個(gè)對(duì)象江耀。
- 這個(gè)對(duì)象的所有屬性和方法,都會(huì)被構(gòu)造函數(shù)的所擁有
- 也就意味著诉植,我們可以把所有對(duì)象實(shí)例需要共享的屬性和方法直接定義在
prototype
對(duì)象上
<script>
function Person(name, age) {
this.name = name;
this.age = age;
}
// 給原型對(duì)象添加新的屬性和方法
Person.prototype.say = function () {
console.log(this.name, this.age, this.type);
};
Person.prototype.type = "人";
var p1 = new Person("lnj", 33);
var p2 = new Person("zs", 33);
console.log(p1.say === p2.say); // true
console.log(p1.type === p2.type); // true
// 當(dāng)調(diào)用對(duì)象的屬性或者方法的時(shí)候祥国,先去找對(duì)象本身的屬性/方法
// 如果對(duì)象沒有該屬性或者方法。此時(shí)去原型中查找對(duì)應(yīng)的屬性/方法
// 如果對(duì)象本身沒有該屬性/方法晾腔,原型中也沒有該屬性或者方法舌稀,此時(shí)會(huì)報(bào)錯(cuò)
p1.say();
console.log(p1.type);
</script>
- 任何函數(shù)都具有一個(gè) prototype 屬性,該屬性是一個(gè)對(duì)象
- 構(gòu)造函數(shù)的 prototype 對(duì)象默認(rèn)都有一個(gè) constructor 屬性灼擂,指向 prototype 對(duì)象所在函數(shù)
- 通過(guò)構(gòu)造函數(shù)得到的實(shí)例對(duì)象內(nèi)部會(huì)包含一個(gè)指向構(gòu)造函數(shù)的 prototype 對(duì)象的指針 proto
原型鏈
<script>
function Person(name, age) {
this.name = name;
this.age = age;
this.type = "超人";
}
// 給原型對(duì)象添加新的屬性和方法
Person.prototype.say = function () {
console.log(this.name, this.age, this.type);
};
Person.prototype.type = "人";
var p = new Person("lnj", 33);
console.log(p.type);
console.log(p.__proto__);
console.log(p.__proto__.__proto__);
console.log(p.__proto__.__proto__.__proto__);
</script>
- 方法查找規(guī)則
- 先查找當(dāng)前對(duì)象, 當(dāng)前對(duì)象有就使用當(dāng)前對(duì)象的方法
- 當(dāng)前對(duì)象沒有再逐層在原型鏈上查找, 最先找到那個(gè)就使用哪個(gè)
- 如果找到null都沒找到就報(bào)錯(cuò)
<script>
function Person(name, age) {
this.name = name;
this.age = age;
// this.say = function () {
// console.log("自己的", this.name, this.age, this.type);
// };
}
// 給原型對(duì)象添加新方法
// Person.prototype.say = function () {
// console.log("原型鏈上的", this.name, this.age, this.type);
// };
var p = new Person("lnj", 33);
// 自己有嗎? 有, 使用自己的
// 自己有嗎? 沒有, 去原型鏈查找
// 都沒有嗎? 報(bào)錯(cuò)
p.say();
</script>
- 屬性查找規(guī)則
- 先查找當(dāng)前對(duì)象, 當(dāng)前對(duì)象有就使用當(dāng)前對(duì)象的方法
- 當(dāng)前對(duì)象沒有再逐層在原型鏈上查找, 最先找到那個(gè)就使用哪個(gè)
- 如果找到null都沒找到就輸出undefined
<script>
function Person(name, age) {
this.name = name;
this.age = age;
// this.type = "超人";
}
// 給原型對(duì)象添加新的屬性和方法
// Person.prototype.type = "人";
var p = new Person("lnj", 33);
// 自己有嗎? 有, 使用自己的
// 自己有嗎? 沒有, 去原型鏈查找
// 都沒有嗎? undefined
console.log(p.type);
</script>
- 屬性的設(shè)置規(guī)則
- 不會(huì)修改原型鏈上的屬性, 會(huì)給當(dāng)前對(duì)象新增一個(gè)屬性
<script>
function Person(name, age) {
this.name = name;
this.age = age;
}
// 給原型對(duì)象添加新的屬性和方法
Person.prototype.type = "人";
var p = new Person("lnj", 33);
console.log(p.__proto__.type);
// p.__proto__.type = "超人";
// 自己有這個(gè)屬性嗎? 沒有, 新增
p.type = "超人";
console.log(p.__proto__.type);
</script>
自定義原型對(duì)象
- 原型對(duì)象是構(gòu)造函數(shù)的一個(gè)屬性, 所以我們可以通過(guò)修改屬性值的方式來(lái)自定義原型對(duì)象
- 需要注意的是, 自定義原型對(duì)象不能破壞原有的三角戀關(guān)系
<script>
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype = {
constructor: Person, // 手動(dòng)還原constructor屬性, 保持三角戀關(guān)系
type: "人",
say: function () {
console.log(this.name, this.age, this.type);
}
}
var p = new Person("lnj", 33);
p.say();
</script>
面向?qū)ο笕筇匦?/h2>
- 封裝性
-
封裝性就是隱藏實(shí)現(xiàn)細(xì)節(jié)壁查,僅對(duì)外公開接口
- 類是數(shù)據(jù)與功能的封裝,數(shù)據(jù)就是成員變量剔应,功能就是方法
- 為什么要封裝?
- 不封裝的缺點(diǎn):當(dāng)一個(gè)類把自己的成員變量暴露給外部的時(shí)候,那么該類就失去對(duì)該成員變量的管理權(quán)睡腿,別人可以任意的修改你的成員變量
- 封裝就是將數(shù)據(jù)隱藏起來(lái),只能用此類的方法才可以讀取或者設(shè)置數(shù)據(jù),不可被外部任意修改是面向?qū)ο笤O(shè)計(jì)本質(zhì)(
將變化隔離
)。這樣降低了數(shù)據(jù)被誤用的可能 (提高安全性
和靈活性
)
<script>
function Person(name) {
this.name = name; // 公有屬性
// 只能在內(nèi)部使用, 不能在外部使用
var age = 666; // 私有屬性
// 公有方法
this.say = function () {
console.log(this.name, age);
test();
}
this.setAge = function (num) {
age = num;
}
this.getAge = function () {
return age;
}
// 私有方法
function test() {
console.log("test");
}
}
var p = new Person("lnj");
console.log(p.name);
p.say();
console.log(p.age); // undefined
p.setAge(123);
console.log(p.getAge());
p.test(); // 報(bào)錯(cuò)
</script>
- 封裝原則
- 將不需要對(duì)外提供的內(nèi)容都隱藏起來(lái),把屬性都隱藏,提供公共的方法對(duì)其訪問(wèn)
實(shí)例屬性和實(shí)例方法/靜態(tài)屬性和靜態(tài)方法
- 通過(guò)構(gòu)造函數(shù)創(chuàng)建出來(lái)的對(duì)象訪問(wèn)的屬性和方法,我們稱之為實(shí)例屬性和實(shí)例方法
- 實(shí)例屬性和實(shí)例方法都是綁定在構(gòu)造函數(shù)創(chuàng)建出來(lái)的對(duì)象上的
<script>
function Person(name) {
this.name = name; // 實(shí)例屬性
this.eat = function () { // 實(shí)例方法
console.log("eat");
}
}
Person.prototype.age = "0"; // 實(shí)例屬性
Person.prototype.say = function () { // 實(shí)例方法
console.log("hello");
}
var p = new Person("lnj");
console.log(p.name); // 通過(guò)對(duì)象訪問(wèn)
console.log(p.age); // 通過(guò)對(duì)象訪問(wèn)
p.eat(); // 通過(guò)對(duì)象訪問(wèn)
p.say(); // 通過(guò)對(duì)象訪問(wèn)
</script>
- 通過(guò)構(gòu)造函數(shù)直接調(diào)用的屬性和方法,我們稱之為靜態(tài)屬性和靜態(tài)方法
- 靜態(tài)屬性和靜態(tài)方法都是綁定在構(gòu)造函數(shù)上的
<script>
function Person() {
Person.name = "lnj"; // 靜態(tài)屬性
Person.eat = function () { // 靜態(tài)方法
console.log("eat");
}
}
Person.count = 0; // 靜態(tài)屬性
Person.say = function () { // 靜態(tài)方法
console.log("hello");
};
console.log(Person.name); // 通過(guò)構(gòu)造函數(shù)訪問(wèn)
console.log(Person.count); // 通過(guò)構(gòu)造函數(shù)訪問(wèn)
Person.eat(); // 通過(guò)構(gòu)造函數(shù)訪問(wèn)
Person.say(); // 通過(guò)構(gòu)造函數(shù)訪問(wèn)
</script>
- 繼承性
- 兒子繼承父親的物品就是繼承最好的體現(xiàn)
- js中繼承目的: 把子類型中共同的屬性和方法提取到父類型中
-
較少代碼的冗余度, 提升代碼的復(fù)用性
- 借用原型鏈實(shí)現(xiàn)繼承
- 直接將子類的原型對(duì)象修改為父類對(duì)象, 這樣就能使用原型鏈上的屬性和方法
<script>
// 父類
function Person() {
this.name = "lnj";
this.age = 33;
this.gender = "male";
}
// 子類
function Student(score) {
this.score = score;
}
// 由于是直接將子類原型對(duì)象修改為了父類對(duì)象
// 所以繼承的屬性值很難自定義
Student.prototype = new Person();
Student.prototype.constructor = Student;
var stu1 = new Student(99);
console.log(stu.name, stu.age, stu.gender, stu.score);
var stu2 = new Student(66);
console.log(stu.name, stu.age, stu.gender, stu.score);
</script>
- 借用構(gòu)造函數(shù)實(shí)現(xiàn)繼承
- 在子類中調(diào)用父類構(gòu)造函數(shù), 并且將父類構(gòu)造函數(shù)的this修改為子類對(duì)象
<script>
// 父類
function Person(name, age, gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
// 借用構(gòu)造函數(shù)只是調(diào)用了父類的構(gòu)造函數(shù), 借用了構(gòu)造函數(shù)中的代碼
// 相當(dāng)于動(dòng)態(tài)的給子類添加了許多屬性, 但是并沒有修改子類的原型
// 所以子類無(wú)法繼承父類的方法
Person.prototype.say = function () {
console.log(this.name, this.age, this.gender);
}
// 子類
function Student(score, name, age, gender) {
Person.call(this, name, age, gender);
this.score = score;
}
var stu1 = new Student(99, "lnj", 33, "male");
var stu2 = new Student(66, "zq", 18, "female");
console.log(stu1.name, stu1.age, stu1.gender, stu1.score);
console.log(stu2.name, stu2.age, stu2.gender, stu2.score);
stu1.say(); // 報(bào)錯(cuò)
stu2.say(); // 報(bào)錯(cuò)
</script>
- 借用構(gòu)造函數(shù)+借用原型鏈組合繼承
- 通過(guò)借用構(gòu)造函數(shù)實(shí)現(xiàn)屬性繼承
- 通過(guò)借用原型鏈實(shí)現(xiàn)方法繼承
<script>
// 父類
function Person(name, age, gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
Person.prototype.say = function () {
console.log(this.name, this.age, this.gender);
}
// 子類
function Student(score, name, age, gender) {
Person.call(this, name, age, gender);
this.score = score;
}
Student.prototype = Person.prototype;
Student.prototype.constructor = Student;
// 由于子類的原型指向了父類的原型, 所以操作的都是同一個(gè)原型對(duì)象
// 給子類的原型新增方法或者屬性, 父類也會(huì)受到影響
Student.prototype.study = function () {
console.log("好好學(xué)習(xí)天天向上");
};
Student.prototype.type = "學(xué)生";
var stu = new Student(99, "lnj", 33, "male");
stu.say();
stu.study();
console.log(stu.type);
var p = new Person("zq", 18, "female");
p.say();
p.study();
console.log(p.type);
</script>
- 終極方案
<script>
// 父類
function Person(name, age, gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
Person.prototype.say = function () {
console.log(this.name, this.age, this.gender);
}
// 子類
function Student(score, name, age, gender) {
Person.call(this, name, age, gender);
this.score = score;
}
Student.prototype = new Person();
Student.prototype.constructor = Student;
// 由于子類的原型指向一個(gè)全新的對(duì)象
// 所以給子類的原型新增方法或者屬性, 父類不會(huì)受到影響
Student.prototype.study = function () {
console.log("好好學(xué)習(xí)天天向上");
};
Student.prototype.type = "學(xué)生";
var stu = new Student(99, "lnj", 33, "male");
stu.say();
stu.study();
console.log(stu.type);
var p = new Person("zq", 18, "female");
p.say();
p.study(); // 報(bào)錯(cuò)
console.log(p.type); // 報(bào)錯(cuò)
</script>
對(duì)象的增刪改查
<script>
// 1.定義構(gòu)造函數(shù)
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.type = "人";
// 2.利用構(gòu)造函數(shù)創(chuàng)建對(duì)象
var p1 = new Person("lnj", 33);
var p2 = new Person("zq", 18);
// 3.動(dòng)態(tài)增加屬性
p1.score = 99;
console.log(p1);
console.log(p2);
// 4.刪除某個(gè)屬性
delete p1.name;
console.log(p1);
console.log(p2);
// 5.修改某個(gè)屬性的值
p1.age = 66;
console.log(p1);
console.log(p2);
// 6.查詢是否包含某個(gè)屬性
// in 會(huì)到原型對(duì)象里面查找
console.log("type" in p1);
// hasOwnProperty: 只在對(duì)象自身查找
console.log(p1.hasOwnProperty("type"));
</script>
Object對(duì)象的bind-call-apply方法
- 默認(rèn)情況下所有對(duì)象都有bind-call-apply方法
- 這三個(gè)方法的作用是用于修改指定函數(shù)中this的指向
<script>
function sum(a, b) {
// 默認(rèn)情況下函數(shù)中的this誰(shuí)調(diào)用就是誰(shuí)
console.log(this);
console.log(a, b);
}
// test();
var obj = {
name: "zq"
};
// bind方法可以修改指定函數(shù)中this的指向
// 但是bind方法并不會(huì)調(diào)用函數(shù), 如果需要手動(dòng)調(diào)用
// sum.bind(obj)(10, 20);
// call方法也可以修改指定函數(shù)中this的指向
// 并且call方法會(huì)自動(dòng)調(diào)用函數(shù), 不用手動(dòng)調(diào)用
// sum.call(obj, 10, 20);
// apply方法也可以修改指定函數(shù)中this的指向
// 并且apply方法會(huì)自動(dòng)調(diào)用函數(shù), 不用手動(dòng)調(diào)用
// 和call方法的區(qū)別是傳遞參數(shù)的形式不同而已
sum.apply(obj,[10, 20]);
</script>
對(duì)象的拷貝
- 淺拷貝
- 對(duì)于基本類型屬性無(wú)論是深淺拷貝,都會(huì)復(fù)制一份
- 對(duì)于引用類型屬性淺拷貝拷貝的是引用類型的地址
- 簡(jiǎn)而言之, 淺拷貝修改引用類型屬性, 拷貝前拷貝后的對(duì)象都會(huì)受到影響
<script>
var obj1 = {
name: "lnj", // 基本類型屬性
age: 33, // 基本類型屬性
dog: { // 引用類型屬性
name: "wc",
age: 1,
}
};
var obj2 = {};
/*
for(var key in obj1){
obj2[key] = obj1[key];
}
*/
function copy(o1, o2){
for(var key in o1){
o2[key] = o1[key];
}
}
copy(obj1, obj2);
console.log(obj1);
console.log(obj2);
// 修改基本類型屬性, 不會(huì)影響原有對(duì)象
obj2.name = "zs";
console.log(obj1.name);
console.log(obj2.name);
// 修改引用類型屬性, 會(huì)影響原有對(duì)象
obj2.dog.name = "xq";
console.log(obj1.dog.name);
console.log(obj2.dog.name);
</script>
- 深拷貝
- 對(duì)于基本類型屬性無(wú)論是深淺拷貝,都會(huì)復(fù)制一份
- 對(duì)于引用類型屬性深拷貝會(huì)將引用類型復(fù)制一份
- 簡(jiǎn)而言之, 深拷貝修改引用類型屬性, 只會(huì)影響當(dāng)前修改對(duì)象
<script>
var obj1 = {
name: "lnj", // 基本類型屬性
age: 33, // 基本類型屬性
dog: { // 引用類型屬性
name: "wc",
age: 1,
}
};
var obj2 = {};
function copy(o1, o2){
for(var key in o1){
var item = o1[key];
// 判斷當(dāng)前屬性是否是引用類型
if(item instanceof Object){
// 創(chuàng)建一個(gè)新引用類型屬性
var o = {};
o2[key] = o;
// 進(jìn)一步拷貝引用類型屬性
copy(item, o);
}
// 判斷當(dāng)前屬性是否數(shù)組
else if(item instanceof Array){
// 創(chuàng)建一個(gè)數(shù)組屬性
var arr = [];
o2[key] = arr;
// 進(jìn)一步拷貝數(shù)組屬性
copy(item, arr);
}
// 如果不是引用類型, 直接拷貝即可
else{
o2[key] = item;
}
}
}
copy(obj1, obj2);
console.log(obj1);
console.log(obj2);
// 修改基本類型屬性, 不會(huì)影響原有對(duì)象
obj2.name = "zs";
console.log(obj1.name);
console.log(obj2.name);
// 修改引用類型屬性, 會(huì)影響原有對(duì)象
obj2.dog.name = "xq";
console.log(obj1.dog.name);
console.log(obj2.dog.name);
</script>
Object Oriented Programming-OOP ——面向?qū)ο缶幊?/p>
- 強(qiáng)調(diào)的是功能行為
- 關(guān)注的是解決問(wèn)題需要哪些步驟
- 首先搞清楚我們要做什么
- 然后分析怎么做
- 最后用代碼體現(xiàn)
- 一步一步去實(shí)現(xiàn),而具體的每一步都需要我們?nèi)?shí)現(xiàn)和操作
面向?qū)ο笫腔诿嫦蜻^(guò)程而言
面向?qū)ο蠛兔嫦蜻^(guò)程都是一種思想
面向?qū)ο?/strong>
- 將功能封裝進(jìn)對(duì)象喂饥,強(qiáng)調(diào)具備了功能的對(duì)象
- 關(guān)注的是解決問(wèn)題需要哪些對(duì)象
當(dāng)需求單一消约, 或者簡(jiǎn)單時(shí)肠鲫, 我們一步一步去操作沒問(wèn)題, 并且效率也挺高或粮。 可隨著需求的更改导饲, 功能的增加, 發(fā)現(xiàn)需要面對(duì)每一個(gè)步驟非常麻煩氯材, 這時(shí)就開始思索渣锦, 能不能把這些步驟和功能再進(jìn)行封裝, 封裝時(shí)根據(jù)不同的功能氢哮,進(jìn)行不同的封裝袋毙,功能類似的封裝在一起。這樣結(jié)構(gòu)就清晰多了冗尤, 用的時(shí)候听盖, 找到對(duì)應(yīng)的類就可以了胀溺, 這就是面向?qū)ο笏枷?/p>
- 面向過(guò)程
- 了解電腦
- 了解自己的需求
- 對(duì)比參數(shù)
- 去電腦城
- 砍價(jià),付錢
- 買回電腦
- 被坑
- 面向?qū)ο?
- 找班長(zhǎng)
- 描述需求
- 班長(zhǎng)把電腦買回來(lái)
- 面向過(guò)程
- 買菜
- 摘菜
- 洗菜
- 切菜
- 炒菜
- 盛菜
- 吃
- 面向?qū)ο?
- 去飯店
- 點(diǎn)菜
- 吃
- 面向過(guò)程
- 脫衣服
- 放進(jìn)盆里
- 放洗衣液
- 加水
- 放衣服
- 搓一搓
- 清一清
- 擰一擰
- 曬起來(lái)
- 面向?qū)ο?
- 脫衣服
- 打開洗衣機(jī)
- 丟進(jìn)去
- 一鍵洗衣烘干
- 終極面向?qū)ο?
- 買電腦/吃飯/洗衣服
- 找個(gè)對(duì)象
- 先要去找具有所需的功能的對(duì)象來(lái)用
- 如果該對(duì)象不存在皆看,那么創(chuàng)建一個(gè)具有所需功能的對(duì)象
- 這樣簡(jiǎn)化開發(fā)并提高復(fù)用
- 現(xiàn)實(shí)生活中可以根據(jù)模板創(chuàng)建對(duì)象,編程語(yǔ)言也一樣,也必須先有一個(gè)模板,在這個(gè)模板中說(shuō)清楚將來(lái)創(chuàng)建出來(lái)的對(duì)象有哪些
屬性
和行為
<script>
// 1.使用默認(rèn)類創(chuàng)建一個(gè)空對(duì)象
var obj = new Object()
// 2.動(dòng)態(tài)給空對(duì)象新增屬性
obj.name = "lnj";
obj.age = 33;
// 3.動(dòng)態(tài)給空對(duì)象新增方法
obj.say = function () {
console.log("hello");
}
// 4.使用對(duì)象的屬性和方法
console.log(obj.name);
console.log(obj.age);
obj.say();
</script>
<script>
/*
// 1.使用字面量創(chuàng)建對(duì)象
var obj = {}; // 相當(dāng)于var obj = new Object()
// 2.動(dòng)態(tài)給空對(duì)象新增屬性
obj.name = "lnj";
obj.age = 33;
// 3.動(dòng)態(tài)給空對(duì)象新增方法
obj.say = function () {
console.log("hello");
}
*/
// 1.使用字面量創(chuàng)建對(duì)象
var obj = {
name : 'lnj',
age: 33,
say : function () {
console.log("hello");
}
}
// 2.使用對(duì)象的屬性和方法
console.log(obj.name);
console.log(obj.age);
obj.say();
</script>
- 上面的創(chuàng)建方式, 沒多創(chuàng)建一個(gè)人都需要將代碼再寫一遍, 冗余代碼太多, 所以我們可以創(chuàng)建創(chuàng)建對(duì)象的代碼封裝到一個(gè)函數(shù)中
- 專門用于創(chuàng)建對(duì)象的函數(shù)我們稱之為工廠函數(shù)
<script>
function createPerson(name, age) {
var obj = new Object();
obj.name = name;
obj.age = age;
obj.say = function () {
console.log("hello");
}
return obj;
}
var obj1 = createPerson("lnj", 33);
var obj2 = createPerson("zq", 18);
console.log(obj1);
console.log(obj2);
</script>
<script>
function createPerson(name, age) {
var obj = {
name: name,
age: age,
say: function () {
console.log("hello");
}
}
return obj;
}
var obj1 = createPerson("lnj", 33);
var obj2 = createPerson("zq", 18);
console.log(obj1);
console.log(obj2);
</script>
<script>
function test() {
console.log(this);
}
// 默認(rèn)情況下直接調(diào)用的函數(shù)都是由window調(diào)用的
// 所以test函數(shù)中的this是window
test(); // 相當(dāng)于window.test();
var obj = new Object()
obj.name = "lnj";
obj.age = 33;
obj.say = function () {
console.log(this.name, this.age);
}
// 這里的say是一個(gè)方法, 方法必須通過(guò)對(duì)象來(lái)調(diào)用
// 所以這里的say方法是由obj對(duì)象調(diào)用的, 所以say方法中的this是obj對(duì)象
obj.say();
</script>
屬性
和行為
腰吟。
- 如:人有身高无埃,體重等屬性,有說(shuō)話毛雇,打架等行為嫉称。
事物名稱(類名):人(Person)
屬性:身高(height)、年齡(age)
行為(功能):跑(run)禾乘、打架(fight)
- 屬性:對(duì)應(yīng)類中的成員變量澎埠。
- 行為:對(duì)應(yīng)類中的成員方法。
屬性
(狀態(tài)特征)和行為
(能干什么事)的對(duì)象都可以抽像成為一個(gè)類new
來(lái)調(diào)用<script>
// 通過(guò)構(gòu)造函數(shù)定義一個(gè)類
// 在構(gòu)造函數(shù)中描述該類事物共性的屬性和行為
function Person(name, age) {
this.name = name;
this.age = age;
this.say = function () {
console.log(this.name, this.age);
}
}
</script>
<script>
// 通過(guò)構(gòu)造函數(shù)定義一個(gè)類
// 在構(gòu)造函數(shù)中描述該類事物共性的屬性和行為
function Person(name, age) {
this.name = name;
this.age = age;
this.say = function () {
console.log(this.name, this.age);
}
}
// 通過(guò)構(gòu)造函數(shù)創(chuàng)建對(duì)象
// new 的執(zhí)行過(guò)程
// 1 在內(nèi)存中創(chuàng)建了一個(gè)空的對(duì)象
// 2 讓構(gòu)造函數(shù)中的this指向剛剛創(chuàng)建的對(duì)象
// 3 執(zhí)行構(gòu)造函數(shù)始藕,在構(gòu)造函數(shù)中設(shè)置屬性和方法(當(dāng)然也可以做其它事情)
// 4 返回了當(dāng)前對(duì)象
var p1 = new Person("lnj", 33);
var p2 = new Person("zq", 18);
p1.say();
p2.say();
</script>
- 不用我們自己手動(dòng)創(chuàng)建對(duì)象
- 不用我們手動(dòng)返回創(chuàng)建好的對(duì)象
- 構(gòu)造函數(shù)名稱首字母必須大寫
- 構(gòu)造函數(shù)必須通過(guò)new調(diào)用
constructor
的屬性, 屬性指向創(chuàng)建該實(shí)例的構(gòu)造函數(shù)<script>
var obj = new Object();
obj.name = "lnj";
console.log(obj); // Object
function Person(name) {
this.name = name;
}
var p = new Person("zs");
console.log(p); // Person
// 可以通過(guò)對(duì)象的constructor屬性判斷某個(gè)對(duì)象是否是某個(gè)類的實(shí)例
console.log(p.constructor === Person); // true
// 也可以通過(guò)instanceof判斷某個(gè)對(duì)象是否是某個(gè)類的實(shí)例
// 企業(yè)開發(fā)推薦
console.log(p instanceof Person);
</script>
<script>
function Person(name, age) {
this.name = name;
this.age = age;
this.type = "人";
this.say = function () {
console.log(this.name, this.age, this.type);
}
}
// 所有對(duì)象的type屬性和say方法都是一樣的
// 但是還會(huì)在每個(gè)對(duì)象的存儲(chǔ)空間中都存儲(chǔ)一份
var p1 = new Person("lnj", 33);
var p2 = new Person("zs", 33);
console.log(p1.say === p2.say); // false
</script>
<script>
function say() {
console.log(this.name, this.age, this.type);
}
function Person(name, age) {
this.name = name;
this.age = age;
this.type = "人";
this.say = say;
}
var p1 = new Person("lnj", 33);
var p2 = new Person("zs", 33);
console.log(p1.say === p2.say); // true
</script>
<script>
// 將共享函數(shù)封裝到一個(gè)對(duì)象中, 與外界隔絕, 這樣就不會(huì)污染全局命名空間了
var fns = {
say: function () {
console.log(this.name, this.age, this.type);
},
setName: function (name) {
this.name = name;
},
setAge: function (age) {
this.age = age;
}
}
function Person(name, age) {
this.name = name;
this.age = age;
this.type = "人";
this.say = fns.say;
this.setName = fns.setName;
this.setAge = fns.setAge;
}
var p1 = new Person("lnj", 33);
var p2 = new Person("zs", 33);
console.log(p1.say === p2.say); // true
</script>
prototype
屬性伍派,指向另一個(gè)對(duì)象江耀。prototype
對(duì)象上<script>
function Person(name, age) {
this.name = name;
this.age = age;
}
// 給原型對(duì)象添加新的屬性和方法
Person.prototype.say = function () {
console.log(this.name, this.age, this.type);
};
Person.prototype.type = "人";
var p1 = new Person("lnj", 33);
var p2 = new Person("zs", 33);
console.log(p1.say === p2.say); // true
console.log(p1.type === p2.type); // true
// 當(dāng)調(diào)用對(duì)象的屬性或者方法的時(shí)候祥国,先去找對(duì)象本身的屬性/方法
// 如果對(duì)象沒有該屬性或者方法。此時(shí)去原型中查找對(duì)應(yīng)的屬性/方法
// 如果對(duì)象本身沒有該屬性/方法晾腔,原型中也沒有該屬性或者方法舌稀,此時(shí)會(huì)報(bào)錯(cuò)
p1.say();
console.log(p1.type);
</script>
<script>
function Person(name, age) {
this.name = name;
this.age = age;
this.type = "超人";
}
// 給原型對(duì)象添加新的屬性和方法
Person.prototype.say = function () {
console.log(this.name, this.age, this.type);
};
Person.prototype.type = "人";
var p = new Person("lnj", 33);
console.log(p.type);
console.log(p.__proto__);
console.log(p.__proto__.__proto__);
console.log(p.__proto__.__proto__.__proto__);
</script>
- 先查找當(dāng)前對(duì)象, 當(dāng)前對(duì)象有就使用當(dāng)前對(duì)象的方法
- 當(dāng)前對(duì)象沒有再逐層在原型鏈上查找, 最先找到那個(gè)就使用哪個(gè)
- 如果找到null都沒找到就報(bào)錯(cuò)
<script>
function Person(name, age) {
this.name = name;
this.age = age;
// this.say = function () {
// console.log("自己的", this.name, this.age, this.type);
// };
}
// 給原型對(duì)象添加新方法
// Person.prototype.say = function () {
// console.log("原型鏈上的", this.name, this.age, this.type);
// };
var p = new Person("lnj", 33);
// 自己有嗎? 有, 使用自己的
// 自己有嗎? 沒有, 去原型鏈查找
// 都沒有嗎? 報(bào)錯(cuò)
p.say();
</script>
- 先查找當(dāng)前對(duì)象, 當(dāng)前對(duì)象有就使用當(dāng)前對(duì)象的方法
- 當(dāng)前對(duì)象沒有再逐層在原型鏈上查找, 最先找到那個(gè)就使用哪個(gè)
- 如果找到null都沒找到就輸出undefined
<script>
function Person(name, age) {
this.name = name;
this.age = age;
// this.type = "超人";
}
// 給原型對(duì)象添加新的屬性和方法
// Person.prototype.type = "人";
var p = new Person("lnj", 33);
// 自己有嗎? 有, 使用自己的
// 自己有嗎? 沒有, 去原型鏈查找
// 都沒有嗎? undefined
console.log(p.type);
</script>
- 不會(huì)修改原型鏈上的屬性, 會(huì)給當(dāng)前對(duì)象新增一個(gè)屬性
<script>
function Person(name, age) {
this.name = name;
this.age = age;
}
// 給原型對(duì)象添加新的屬性和方法
Person.prototype.type = "人";
var p = new Person("lnj", 33);
console.log(p.__proto__.type);
// p.__proto__.type = "超人";
// 自己有這個(gè)屬性嗎? 沒有, 新增
p.type = "超人";
console.log(p.__proto__.type);
</script>
<script>
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype = {
constructor: Person, // 手動(dòng)還原constructor屬性, 保持三角戀關(guān)系
type: "人",
say: function () {
console.log(this.name, this.age, this.type);
}
}
var p = new Person("lnj", 33);
p.say();
</script>
- 封裝性
-
封裝性就是隱藏實(shí)現(xiàn)細(xì)節(jié)壁查,僅對(duì)外公開接口
- 類是數(shù)據(jù)與功能的封裝,數(shù)據(jù)就是成員變量剔应,功能就是方法
-
- 為什么要封裝?
- 不封裝的缺點(diǎn):當(dāng)一個(gè)類把自己的成員變量暴露給外部的時(shí)候,那么該類就失去對(duì)該成員變量的管理權(quán)睡腿,別人可以任意的修改你的成員變量
- 封裝就是將數(shù)據(jù)隱藏起來(lái),只能用此類的方法才可以讀取或者設(shè)置數(shù)據(jù),不可被外部任意修改是面向?qū)ο笤O(shè)計(jì)本質(zhì)(
將變化隔離
)。這樣降低了數(shù)據(jù)被誤用的可能 (提高安全性
和靈活性
)
<script>
function Person(name) {
this.name = name; // 公有屬性
// 只能在內(nèi)部使用, 不能在外部使用
var age = 666; // 私有屬性
// 公有方法
this.say = function () {
console.log(this.name, age);
test();
}
this.setAge = function (num) {
age = num;
}
this.getAge = function () {
return age;
}
// 私有方法
function test() {
console.log("test");
}
}
var p = new Person("lnj");
console.log(p.name);
p.say();
console.log(p.age); // undefined
p.setAge(123);
console.log(p.getAge());
p.test(); // 報(bào)錯(cuò)
</script>
- 封裝原則
- 將不需要對(duì)外提供的內(nèi)容都隱藏起來(lái),把屬性都隱藏,提供公共的方法對(duì)其訪問(wèn)
實(shí)例屬性和實(shí)例方法/靜態(tài)屬性和靜態(tài)方法
- 通過(guò)構(gòu)造函數(shù)創(chuàng)建出來(lái)的對(duì)象訪問(wèn)的屬性和方法,我們稱之為實(shí)例屬性和實(shí)例方法
- 實(shí)例屬性和實(shí)例方法都是綁定在構(gòu)造函數(shù)創(chuàng)建出來(lái)的對(duì)象上的
<script>
function Person(name) {
this.name = name; // 實(shí)例屬性
this.eat = function () { // 實(shí)例方法
console.log("eat");
}
}
Person.prototype.age = "0"; // 實(shí)例屬性
Person.prototype.say = function () { // 實(shí)例方法
console.log("hello");
}
var p = new Person("lnj");
console.log(p.name); // 通過(guò)對(duì)象訪問(wèn)
console.log(p.age); // 通過(guò)對(duì)象訪問(wèn)
p.eat(); // 通過(guò)對(duì)象訪問(wèn)
p.say(); // 通過(guò)對(duì)象訪問(wèn)
</script>
- 通過(guò)構(gòu)造函數(shù)直接調(diào)用的屬性和方法,我們稱之為靜態(tài)屬性和靜態(tài)方法
- 靜態(tài)屬性和靜態(tài)方法都是綁定在構(gòu)造函數(shù)上的
<script>
function Person() {
Person.name = "lnj"; // 靜態(tài)屬性
Person.eat = function () { // 靜態(tài)方法
console.log("eat");
}
}
Person.count = 0; // 靜態(tài)屬性
Person.say = function () { // 靜態(tài)方法
console.log("hello");
};
console.log(Person.name); // 通過(guò)構(gòu)造函數(shù)訪問(wèn)
console.log(Person.count); // 通過(guò)構(gòu)造函數(shù)訪問(wèn)
Person.eat(); // 通過(guò)構(gòu)造函數(shù)訪問(wèn)
Person.say(); // 通過(guò)構(gòu)造函數(shù)訪問(wèn)
</script>
- 繼承性
- 兒子繼承父親的物品就是繼承最好的體現(xiàn)
- js中繼承目的: 把子類型中共同的屬性和方法提取到父類型中
-
較少代碼的冗余度, 提升代碼的復(fù)用性
- 借用原型鏈實(shí)現(xiàn)繼承
- 直接將子類的原型對(duì)象修改為父類對(duì)象, 這樣就能使用原型鏈上的屬性和方法
<script>
// 父類
function Person() {
this.name = "lnj";
this.age = 33;
this.gender = "male";
}
// 子類
function Student(score) {
this.score = score;
}
// 由于是直接將子類原型對(duì)象修改為了父類對(duì)象
// 所以繼承的屬性值很難自定義
Student.prototype = new Person();
Student.prototype.constructor = Student;
var stu1 = new Student(99);
console.log(stu.name, stu.age, stu.gender, stu.score);
var stu2 = new Student(66);
console.log(stu.name, stu.age, stu.gender, stu.score);
</script>
- 借用構(gòu)造函數(shù)實(shí)現(xiàn)繼承
- 在子類中調(diào)用父類構(gòu)造函數(shù), 并且將父類構(gòu)造函數(shù)的this修改為子類對(duì)象
<script>
// 父類
function Person(name, age, gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
// 借用構(gòu)造函數(shù)只是調(diào)用了父類的構(gòu)造函數(shù), 借用了構(gòu)造函數(shù)中的代碼
// 相當(dāng)于動(dòng)態(tài)的給子類添加了許多屬性, 但是并沒有修改子類的原型
// 所以子類無(wú)法繼承父類的方法
Person.prototype.say = function () {
console.log(this.name, this.age, this.gender);
}
// 子類
function Student(score, name, age, gender) {
Person.call(this, name, age, gender);
this.score = score;
}
var stu1 = new Student(99, "lnj", 33, "male");
var stu2 = new Student(66, "zq", 18, "female");
console.log(stu1.name, stu1.age, stu1.gender, stu1.score);
console.log(stu2.name, stu2.age, stu2.gender, stu2.score);
stu1.say(); // 報(bào)錯(cuò)
stu2.say(); // 報(bào)錯(cuò)
</script>
- 借用構(gòu)造函數(shù)+借用原型鏈組合繼承
- 通過(guò)借用構(gòu)造函數(shù)實(shí)現(xiàn)屬性繼承
- 通過(guò)借用原型鏈實(shí)現(xiàn)方法繼承
<script>
// 父類
function Person(name, age, gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
Person.prototype.say = function () {
console.log(this.name, this.age, this.gender);
}
// 子類
function Student(score, name, age, gender) {
Person.call(this, name, age, gender);
this.score = score;
}
Student.prototype = Person.prototype;
Student.prototype.constructor = Student;
// 由于子類的原型指向了父類的原型, 所以操作的都是同一個(gè)原型對(duì)象
// 給子類的原型新增方法或者屬性, 父類也會(huì)受到影響
Student.prototype.study = function () {
console.log("好好學(xué)習(xí)天天向上");
};
Student.prototype.type = "學(xué)生";
var stu = new Student(99, "lnj", 33, "male");
stu.say();
stu.study();
console.log(stu.type);
var p = new Person("zq", 18, "female");
p.say();
p.study();
console.log(p.type);
</script>
- 終極方案
<script>
// 父類
function Person(name, age, gender) {
this.name = name;
this.age = age;
this.gender = gender;
}
Person.prototype.say = function () {
console.log(this.name, this.age, this.gender);
}
// 子類
function Student(score, name, age, gender) {
Person.call(this, name, age, gender);
this.score = score;
}
Student.prototype = new Person();
Student.prototype.constructor = Student;
// 由于子類的原型指向一個(gè)全新的對(duì)象
// 所以給子類的原型新增方法或者屬性, 父類不會(huì)受到影響
Student.prototype.study = function () {
console.log("好好學(xué)習(xí)天天向上");
};
Student.prototype.type = "學(xué)生";
var stu = new Student(99, "lnj", 33, "male");
stu.say();
stu.study();
console.log(stu.type);
var p = new Person("zq", 18, "female");
p.say();
p.study(); // 報(bào)錯(cuò)
console.log(p.type); // 報(bào)錯(cuò)
</script>
對(duì)象的增刪改查
<script>
// 1.定義構(gòu)造函數(shù)
function Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.type = "人";
// 2.利用構(gòu)造函數(shù)創(chuàng)建對(duì)象
var p1 = new Person("lnj", 33);
var p2 = new Person("zq", 18);
// 3.動(dòng)態(tài)增加屬性
p1.score = 99;
console.log(p1);
console.log(p2);
// 4.刪除某個(gè)屬性
delete p1.name;
console.log(p1);
console.log(p2);
// 5.修改某個(gè)屬性的值
p1.age = 66;
console.log(p1);
console.log(p2);
// 6.查詢是否包含某個(gè)屬性
// in 會(huì)到原型對(duì)象里面查找
console.log("type" in p1);
// hasOwnProperty: 只在對(duì)象自身查找
console.log(p1.hasOwnProperty("type"));
</script>
Object對(duì)象的bind-call-apply方法
- 默認(rèn)情況下所有對(duì)象都有bind-call-apply方法
- 這三個(gè)方法的作用是用于修改指定函數(shù)中this的指向
<script>
function sum(a, b) {
// 默認(rèn)情況下函數(shù)中的this誰(shuí)調(diào)用就是誰(shuí)
console.log(this);
console.log(a, b);
}
// test();
var obj = {
name: "zq"
};
// bind方法可以修改指定函數(shù)中this的指向
// 但是bind方法并不會(huì)調(diào)用函數(shù), 如果需要手動(dòng)調(diào)用
// sum.bind(obj)(10, 20);
// call方法也可以修改指定函數(shù)中this的指向
// 并且call方法會(huì)自動(dòng)調(diào)用函數(shù), 不用手動(dòng)調(diào)用
// sum.call(obj, 10, 20);
// apply方法也可以修改指定函數(shù)中this的指向
// 并且apply方法會(huì)自動(dòng)調(diào)用函數(shù), 不用手動(dòng)調(diào)用
// 和call方法的區(qū)別是傳遞參數(shù)的形式不同而已
sum.apply(obj,[10, 20]);
</script>
對(duì)象的拷貝
- 淺拷貝
- 對(duì)于基本類型屬性無(wú)論是深淺拷貝,都會(huì)復(fù)制一份
- 對(duì)于引用類型屬性淺拷貝拷貝的是引用類型的地址
- 簡(jiǎn)而言之, 淺拷貝修改引用類型屬性, 拷貝前拷貝后的對(duì)象都會(huì)受到影響
<script>
var obj1 = {
name: "lnj", // 基本類型屬性
age: 33, // 基本類型屬性
dog: { // 引用類型屬性
name: "wc",
age: 1,
}
};
var obj2 = {};
/*
for(var key in obj1){
obj2[key] = obj1[key];
}
*/
function copy(o1, o2){
for(var key in o1){
o2[key] = o1[key];
}
}
copy(obj1, obj2);
console.log(obj1);
console.log(obj2);
// 修改基本類型屬性, 不會(huì)影響原有對(duì)象
obj2.name = "zs";
console.log(obj1.name);
console.log(obj2.name);
// 修改引用類型屬性, 會(huì)影響原有對(duì)象
obj2.dog.name = "xq";
console.log(obj1.dog.name);
console.log(obj2.dog.name);
</script>
- 深拷貝
- 對(duì)于基本類型屬性無(wú)論是深淺拷貝,都會(huì)復(fù)制一份
- 對(duì)于引用類型屬性深拷貝會(huì)將引用類型復(fù)制一份
- 簡(jiǎn)而言之, 深拷貝修改引用類型屬性, 只會(huì)影響當(dāng)前修改對(duì)象
<script>
var obj1 = {
name: "lnj", // 基本類型屬性
age: 33, // 基本類型屬性
dog: { // 引用類型屬性
name: "wc",
age: 1,
}
};
var obj2 = {};
function copy(o1, o2){
for(var key in o1){
var item = o1[key];
// 判斷當(dāng)前屬性是否是引用類型
if(item instanceof Object){
// 創(chuàng)建一個(gè)新引用類型屬性
var o = {};
o2[key] = o;
// 進(jìn)一步拷貝引用類型屬性
copy(item, o);
}
// 判斷當(dāng)前屬性是否數(shù)組
else if(item instanceof Array){
// 創(chuàng)建一個(gè)數(shù)組屬性
var arr = [];
o2[key] = arr;
// 進(jìn)一步拷貝數(shù)組屬性
copy(item, arr);
}
// 如果不是引用類型, 直接拷貝即可
else{
o2[key] = item;
}
}
}
copy(obj1, obj2);
console.log(obj1);
console.log(obj2);
// 修改基本類型屬性, 不會(huì)影響原有對(duì)象
obj2.name = "zs";
console.log(obj1.name);
console.log(obj2.name);
// 修改引用類型屬性, 會(huì)影響原有對(duì)象
obj2.dog.name = "xq";
console.log(obj1.dog.name);
console.log(obj2.dog.name);
</script>