對(duì)象
創(chuàng)建對(duì)象
- 方式1-字面量表示,推薦使用
var a = {};
- 方式2-調(diào)用構(gòu)造函數(shù),不推薦使用
var obj = new Object();
//Object是所有對(duì)象的基礎(chǔ)
- 方式3 亩钟,自定義方法,形式和函數(shù)一樣乓梨,為了區(qū)分,構(gòu)造函數(shù)命名嚴(yán)格按照大駝峰形式(所有單詞首字母大寫(xiě))
function Person(){}
var person = new Person();
Object的所有實(shí)例都有下列屬性和方法,因此所有的對(duì)象都有這些屬性和方法
- constructor
- hasOwnProperty(propertyName)
--->propertyName必須為字符串類(lèi)型 - propertyIsEnunerable(propertyName)
--->propertyName必須為字符串類(lèi)型 - isPrototypeOf(object)
- toString()
- valueOf()
- toLocaleString()
使用構(gòu)造函數(shù)創(chuàng)建對(duì)象模式
function Person(name,age,job){
this.name=name;
this.age=age;
this.job=job;
this.sayName=function(){
console.log(this.name);
};
}
var person1 = new Person('xiaoli',29,'doctor');
var person1 = new Person('xiaozhang',38,'teacher');
console.log(person1.constructor ==Person);//true
console.log(person2.constructor ==Person);//true
構(gòu)造函數(shù)原理
構(gòu)造函數(shù)和普通函數(shù)的唯一區(qū)別就是名字要首字母大寫(xiě)和要用new實(shí)例化
要?jiǎng)?chuàng)建Person的實(shí)例清酥,必須使用new扶镀,以這種方式調(diào)用構(gòu)造函數(shù)實(shí)際上會(huì)經(jīng)歷4個(gè)階段
- 創(chuàng)建一個(gè)新對(duì)象
- 新對(duì)象名字叫this(構(gòu)造函數(shù)的作用域賦予新對(duì)象)
- 執(zhí)行構(gòu)造函數(shù)中的代碼(為這個(gè)新對(duì)象添加屬性)
this.xxx=xxx;
- 返回新對(duì)象
return this
構(gòu)造函數(shù)的注意點(diǎn)
- 不同實(shí)例的同名函數(shù)是不相等的。
- 所以創(chuàng)建兩個(gè)完成同樣任務(wù)的Function實(shí)例沒(méi)有必要,況且由this對(duì)象在焰轻,根本不用再執(zhí)行代碼前就把函數(shù)綁定在特定對(duì)象上
- 把函數(shù)定義轉(zhuǎn)移到構(gòu)造函數(shù)外部來(lái)解決這個(gè)問(wèn)題
function Person(name,age,job){
this.name=name;
this.age=age;
this.job=job;
this.sayName=sayName;
}
function sayName(){
console.log(this.name);
}
var person1 = new Person('xiaoli',29,'doctor');
var person1 = new Person('xiaozhang',38,'teacher');
但是這樣又有新的問(wèn)題臭觉。
- 在全局作用域中定義的sayName只能被某個(gè)函數(shù)調(diào)用,
- 如果需要定義很多方法辱志,那就要定義很多全局函數(shù)蝠筑,沒(méi)有封裝性
- 通過(guò)原型模式來(lái)解決
原型模式
我們創(chuàng)建的每一個(gè)函數(shù)都有prototype屬性,prototype也是對(duì)象揩懒,是構(gòu)造函數(shù)制造出來(lái)的對(duì)象的公共祖先什乙,創(chuàng)造出來(lái)的原型可以繼承原型里面的屬性和方法
- JavaScript 的所有對(duì)象中都包含了一個(gè)
__proto__
內(nèi)部屬性,這個(gè)屬性所對(duì)應(yīng)的就是該對(duì)象的原型 - JavaScript 的函數(shù)對(duì)象已球,除了原型
__proto__
之外臣镣,還預(yù)置了 prototype 屬性 - 當(dāng)函數(shù)對(duì)象作為構(gòu)造函數(shù)創(chuàng)建實(shí)例時(shí)辅愿,該 prototype 屬性值將被作為實(shí)例對(duì)象的原型
__proto__
。
- prototype是通過(guò)調(diào)用構(gòu)造函數(shù)而創(chuàng)建的那個(gè)對(duì)象實(shí)例退疫,的原型對(duì)象
不必再構(gòu)造函數(shù)中定義對(duì)象實(shí)例的信息渠缕,而是將信息直接添加到原型對(duì)象中
function Person(){}
Person.prototype.name="lia";
Person.prototype.age=19;
Person.prototype.sayName=function(){
console.log(this.name);
};
var person1 = new Person();
var person2 = new Person();
person1.sayName();
person2.sayName();
理解原型對(duì)象
- 無(wú)論什么時(shí)候,只要?jiǎng)?chuàng)建了一個(gè)新函數(shù)褒繁,就會(huì)為該函數(shù)創(chuàng)建一個(gè)prototype屬性。這個(gè)屬性指向函數(shù)的原型對(duì)象
- 在默認(rèn)情況下馍忽,所有原型對(duì)象都會(huì)自動(dòng)獲得一個(gè)constructor(構(gòu)造函數(shù))屬性棒坏,這個(gè)屬性是一個(gè)指向prototype屬性所在函數(shù)的指針,
Person.prototype.constructor指向Person
- 創(chuàng)建一個(gè)自定義的構(gòu)造函數(shù)后遭笋,其原型對(duì)象只會(huì)默認(rèn)取得constructor屬性坝冕,至于其他方法,都是從Object繼承來(lái)的
- 構(gòu)造函數(shù)初始化時(shí)瓦呼,新建的this空對(duì)象其實(shí)不是空的喂窟,里面自帶一條
__proto__:Person.prototype
,用來(lái)指引實(shí)例函數(shù)尋找他的原型 - 在調(diào)用構(gòu)造函數(shù)創(chuàng)建一個(gè)新實(shí)例后,該實(shí)例的內(nèi)部將包含一個(gè)指針央串,指向該構(gòu)造函數(shù)的原型對(duì)象磨澡。es5管這個(gè)指針叫[[prototype]]
- 腳本中沒(méi)有標(biāo)準(zhǔn)的方法訪問(wèn)[[prototype]],但Firefox,safari和chrome在每個(gè)對(duì)象上都支持一個(gè)屬性proto,而在其他實(shí)現(xiàn)中质和,這個(gè)屬性對(duì)腳本則是完全不可見(jiàn)的稳摄。
- 這個(gè)鏈接存在于 實(shí)例函數(shù) 和 構(gòu)造函數(shù)的原型對(duì)象 之間,而不是存在于實(shí)例和構(gòu)造函數(shù)之間
- 函數(shù)才有prototype饲宿,實(shí)例對(duì)象只有有proto厦酬, 而函數(shù)有的proto是因?yàn)楹瘮?shù)是Function的實(shí)例對(duì)象
以以上 Person構(gòu)造函數(shù)為例
- Person.prototype指向了原型對(duì)象,而Person.prototype.cunstructor又指向了Person,
其他注意點(diǎn)
- 當(dāng)為對(duì)象實(shí)例添加一個(gè)屬性是瘫想,這個(gè)屬性會(huì) <em>屏蔽</em> 原型對(duì)象中保存的同名屬性仗阅。(添加這個(gè)同名屬性只會(huì)阻止我們?cè)L問(wèn)原型對(duì)象中的那個(gè)屬性,而不會(huì)修改那個(gè)屬性国夜。)减噪。即使這個(gè)屬性為null
- 但是,使用delete操作符可以完全刪除實(shí)例屬性支竹,從而可以可以重新訪問(wèn)原型中的屬性
原型鏈-使用proto連接
任意一個(gè)函數(shù)(包括構(gòu)造函數(shù))都有一個(gè)prototype屬性旋廷,指向該函數(shù)的原型對(duì)象,同樣任意一個(gè)構(gòu)造函數(shù)實(shí)例化的對(duì)象礼搁,都有一個(gè)proto屬性
Grande.prototype.lastname="你爺爺";
function Grande(){}
var grande = new Grande();
Father.prototype=grande;
function Father (){}
var father = new Father();
Son.prototype=father;
function Son (){}
var son = new Son();
console.log(son.lastname);//你爺爺
Grande.prototype.proto--->Object.prototype
- 絕大多數(shù)對(duì)象的最終饶碘,都會(huì)繼承自O(shè)bject.protype
var obj = Object.create(null)
這樣就沒(méi)有原型了,因?yàn)閚ull和undefined沒(méi)有toString方法
- 重寫(xiě)toString方法
Number.prototype.toString=function(){
return "你要返回的東西";
}
方法
isPrototypeOf()
console.log(Person.prototype.isPrototypeOf(person1));//true
console.log(Person.prototype.isPrototypeOf(person2));//true
//因?yàn)镻erson1和Person2內(nèi)部都有一個(gè)指向Person的指針馒吴,所以返回true
Object.getPrototypeOf
返回這個(gè)對(duì)象的原型
hasOwnPrototype()
一個(gè)屬性是存在實(shí)例中扎运,而不是存在于原型對(duì)象中瑟曲,返回ture,繼承自O(shè)bject
使用for-in循環(huán)是豪治,會(huì)循環(huán)實(shí)例和原型對(duì)象里面的屬性洞拨,
確認(rèn)屬性是原型中的屬性
function hasPrototypeProperty(object,name){
return (name in object) && !object.hasOwnPrototype(name);
}
//左邊的卡確認(rèn)是實(shí)例或者原型的屬性,左邊返回ture后负拟,右邊的如果是原型里面的屬性烦衣,則整體返回true
proto和prototype的聯(lián)系和區(qū)別
function a() {
//console.log("I'am a function.");
}
//b是實(shí)例化對(duì)象,a是構(gòu)造函數(shù)
var b = new a();
console.log(b.__proto__ == a.prototype); //true
//一個(gè)對(duì)象的隱式原型指向構(gòu)造該對(duì)象的構(gòu)造函數(shù)的原型掩浙,這也保證了實(shí)例能夠訪問(wèn)在構(gòu)造函數(shù)原型中定義的屬性和方法花吟。
console.log(Object.getPrototypeOf(b) == a.prototype); //true
區(qū)別:
- 區(qū)別:prototype是構(gòu)造函數(shù)訪問(wèn)原型對(duì)象,proto是對(duì)象實(shí)例訪問(wèn)原型對(duì)象厨姚。
- proto是每個(gè)對(duì)象都有的一個(gè)屬性衅澈,而prototype是函數(shù)才會(huì)有的屬性!!!
- 使用Object.getPrototypeOf()代替proto
注意: 函數(shù)才有prototype,實(shí)例對(duì)象只有有proto谬墙, 而函數(shù)有的proto是因?yàn)楹瘮?shù)是Function的實(shí)例對(duì)象
繼承
借用構(gòu)造函數(shù)法
在構(gòu)造函數(shù)中 使用Parent.call(this)
的方法繼承父類(lèi)屬性今布。
原理: 將子類(lèi)的this使用父類(lèi)的構(gòu)造函數(shù)跑一遍
缺點(diǎn): Parent原型鏈上的屬性和方法并不會(huì)被子類(lèi)繼承
function Parent() {
this.name = 'parent'
}
function Child() {
Parent.call(this);
this.type = 'child'
}
原型鏈實(shí)現(xiàn)繼承
原理:把子類(lèi)的prototype(原型對(duì)象)直接設(shè)置為父類(lèi)的實(shí)例
Son.prototype=new Father();
缺點(diǎn):當(dāng)子類(lèi)對(duì)象上進(jìn)行值修改時(shí),如果是修改的原始類(lèi)型的值拭抬,那么會(huì)在實(shí)例上新建這樣一個(gè)值部默;
但如果是引用類(lèi)型的話,他就會(huì)去修改子類(lèi)上唯一一個(gè)父類(lèi)實(shí)例里面的這個(gè)引用類(lèi)型玖喘,這會(huì)影響所有子類(lèi)實(shí)例
繼承圣杯模式
* 繼承圣杯模式
var inherit =(function(){
var F = function(){};//利用閉包甩牺,實(shí)現(xiàn)變量私有化
return function(Target,Origin){
F.prototype = Origin.prototype;
Target.prototype= new F();
//通過(guò)原型鏈連接
Targer.prototype.constructor = Target;
Target.prototype.uber = Origin.prototype;
}
}());
對(duì)象的遍歷
for in
var obj= {
name:123,
age:33,
__proto__:{
lastName:deng;
}
}
for(bar i in obj){
//為了不訪問(wèn)到原型里面的方法
if(obj.hasOwnProperty(i)){
console,log(i+":"+obj[i]);
}
}
//i是一個(gè)字符串累奈,如果寫(xiě)成obj.i,系統(tǒng)會(huì)轉(zhuǎn)換成obj['i']贬派,就成屬性讀取,澎媒,所以必須要寫(xiě)方括號(hào)搞乏,
//如果是系統(tǒng)自帶的原型,沒(méi)程序員設(shè)置戒努,他就不會(huì)打印出來(lái)请敦,。要程序員自己設(shè)置的原型储玫,才會(huì)被打印出來(lái)
object.name與object["name"]
- 使用方括號(hào)讀取對(duì)象屬性(替代點(diǎn)方式)-比較靈活
var obj = {
name:"xiaoli"
}
obj.name;//用這種方式訪問(wèn)侍筛,會(huì)在后臺(tái)轉(zhuǎn)換成方括號(hào)模式
obj['name'];
//這兩種方式,本質(zhì)來(lái)說(shuō)是一樣的撒穷,方括號(hào)比較靈活
- 應(yīng)用
var deng = {
wife1:{name:"1111"},
wife2:{name:"22222"},
wife3:{name:"3333"},
wife4:{name:"4444"},
sayWife:function(num){
console.log(deng['wife'+num]);
}
}
模擬Jquery實(shí)現(xiàn)鏈?zhǔn)骄幊?/h4>
var deng = {
smoke : function(){
console.log("smoking...");
return this
},
sayHi:function(){
console.log("hello");
return this;
}
}
deng.smoke().sayHi();
var deng = {
smoke : function(){
console.log("smoking...");
return this
},
sayHi:function(){
console.log("hello");
return this;
}
}
deng.smoke().sayHi();