昨天我們講了在面向對象中創建對象的幾種方式
- 工廠模式
- 構造函數模式
工廠模式創建的對象,像工廠一樣來創建對象,創建的每一個對象都是通過new Object()來創建的,原型直指Object()
構造函數似乎不錯,但有的時候我們需要對屬性和方法進行修改,屬性vue的同學應該都遇到過這種情況,我們需要聲明一些全局變量,我們一般這麼做
//封裝全局的ajax請求
Vue.prototype.$http = function (url, options) {
//部分代碼省略
return fetch(url, options)
}
// 注入 全局的wxSDK
Vue.prototype.$wx = Wx
這時候就用到了原型
我之前就用了大量篇幅講過javascript的原型,這次遇到了面向對象,換個角度再次討論
原型模式創建對象
我們創建的每個函數都有一個prototype(原型)屬性,這個屬性是一個指針,指向一個對象,而這個對象的用途是包含可以由特定類型的所有實例共享的屬性和方法。
使用原型對象的好處是可以讓所有對象實例共享它所包含的屬性和方法。
也就是說,不用再構造函數中定義對象的實例信息,而是將這些屬性和方法添加到原型對象中
一個🌰:
function Hero() {}
Hero.prototype.name = "歐陽鋒"
Hero.prototype.nickname = "西毒"
Hero.prototype.doSth = function () {
console.log('學習九陰真經');
}
const hero1 = new Hero()
console.log(hero1.name); //=>歐陽鋒
console.log(hero1.nickname); //=>西毒
hero1.doSth() //=>學習九陰真經
const hero2 = new Hero()
console.log(hero2.name); //=>歐陽鋒
console.log(hero2.nickname); //=>西毒
hero2.doSth() //=>學習九陰真經
我們看下hero1
console.log(hero1);
我們可以通過對象實例訪問保存在原型中的值,但是我們不能通過對象實例重寫原型中的值,強制的重寫也可以
function Hero() {
}
Hero.prototype.name = "歐陽鋒"
Hero.prototype.nickname = "西毒"
Hero.prototype.doSth = function () {
console.log('學習九陰真經');
}
const hero1 = new Hero()
hero1.__proto__.name='黃藥師'
console.log(hero1.name);//=>黃藥師
const hero2 = new Hero()
console.log(hero2.name);//=>黃藥師
這麼做就是修改了原型鏈,通過原型創建的其他實例也會同步更改,這樣不是我們所希望的結果
怎麼辦?
實例屬性屏蔽同名的原型屬性
在實例中添加屬性,使這個屬性和原型中的屬性同名,這個屬性會屏蔽掉(也可以理解爲覆蓋掉)原型中的那個屬性
繼續👆的🌰
function Hero() {
}
Hero.prototype.name = "歐陽鋒"
Hero.prototype.nickname = "西毒"
Hero.prototype.doSth = function () {
console.log('學習九陰真經');
}
const hero1 = new Hero()
const hero2 = new Hero()
hero1.name="黃藥師"
console.log(hero1.name);//=>黃藥師
console.log(hero2.name);//=>歐陽鋒
我們看下hero1
console.log(hero1);
黃藥師在實例中,歐陽鋒在原型中,
我們訪問hero1.name,會先在實例上去搜索name屬性,實例上存在name屬性,就返回實例上的name屬性,停止搜索。
我們再去訪問hero2.name,同樣的會在實例上進行搜索name屬性,但是沒搜到,繼續順着原型鏈進行搜索,搜到了,返回原型中的name屬性。
我們在原型中添加一個屬性,這個屬性會屏蔽掉原型中的同名屬性,也就是說會阻止我們訪問原型中的同名屬性,但是不會修改,如果我們把這個屬性設置爲null,會怎麼樣呢?
//部分代碼省略
const hero1 = new Hero()
const hero2 = new Hero()
hero1.name=null
console.log(hero1.name);//=>null(來自實例)
console.log(hero2.name);//=>歐陽鋒(來自原型)
所以說,就算設置爲null,結論是和上面是相同的,只要在實例上存在這個要訪問的屬性,就會停止搜索
實例中的屬性我不想要了怎麼辦,設置null也無效,使用delete刪除
//部分代碼省略
const hero1 = new Hero()
const hero2 = new Hero()
hero1.name=null
delete(hero1.name)//刪除實例中的屬性
console.log(hero1.name);//=>歐陽鋒(來自原型)
console.log(hero2.name);//=>歐陽鋒(來自原型)
判斷屬性屬於實例還是原型
有個內置方法hasOwnProperty()用來判斷屬性是否來自於實例,屬於實例則返回true,否則false
//部分代碼省略
const hero1 = new Hero()
const hero2 = new Hero()
hero1.name='黃藥師'
// console.log(hero1.name);
// console.log(hero2.name);
console.log(hero1.hasOwnProperty('name'));//=>true
console.log(hero2.hasOwnProperty('name'));//=>false
還有一個in操作符,如果對象能訪問到指定屬性就返回true
//部分代碼省略
const hero1 = new Hero()
const hero2 = new Hero()
hero1.name='黃藥師'
console.log('name' in hero1);//=>true
console.log('name' in hero2);//=>true
我們將in和hasOwnProperty()結合在一起,封裝一個方法用來判斷屬性來自原型還是實例
const hero1 = new Hero()
const hero2 = new Hero()
hero1.name = '黃藥師'
//true=>來自原型
//false=>來自實例
function attrFromProto(obj, attr) {
return !obj.hasOwnProperty(attr) && (attr in obj)
}
console.log(attrFromProto(hero1, 'name'));//=>false(來自實例)
console.log(attrFromProto(hero2, 'name'));//=>true(來自原型)
原型模式創建對象的簡化版
👆我們創建對象是這樣的
function Hero() {
}
Hero.prototype.name = "歐陽鋒"
Hero.prototype.nickname = "西毒"
Hero.prototype.doSth = function () {
console.log('學習九陰真經');
}
我能重複的寫prototype,我們結合對象字面量簡化一下
function Hero() {
}
Hero.prototype = {
name: "歐陽鋒",
nickname: "西毒",
doSth: function () {
console.log('學習九陰真經');
}
}
通過原型模式創建對象注意事項
在原型中添加公有方法是值得鼓勵的,類似於我們在vue中添加全局ajax,添加一下基本類型的變量也可以,但是當添加引用類型的屬性時候問題就出現了
function Hero() {
}
Hero.prototype.name = "歐陽鋒"
Hero.prototype.nickname = "西毒"
Hero.prototype.skill = ["靈蛇拳", "神駝雪山掌"]
Hero.prototype.doSth = function () {
console.log('學習九陰真經');
}
const hero1 = new Hero()
const hero2 = new Hero()
hero1.skill.push('靈蛇杖法')
console.log(hero1.skill);//=>["靈蛇拳", "神駝雪山掌", "靈蛇杖法"]
console.log(hero2.skill);//=>["靈蛇拳", "神駝雪山掌", "靈蛇杖法"]
我只在hero1實例中push了“靈蛇杖法”,結果影響到了hero2,
當在實例對象上添加引用類型時要格外小心
今天就到這裏,明天不見不散
收集整理了一些電子書,有需要的,在公衆號後臺回覆“電子書”即可領取