構造函數的缺點
JavaScript通過構造函數生成新對象抚太,因此構造函數可以視為對象的模板 實例對象的屬性和方法,可以定義在構造函數內部
但同一個構造函數的對象實例之間捶牢,無法共享屬性 是對系統(tǒng)資源的浪費
但同一個構造函數的對象實例之間梯嗽,無法共享屬性 是對系統(tǒng)資源的浪費
所以棄用在構造函數里為實例定義方法
prototype屬性的作用
JavaScript的每個對象都繼承另一個對象杯矩,后者稱為“原型”(prototype)對象 null除外,它沒有自己的原型對象
通過構造函數生成對象實例時巾表,會自動為實例對象分配原型對象 每一個構造函數都有一個prototype屬性汁掠,這個屬性就是實例對象的原型對象
原型對象的屬性不是實例對象自身的屬性
只要修改原型對象,變動就立刻會體現(xiàn)在所有實例對象上
Animal.prototype.color = 'yellow';
cat1.color // "yellow"
cat2.color // "yellow"
上面代碼中集币,原型對象的color屬性的值變?yōu)閥ellow考阱,兩個實例對象的color屬性立刻跟著變
這是因為實例對象其實沒有color屬性,都是讀取原型對象的color屬性
也就是說鞠苟,當實例對象本身沒有某個屬性或方法的時候乞榨,它會到構造函數的prototype屬性指向的對象,去尋找該屬性或方法
如果實例對象自身就有某個屬性或方法当娱,它就不會再去原型對象尋找這個屬性或方法
cat1.color = 'black';
cat1.color // 'black'
cat2.color // 'yellow'
總結一下
原型對象的作用吃既,就是定義所有實例對象共享的屬性和方法
這也是它被稱為原型對象的含義,而實例對象可以視作從原型對象衍生出來的子對象
由于JavaScript的所有對象都有構造函數趾访,而所有構造函數都有prototype屬性态秧,所以所有對象都有自己的原型對象(null除外)
原型鏈
由于原型本身也是對象,所以也有自己的原型扼鞋,于是乎就形成了一條原型鏈(prototype chain)
如果一層層地上溯申鱼,所有對象的原型最終都可以上溯到Object.prototype,即Object構造函數的prototype屬性指向的那個對象
那么云头,Object.prototype對象有沒有它的原型呢捐友?回答可以是有的,就是沒有任何屬性和方法的null對象溃槐,而null對象沒有自己的原型
...... → Object對象實例 → Object.prototype → null
“原型鏈”的作用是匣砖,讀取對象的某個屬性時,JavaScript引擎先尋找對象本身的屬性昏滴,如果找不到猴鲫,就到它的原型去找,如果還是找不到谣殊,就到原型的原型去找
如果直到最頂層的Object.prototype還是找不到拂共,則返回undefined
如果對象自身和它的原型,都定義了一個同名屬性姻几,那么優(yōu)先讀取對象自身的屬性宜狐,這叫做“覆蓋”(overriding)
需要注意的是势告,一級級向上,在原型鏈尋找某個屬性抚恒,對性能是有影響的 所尋找的屬性在越上層的原型對象咱台,對性能的影響越大 如果尋找某個不存在的屬性,將會遍歷整個原型鏈
constructor屬性
構造函數.prototype所指向的原型對象有一個constructor屬性俭驮,默認指向構造函數.prototype對象所在的構造函數
function P() {}
P.prototype.constructor === P// true
由于constructor屬性定義在prototype對象上面回溺,意味著可以被所有實例繼承
實例.constructor 繼承原型(構造函數.prototype)身上的constructor屬性 即:原型的構造函數也是實例的構造函數
constructor屬性的作用,是分辨原型對象到底屬于哪個構造函數
function P() {}
var p = new P();
p.constructor// function P() {}
p.constructor === P.prototype.constructor// true
p.hasOwnProperty('constructor')// false
有了constructor屬性表鳍,就可以從實例新建另一個實例
function Constr() {}
var x = new Constr();
var y = new x.constructor();//相當于:new Constr()
y instanceof Constr // true
由于constructor屬性是一種原型對象與構造函數的關聯(lián)關系馅而,所以修改原型對象的時候,務必要小心
修改原型對象后譬圣,再生成實例(這個順序不能變!!)
否則:實例.constructor瓮恭,instanceof運算符 結果都反過來且理解不了
function A() {}
function B() {}
A.prototype = B.prototype;//先改變原型,再生成實例(符合我的理解思路,按這個記憶)
var a = new A();
a instanceof A // true 反映的是實例與構造函數的關系,改原型當然不受影響
a.constructor.name // B 因為在修改原型對象后沒有指定舊原型身上的constructor屬性,自然就調新原型身上的constructor屬性
養(yǎng)成好習慣,修改原型對象時一并校正constructor屬性的指向
// 避免這種寫法C.prototype = {
method1: function (...) { ... },
// ...};
// 較好的寫法C.prototype = {
constructor: C,
method1: function (...) { ... },
// ...};
// 好的寫法C.prototype.method1 = function (...) { ... };
function A() {}
function B() {}
A.prototype = B.prototype;
A.prototype.constructor = A; // 好習慣修改原型對象后,重新指定原型上的constructor的屬性
var a = new A();
a instanceof A
a.constructor.name // A 重新指定原型的constructor屬性,就不走新原型身上的constructor
構造函數身上不僅有prototype屬性指向實例原型對象
構造函數還有name屬性,顯示構造函數的名稱
function Foo() {}
var f = new Foo();
Foo.name // "Foo"
f.constructor.name // "Foo"
Object.prototype.isPrototypeOf() [多情]
對象實例的isPrototypeOf方法,用來判斷X是不是實例的原型對象
由于isPrototypeOf() 對整個原型鏈上的對象都有效厘熟,因此同一個實例屯蹦,可能會有多個原型對象都返回true
var o1 = {};
var o2 = Object.create(o1);
var o3 = Object.create(o2);
//原型鏈 o3→o2→o1
o2.isPrototypeOf(o3) // true
o2 是 o3的原型對象
o1.isPrototypeOf(o3) // true
o1 是 o3的原型對象
instanceof運算符 [多情]
instanceof運算符返回一個布爾值,表示對象實例是否是某個構造函數拍出來的
var v = new Vehicle();
v instanceof Vehicle // true
instanceof運算符的左邊是實例對象,右邊是構造函數
它的運算實質:檢查右邊構造函數的prototype屬性所指向的原型對象,是否在左邊對象實例的原型鏈上
v instanceof Vehicle
// 等同于
Vehicle.prototype.isPrototypeOf(v)
由于instanceof對整個原型鏈上的對象都有效绳姨,因此同一個對象實例登澜,可能會對多個構造函數都返回true
var d = new Date();
d instanceof Date // true
d instanceof Object // true
Object.getPrototypeOf()
Object對象的又一個靜態(tài)方法↓ 返回對象實例的原型對象
Object.keys() Object. getOwnPropertyNames()
因為這個原型對象是該實例原型鏈中最內層的,所以它是獲取原型對象的標準方法
function Ft() {}
var f = new Ft ();
Object.getPrototypeOf(f) === Ft.prototype// true
Object.setPrototypeOf()[不常用]
Object對象的又一個靜態(tài)方法↓
Object.keys() Object. getOwnPropertyNames()
Object.setPrototypeOf()
接收兩個參數,第一個是現(xiàn)有對象飘庄,第二個是原型對象 脑蠕,并返回現(xiàn)有對象(它的原型對象已被設置)
var a = {x: 1};
var b = {y: 2};
var c = Object.setPrototypeOf(a, b);
c.x //1 c自身的x屬性
c.y //2 c從它的原型對象b上繼承來的y屬性
Object.create()[不常用]
Object對象的又一個靜態(tài)方法↓
Object.keys() Object. getOwnPropertyNames()
Object.create()
它接受一個對象作為參數,返回一個新對象 后者完全繼承前者的屬性方法跪削,即原有對象成為新對象的原型
var A = {
print: function () {
console.log('hello');
}
};
var B = Object.create(A);
B.print() // hello B從它的原型對象A上繼承來的print方法
下面三種方式生成的新對象是等價的
var o1 = new Object();
var o2 = Object.create(Object.prototype);// o2的原型對象是Object.prototype,o2就是Object對象實例
var o3 = {}; // 對象字面量方法
如果想要生成一個不繼承任何屬性(比如沒有toString和valueOf方法)的對象谴仙,可以將Object.create的參數設為null
var o = Object.create(null);
o.valueOf()
// TypeError: o.valueOf is not a function