JS之Prototypal Inheritance
本文解釋了:
prototype concept
inheritance concept
原型鏈的概念及用法
修改原型的危害
非基本數(shù)據(jù)類型自帶一些屬性和方法
基本數(shù)據(jù)自帶對(duì)象包裝Object wrapper
-
prototype concept
By default, every function has a property called prototype. This property by default is empty and you can add properties and methods to it. When you create object from this function, e.g. if I create X1 from X, it would inherit these properties and methods that define X's prototype.
-
inheritance concept
- One object gets access to the properties and methods of another object.
- All JavaScript objects inherit properties and methods from a prototype.
-
原型鏈 prototype chain
每個(gè)對(duì)象都有一個(gè) prototype豺鼻,每個(gè) prototype 也是一個(gè)對(duì)象。每個(gè)對(duì)象是通過(guò)自己的 prototype 來(lái)繼承屬性和方法的款慨;
JavaScript自帶原型鏈搜索功能儒飒,如上圖的obj.prop2實(shí)際上是obj.prototype.prop2, obj.prop3也是obj.prototype.prototype.prop3, 但是我們不用手動(dòng)地這樣層層地去找這個(gè)屬性究竟是在哪一個(gè)prototype的對(duì)象里,JS直接幫我們找了檩奠,所以直接調(diào)用obj.prop2等就可以桩了。
同樣,我們也可以**手動(dòng)修改對(duì)象的原型埠戳,使它指向別的對(duì)象(by reference):
var person = {
firstName: 'default',
lastName: 'default',
getFullName: function() {
return this.firstName + ' ' + this.lastName;
}
}
var john = {
firstName : 'John',
lastName : 'Doe'
}
//手動(dòng)設(shè)置把john的原型指向person object
john.__proto__ = person;
console.log(john.getFullName()); //John Doe
console.log(john.firstName); //John
注意井誉,最后的console.log(john.firstName);
的結(jié)果是John而非person
里的default
,是因?yàn)?code>john對(duì)象本身就自帶firstName
這個(gè)屬性整胃,在原型鏈的頂端颗圣,JS引擎在原型從上到下層層遞找,所以當(dāng)JS引擎在原型鏈頂端找到方法和屬性后屁使,就停止再往下找在岂,直接調(diào)用再頂端的(也就是john
對(duì)象)的firstName
屬性。此外蛮寂,也要注意蔽午,當(dāng)person
對(duì)象的getFullName()
方法加入John
對(duì)象的原型后,里面的this
就指向了john對(duì)象酬蹋。
同理及老,如果在上方的代碼塊下再加上以下代碼,會(huì)得到:
var jane = {
firstName : 'Jane'
}
jane.__proto__ = person;
console.log(jane.getFullName()); //Jane default
-
修改原型的危害
修改對(duì)象的原型在實(shí)際生活中是一個(gè)十分危險(xiǎn)的舉動(dòng)除嘹,一旦被修改写半,之后所聲明擁有同一原型的新對(duì)象將受到牽連岸蜗。例如:
const num = 42;
=> undefined
num.toString();
=> '42'
Number.prototype.toString = function() {return '100'};
=> [Function]
num.toString();
=> '100'
num
=> 42
const num2 = 50;
=> undefined
num2.toString();
=> '100'
從上例可以看出尉咕,當(dāng)把整個(gè)Number對(duì)象原型中的toString()
方法修改后,所有的number對(duì)象里的方法也都被改寫(xiě)璃岳。
-
非基本數(shù)據(jù)類型自帶一些屬性和方法
- Array.prototype.push()
- String.prototype.toUpperCase()
比如說(shuō)我們?cè)跒g覽器自帶的console里聲明一個(gè)變量const arr = ['value', 'value1']
可以得到以下的結(jié)果
在console里輸入arr和它的原型
*#### Each object stores a reference to its prototype.每個(gè)對(duì)象都存儲(chǔ)了對(duì)其原型的引用年缎。(即每個(gè)對(duì)象都知道原型自帶的屬性和方法)
-
Properties/Methods defined most tightly to the instance have priority.
比如說(shuō)在當(dāng)arr.proto和arr.proto.proto兩個(gè)object都包含同樣的功能toString,但當(dāng)我們?cè)谡{(diào)用arr.toString的時(shí)候,實(shí)際調(diào)用的是arr.proto.toString,就如arr雖然既是array又是object铃慷,但是相較之下稱呼其為array更具體单芜,arr.proto.toString也比arr.proto.proto.toString更specific,離arr這個(gè)instance更近犁柜。
-
Most primitive types have object wrappers
- String()
- Number()
- Boolean()
- Object()
- (Symbol())
這里要注意的是洲鸠,雖然我們之前說(shuō)只有非基本數(shù)據(jù)類型才自帶一些屬性和方法,但是基本數(shù)據(jù)類型卻自帶對(duì)象包裝(object wrapper),而這些包裝也是自帶原型的,所以可以調(diào)用原型的方法扒腕。即JS will automatically box(wrap) primitive values so you have access to the methods.
42.toString(); // Errors(還未包裝)
const x = 42;(包裝后)
x.toString(); //'42'
x.__proto__; //[Number: 0]
x instanceof Number // false
這里也要注意>畹怼!瘾腰!最后一個(gè)例子中的instanceof
operator**運(yùn)算符用于測(cè)試構(gòu)造函數(shù)的prototype屬性是否出現(xiàn)在對(duì)象的原型鏈中的任何位置皆的。而此處的x只是為了引用方法的對(duì)Number對(duì)象的包裝(It's just boxed around that number object for your reference)。
同理"foo" instanceof String //false蹋盆;String("foo") instanceof String //true
费薄。