- prototype原型對(duì)象
無(wú)論什么時(shí)候,只要?jiǎng)?chuàng)建了一個(gè)新函數(shù)告抄,就會(huì)根據(jù)一組特定的規(guī)則為該函數(shù)創(chuàng)建一個(gè) prototype 屬性prototype既是屬性也是對(duì)象(事實(shí)上撰茎,他是一個(gè)指向某個(gè) 叫做 “原型對(duì)象"的對(duì)象 的屬性)
這個(gè)原型對(duì)象的用途就是去包含所有共享的屬性和方法。例子是Array(Array是一個(gè)構(gòu)造數(shù)組的函數(shù)打洼,創(chuàng)建數(shù)組時(shí)被執(zhí)行)的slice就存在這里面
簡(jiǎn)單來(lái)說(shuō):prototype是一個(gè)容器龄糊,存著這個(gè)函數(shù)所有將共享的屬性和方法。
值得注意的是 :prototype其實(shí)是一個(gè)函數(shù)對(duì)象
- __ proto__隱式原型
js萬(wàn)物皆對(duì)象募疮,這些對(duì)象都有__ proto__屬性炫惩,即:對(duì)象具有屬性__ proto__,可稱(chēng)為隱式原型阿浓。
一個(gè)對(duì)象的隱式原型指向構(gòu)造該對(duì)象的構(gòu)造函數(shù)的原型(下文通過(guò)new論證如何實(shí)現(xiàn))這也保證了實(shí)例能夠訪(fǎng)問(wèn)在構(gòu)造函數(shù)原型中定義的屬性和方法他嚷。(就像下面自定義屬性和方法那樣)
__ proto__指向這個(gè)實(shí)例的構(gòu)造器的原型即prototype,好比數(shù)組的__ proto__就指向了Array的原型芭毙,因此我們可以通過(guò)給Array的原型添加新的方法(比如切割)來(lái)玩弄數(shù)組爸舒。
為什么只能是Array而非我自己的構(gòu)造器呢?
因?yàn)槌悄悴挥肁rray這個(gè)構(gòu)造器來(lái)建立數(shù)組稿蹲,否則這個(gè)數(shù)組的__ proto__顯然是指向Array這個(gè)構(gòu)造器的原型而非是你的。所以理論上你如果可以用你自己的構(gòu)造器新建數(shù)組的話(huà)鹊奖,那這是ok的
特殊:函數(shù)本身的構(gòu)造器是Function苛聘,于是函數(shù)的__ proto__指向Function.prototype(寫(xiě)在js內(nèi)部)
通過(guò)constructor(暫不知why) 我們還可繼續(xù)為原型對(duì)象添 加其他屬性和方法 (如下方2.1)
- 構(gòu)造函數(shù):一個(gè)返回this對(duì)象的函數(shù)
構(gòu)造函數(shù)和普通函數(shù)的區(qū)別在于:調(diào)用方式不一樣。作用也不一樣(構(gòu)造函數(shù)用來(lái)新建實(shí)例對(duì)象)配合new可以實(shí)現(xiàn)自動(dòng)化
構(gòu)造函數(shù)的函數(shù)名與類(lèi)名相同:Person( ) 這個(gè)構(gòu)造函數(shù)忠聚,Person 既是函數(shù)名设哗,也是這個(gè)對(duì)象的類(lèi)名
內(nèi)部用this 來(lái)構(gòu)造屬性和方法
構(gòu)造流程
let Parent = function (name, age) {
//1.創(chuàng)建一個(gè)新對(duì)象,賦予this两蟀,這一步是隱性的网梢,
// let this = {};
//2給this指向的對(duì)象賦予構(gòu)造屬性
this.name = name;
this.age = age;
//3.如果沒(méi)有手動(dòng)返回對(duì)象,則默認(rèn)返回this指向的這個(gè)對(duì)象赂毯,也是隱性的
// return this;
};
注意:上面的三步走并不是指這個(gè)函數(shù)會(huì)這么執(zhí)行战虏,而是new的時(shí)候幫它執(zhí)行這三步了
是new幫它return的 當(dāng)然 你可以手動(dòng)創(chuàng)建that 然后return
new的過(guò)程發(fā)生了什么
? 以構(gòu)造器的prototype屬性(即存放公共方法或?qū)傩缘哪莻€(gè)對(duì)象)為原型(即__ proto__)拣宰,創(chuàng)建新對(duì)象,即:還是個(gè)空對(duì)象時(shí)烦感,就有了那些公共屬性
? 將this(也就是上一句中的新對(duì)象)和調(diào)用參數(shù)傳給構(gòu)造器巡社,執(zhí)行;
? 如果構(gòu)造器沒(méi)有手動(dòng)返回對(duì)象手趣,則返回第一步創(chuàng)建的對(duì)象原型鏈
age是怎么找到的呢晌该,首先會(huì)去person1的私有屬性(本身的屬性),有沒(méi)有age绿渣,如果有的話(huà)就去這個(gè)屬性的值朝群,如果沒(méi)有就會(huì)查找person1.__ proto__指向上的對(duì)象(即Person.prototype)有沒(méi)有該屬性有的話(huà)就會(huì)返回該屬性的值,如果沒(méi)有接著查找person1.__ proto__.__ proto__* 即指向了Object.prototype (因?yàn)開(kāi)_ proto__也是由Object構(gòu)造器構(gòu)造的嘛)然后看有沒(méi)有你寫(xiě)的屬性或者方法 沒(méi)有的就返回undefined了中符。上述的查找過(guò)程就是作用域鏈姜胖。
(什么叫'直到Object.prototype指向的對(duì)象有沒(méi)有'*)?
就是一路按相同規(guī)則查找就完事了 修改在上面了。
function Person1(){ }
Person1.prototype.name = "Nicholas";
Person1.prototype.age = 29;
Person1.prototype.job = "Software Engineer";
Person1.prototype.sayName = function(){ alert(this.name); };
var Person2 = function(){
this.name = 'pj';
Person1.prototype.name = "jy"; //寫(xiě)外面也行
}
var person1 = new Person1();
person1.sayName(); //"Nicholas"
person1.age; // 29
var person2 = new Person2;//()可有可無(wú)
person2.name;//pj 而非jy
- constructor來(lái)鞏固原型鏈
原型對(duì)象有一個(gè)默認(rèn)屬性舟茶,叫做constructor谭期,這個(gè)屬性指回原構(gòu)造函數(shù)。即Person.prototype.constructor===Person
如此 p.constuctor 順著原型鏈:找不到這個(gè)名字吧凉,那就找__ proto__ 而所有除了函數(shù)外的對(duì)象的__ proto__其實(shí)都指向了構(gòu)造器的prototype(原理上面講了) 于是隧出,找到了constructor,發(fā)現(xiàn)阀捅,constructor永遠(yuǎn)指向這個(gè)構(gòu)造函數(shù)本身胀瞪,于是,就指回了他爸Person
function Person(name) {
this.name = name
}
// 修改原型
Person.prototype.getName = function() {}
var p = new Person('jack')
console.log(p.__proto__ === Person.prototype) // true
console.log(p.constructor === Person) // true
console.log(p.__proto__ === p.constructor.prototype) // true
- 自己實(shí)現(xiàn)一切:
- 先熟悉apply是什么
1.apply示例:
<script type="text/javascript">
/*定義一個(gè)人類(lèi)*/
function Person(name,age) {
this.name=name; this.age=age;
}
/*定義一個(gè)學(xué)生類(lèi)*/
functionStudent(name,age,grade) {
Person.apply(this,arguments); this.grade=grade;
}
//創(chuàng)建一個(gè)學(xué)生類(lèi)
var student=new Student("qian",21,"一年級(jí)");
//測(cè)試
alert("name:"+student.name+"\n"+"age:"+student.age+"\n"+"grade:"+student.grade);
//大家可以看到測(cè)試結(jié)果name:qian age:21 grade:一年級(jí)
//學(xué)生類(lèi)里面我沒(méi)有給name和age屬性賦值啊,為什么又存在這兩個(gè)屬性的值呢,這個(gè)就是apply的神奇之處.
</script>
分析: Person.apply(this,arguments);
this:在創(chuàng)建對(duì)象在這個(gè)時(shí)候代表的是student這個(gè)對(duì)象//為什么這時(shí)student就是對(duì)象了饲鄙?
arguments:是一個(gè)數(shù)組,也就是[“qian”,”21”,”一年級(jí)”];
也就是通俗一點(diǎn)講就是:用student去執(zhí)行Person這個(gè)類(lèi)里面的內(nèi)容,在Person這個(gè)類(lèi)里面存在this.name等之類(lèi)的語(yǔ)句,這樣就將屬性創(chuàng)建到了student對(duì)象里面
以黑盒子理解:傳一個(gè)對(duì)象和一堆參數(shù)(數(shù)組)進(jìn)去凄诞。只要目標(biāo)函數(shù)(Person)是個(gè)合格的構(gòu)造函數(shù),那他就會(huì)幫你把這個(gè)對(duì)象的屬性給生成好忍级!
拓展:call也差不多
- 正式開(kāi)始:
// 構(gòu)造器函數(shù)
let Parent = function (name, age) {
this.name = name;
this.age = age;
};
Parent.prototype.sayName = function () {
console.log(this.name);
};
//自己定義的new方法
let newMethod = function (Parent, ...rest) {
//執(zhí)行三步走戰(zhàn)略
// 1.以構(gòu)造器的prototype屬性為原型帆谍,創(chuàng)建新對(duì)象;
let child = Object.create(Parent.prototype);
// 2.將this和調(diào)用參數(shù)傳給構(gòu)造器執(zhí)行 利用apply 把child的屬性生成好
Parent.apply(child, rest);
// 3.返回第一步的對(duì)象
return child;
};
//創(chuàng)建實(shí)例轴咱,將構(gòu)造函數(shù)Parent與形參作為參數(shù)傳入
const child = newMethod(Parent, 'echo', 26);
child.sayName() //'echo';
//最后檢驗(yàn)汛蝙,與使用new的效果相同
child instanceof Parent//true
child.hasOwnProperty('name')//true
child.hasOwnProperty('age')//true
child.hasOwnProperty('sayName')//false
- constuctor
可以理解為一個(gè)標(biāo)記作用 方便追溯分類(lèi)構(gòu)造器
var arr = []
// 恒為真
arr.constructor === Array
var num = 2
// 恒為真
num.constructor === Number
var str = '123'
// 恒為真
str.constructor === String
- instanceof檢測(cè)Parent的原型是否在child的原型鏈中
- constructor改變后 不會(huì)改變instanceof
child instanceof Parent == true
如何改變instanceof的結(jié)果呢?方法如下:
Parent.prototype = {};//覆寫(xiě)原型 使得原型鏈斷掉
child.prototype.__proto__ !== Parent.prototype
- constructor改變后 不會(huì)改變instanceof
-
ownProperty
用于確定某個(gè)屬性是在實(shí)例上還是在原型上 如果是,返回true - 方法和屬性要寫(xiě)在prototype還是構(gòu)造器里朴肺?
- 如果寫(xiě)在構(gòu)造器:每次構(gòu)造對(duì)象時(shí)窖剑,這些屬性和方法將被完全重新生成并寫(xiě)入該對(duì)象(為了實(shí)現(xiàn)私有),會(huì)耗費(fèi)對(duì)應(yīng)的內(nèi)存
- 如果寫(xiě)在prototype里:每次構(gòu)造對(duì)象時(shí)戈稿,會(huì)將__ proto__指過(guò)來(lái)西土,從而不用重新生成,也就是說(shuō)鞍盗,他是所有對(duì)應(yīng)實(shí)例所共享的需了,無(wú)論是person1還是person2訪(fǎng)問(wèn)的都是相同的屬性跳昼,相同的sayname(),這個(gè)過(guò)程叫做原型繼承援所,顯然庐舟,誰(shuí)都可以訪(fǎng)問(wèn),就無(wú)法私有了