構(gòu)造函數(shù)
構(gòu)造函數(shù)就是一個普通的函數(shù),創(chuàng)建方式和普通函數(shù)沒有區(qū)別枯饿,不同的是構(gòu)造函數(shù)習慣上首字母大寫。另外就是調(diào)用方式的不同诡必,普通函數(shù)是直接調(diào)用奢方,而構(gòu)造函數(shù)需要使用new關(guān)鍵字來調(diào)用。
function Person(name, age, gender) {
this.name = name
this.age = age
this.gender = gender
this.sayName = function () {
alert(this.name);
}
}
var per = new Person("孫悟空", 18, "男");
function Dog(name, age, gender) {
this.name = name
this.age = age
this.gender = gender
}
var dog = new Dog("旺財", 4, "雄")
console.log(per);//當我們直接在頁面中打印一個對象時,事件上是輸出的對象的toString()方法的返回值
console.log(dog);
每創(chuàng)建一個Person構(gòu)造函數(shù)蟋字,在Person構(gòu)造函數(shù)中稿蹲,為每一個對象都添加了一個sayName方法,也就是說構(gòu)造函數(shù)每執(zhí)行一次就會創(chuàng)建一個新的sayName方法鹊奖。這樣就導致了構(gòu)造函數(shù)執(zhí)行一次就會創(chuàng)建一個新的方法苛聘,執(zhí)行10000次就會創(chuàng)建10000個新的方法,而10000個方法都是一摸一樣的忠聚,為什么不把這個方法單獨放到一個地方设哗,并讓所有的實例都可以訪問到呢?這就需要原型(prototype)
原型
在JavaScript中,每當定義一個函數(shù)數(shù)據(jù)類型 (普通函數(shù)咒林、類)
時候熬拒,都會天生自帶一個prototype屬性,這個屬性指向函數(shù)的原型對象垫竞,并且這個屬性是一個對象數(shù)據(jù)類型的值澎粟。
原型對象就相當于一個公共的區(qū)域,所有同一個類的實例都可以訪問到這個原型對象欢瞪,我們可以將對象中共有的內(nèi)容活烙,統(tǒng)一設(shè)置到原型對象中。
function Person(name,age){
this.name=name
this.age=age
this.sayName = function () {
alert(this.name);
}
}
改造下
function Person(name,age){
this.name=name
this.age=age
}
Person.prototype.year=2021 // 往原型上添加一點東西
Person.prototype.sayName =function(){
alert(this.name);
}
const personA=new Person()
const personB=new Person()
console.log('personA',personA , personB)
console.log(personA.hasOwnProperty('year'), 'year' in personA ) // false true
console.log(personB.hasOwnProperty('year'), 'year' in personB) // false true
// hasOwnProperty只會從實例本身上找屬性遣鼓, in會從實例所屬類的原型身上找
console.log('personA.year',personA.year , personB.year ) // 2021 2021
原型的作用:
1:實現(xiàn)對象之間的數(shù)據(jù)共享啸盏。
2.在es6之前,沒有class的情況下骑祟,模擬面向?qū)ο蠡嘏常瑯?gòu)造函數(shù)中放私有屬性,原型上放公有屬性次企,一般放方法怯晕。
通過原型添加的方法,可以完美的解決屬性與方法共享問題,從而節(jié)省了內(nèi)存空間..
原型鏈
每一個對象數(shù)據(jù)類型(普通的對象、實例缸棵、prototype......)
也天生自帶一個屬性__proto__
舟茶,這個屬性的值是當前實例所屬類的原型對象(prototype)。
原型對象中有一個屬性constructor, 它指向構(gòu)造函數(shù)堵第。
function Person() {} // 構(gòu)造函數(shù)
var person = new Person() // 實例person
console.log(person.__proto__ === Person.prototype) // true
console.log(Person.prototype.constructor===Person) // true
//順便學習一個ES5的方法,可以獲得對象的原型
console.log(Object.getPrototypeOf(person) === Person.prototype) // true
js實例對象的原型屬性
__proto__
的屬性值吧凉,指向這個實例對象所屬類的原型
例如 new Array 出來的數(shù)組,屬于Array這個類
const xArray=new Array()
那么
xArray.__proto__ === Array.prototype // true
Array.prototype擁有的方法踏志,array的每一個實例都會有阀捅,直接通過xArray.push()這樣調(diào)用,而不需要xArray._ proto _.push针余,不需要這樣去調(diào)用也搓。
Array.prototype.__proto__ === Object.prototype // true
Object.prototype._proto_ // null
所謂原型鏈赏廓,指的就是這一條指針鏈!
原型鏈的頂層就是Object.prototype傍妒,而Object的原型對象的是沒有原型屬性的幔摸。
Object.prototype._proto_ ===null
何為原型鏈
在JavaScript中萬物都是對象,對象和對象之間也有關(guān)系颤练,并不是孤立存在的既忆。對象之間的繼承關(guān)系,在JavaScript中是通過prototype對象指向父類對象嗦玖,直到指向Object對象為止患雇,這樣就形成了一個原型指向的鏈條,專業(yè)術(shù)語稱之為原型鏈宇挫。
舉例說明:person → Person → Object 苛吱,普通人繼承人類,人類繼承對象類
當我們訪問對象的一個屬性或方法時器瘪,它會先在對象自身中尋找翠储,如果有則直接使用,如果沒有則會去原型對象中尋找橡疼,如果找到則直接使用援所。如果沒有則去原型的原型中尋找,直到找到Object對象的原型,Object對象的原型沒有原型欣除,如果在Object原型中依然沒有找到住拭,則返回undefined。
我們可以使用對象的hasOwnProperty()來檢查對象自身中是否含有該屬性历帚;使用in檢查對象中是否含有某個屬性時滔岳,如果對象中沒有但是原型中有,也會返回true
function Person() {}
Person.prototype.a = 123;
Person.prototype.sayHello = function () {
alert("hello");
};
var person = new Person()
console.log(person.a)//123
console.log(person.hasOwnProperty('a'));//false
console.log('a'in person)//true
person實例中沒有a這個屬性挽牢,從 person 對象中找不到 a 屬性就會從 person 的原型也就是 person.__proto__
澈蟆,也就是 Person.prototype中查找,很幸運地得到a的值為123卓研。那假如 person.__proto__
中也沒有該屬性,又該如何查找睹簇?
當讀取實例的屬性時奏赘,如果找不到,就會查找與對象關(guān)聯(lián)的原型中的屬性太惠,如果還查不到磨淌,就去找原型的原型,一直找到最頂層Object為止凿渊。Object是JS中所有對象數(shù)據(jù)類型的基類(最頂層的類)梁只,在Object.prototype上沒有__proto__
這個屬性缚柳。
console.log(Object.prototype.__proto__ === null) // true
所謂原型鏈,指的就是圖中的proto這一條指針鏈搪锣!
原型鏈的頂層就是Object.prototype秋忙,而Object的原型對象的是沒有原型屬性的。
先找自身构舟,找不到就沿著__proto__
一直往上找原型灰追,直到找到最頂層的類Object,Object類沒有原型了狗超,如果還找不到就返回undefined
實戰(zhàn):用ES6的class定義一套對象/函數(shù)
ES6提供了class弹澎,但是這個并不是類,而是 Function 的語法糖努咐。
目的是簡化ES5里面苦蒿,為了實現(xiàn)繼承而采用的各種“神操作”。
用class來定義渗稍,結(jié)構(gòu)和關(guān)系會非常清晰佩迟,再也不會看著頭疼了,建議新手可以跳過ES5的實現(xiàn)方式免胃,直接用ES6的方式音五。
我們先定義一個Base,然后定義一個Person繼承Base羔沙,再定義一個Man繼承Person躺涝。
也就是說,可以深層繼承扼雏。
class Base {
constructor (title) {
this.title = '測試一下基類:' + title
}
baseFun1(info) {
console.log('\n這是base的函數(shù)一坚嗜,參數(shù):', info, '\nthis:', this)
}
}
class Person extends Base{
constructor (title, age) {
super(title)
this.title = '人類:' + title
this.age = age
}
personFun1(info) {
console.log('\n這是base的函數(shù)一,參數(shù):', info, '\nthis:', this)
}
}
class Man extends Person {
constructor (title, age, date) {
super(title, age)
this.title = '男人:' + title
this.birthday = date
}
manFun3 (msg) {
console.log('jim 的 this ===', this, msg)
}
}
構(gòu)造函數(shù) constructor
打印結(jié)果很清晰的表達了诗充,構(gòu)造函數(shù)就是我們定義的class苍蔬。屬性
屬性比較簡單,統(tǒng)統(tǒng)都掛在 this 上面蝴蜓,而且是同一個級別碟绑。函數(shù)
函數(shù)就有點復雜了,首先函數(shù)是分級別的茎匠,掛在每一級的原型上面-
原型鏈
Man的實例 > Man的原型 > Person的原型 > Base 的原型 > Object 的原型格仲。
通過__proto__
連接了起來。
image.png
Man的實例 man1诵冒,可以通過這個“鏈條”凯肋,找到 baseFun1,
直接用 man1.baseFun1()
即可(?)汽馋,
而不需要使用__proto__
侮东;
man1.__ proto__.__ proto__.__ proto__.baseFun1()
(?)
man1.__ proto__ === Man.prototype
Man.prototype .__proto__ === Person.prototype
Person.prototype .__proto__ === Base.prototype
Base.prototype .__proto__ === Object.prototype
Object.prototype .__proto__ === null
Object是js頂層類圈盔,原型鏈的頂端也就是Object.prototype ,Object.prototype沒有
__proto__
屬性