對象原型
參考 - MDN
Javascript中的原型
在Javascript中,每一個函數都有一個特殊的屬性,叫做原型
下面獲取函數的原型fn.prototype
function f1(){}
console.log(f1.prototype)
/*{constructor: f f1()__proto__:{constructor: f Object()__defineGetter__: f __defineGetter__()__defineSetter__: f __defineSetter__()hasOwnProperty: f hasOwnProperty()__lookupGetter__: f __lookupGetter__()__lookupSetter__: f __lookupSetter__()isPrototypeOf: f isPrototypeOf()...}}
*/
下面給函數的原型添加屬性fn.prototype.hello = 'world'
function f1(){}
f1.prototype.hello = 'world'
console.log(f1.prototype)
/*{hello: "world"constructor: f f1()__proto__: Object}
*/
創建一個函數的實例new fn()
,并給實例添加屬性
function Person(){}
Person.prototype.hello = 'world'
var p1 = new Person()
p1.say = 'hi'
console.log(p1)
/*{say: "Hi"__proto__:{hello: "world"constructor: f Person()__proto__: Object}}
*/
瀏覽器訪問某個屬性的尋找順序:
- 首先會尋找這個實例是否含有該屬性
- 如果有則返回,否則會通過
__proto__
尋找該實例的原型Person.prototype
上是否含有該屬性. - 如果有則返回,否則會通過
__proto__.__proto__
的上尋找該屬性. - 如此循環.到最后
__proto__.__proto__. ... = undefined
則返回undefined
理解原型對象
下面定義一個構造器函數
function Person(first, last, age, gender, interests){this.first = firstthis.last = lastthis.age = agethis.gender = genderthis.interests = interests
}
var p1 = new Person("Li","Bruce",18,'男','coding')
console.log(p1.__proto__ === Person.prototype) // true
console.log(p1.__proto__.__proto__ === Object.prototype) // true
console.log(p1)
/*{first: "Li"last: "Bruce"age: 18gender: "男"interests: "coding"__proto__:{constructor: f Person(first, last, age, gender, interests)__proto__:{constructor: f Object()__defineGetter__: f __defineGetter__()...valueOf: f valueOf()...}}}
*/
- 此時存在一條原型鏈:
此時,調用如下:
p1.valueOf()
根據前面的規則:
- 瀏覽器首先檢查,p1對象是否含有
valueOf()
方法 - 如果沒有,則瀏覽器檢查p1對象的原型對象(Person.prototype, 通過瀏覽器提供的
__proto__
訪問)是否具有可用的valueOf()
方法 - 如果還沒有,瀏覽器會檢查
Person()
構造函數的prototype屬性所指向的對象的原型對象(Object.prototype)是否含有該方法,如果有則返回,否則返回undefined
原型鏈中的方法和屬性沒有被復制到其他對象 – 它們被訪問需要通過"原型鏈"的方式
官方并未提供
__proto__
屬性,在JavaScript語言標準中用[[prototype]]表示.然而,大多數現代瀏覽器還是提供了一個名為__proto__
的屬性.
prototype屬性: 繼承成員被定義的地方
查看MDN - Object可以看到,Object有很多屬性,但是在上面的p1中,并不是全部都繼承了.
原因在于: 被繼承的屬性僅僅只是定義在Object.prototype
上的屬性.定義在Object本身上的屬性是不會被繼承的
看下面的栗子:
function Person(){}
console.log(Person.prototype)
/*{constructor: f Person()__proto__: Object}
*/
默認情況下,構造器(此處為Person)的prototype
屬性初始為空白.
靜態成員與實例成員
靜態成員
: 在構造函數本身上添加的成員實例成員
: 構造函數內部通過this添加的成員,只能通過實例化的對象來訪問
function Person(name, age){this.name = namethis.age = age
}
Person.sex = '男'
var p1 = new Person('Marron', 18)
// name、age就是實例成員
// sex就是靜態成員
使用prototype的好處
節約內存.例如下述:
function Person(name, age){this.name = namethis.age = agethis.sayHi = function(){console.log('Hi ~')}
}
var p1 = new Person('Mar', 18)
var p2 = new Person('Marron', 19)
上面通過構造函數,生成了2個實例化對象,但是兩個實例化對象的方法的內存地址是不同的.
console.log(p1.sayHi == p2.sayHi) // false
引用prototype屬性可以節約內存
function Person(name, age){this.name = namethis.age = age
}
Person.prototype.sayHi = function(){console.log('Hi ~')
}
var p1 = new Person('Mar', 18)
var p2 = new Person('Marron', 19)
console.log(p1.sayHi == p2.sayHi) // true
p1.sayHi() // "Hi ~"
-
可見構造函數通過
Person.prototype
類似構造的所有對象是共享的(同一個內存空間) -
javascript規定,每一個構造函數都有一個prototype屬性,指向另一個對象.