原型、原型鏈、new做了什麼、繼承
原型和原型鏈
比我們通過一個建構函式new了一個新物件,建構函式的原型prototype指向一個物件,所有通過該建構函式new的新物件可以共享它所包含的屬性和方法。
建構函式的原型prototype是一個物件,那麼它也可以有自己的建構函式原型prototype,通過這樣,形成一個原型鏈。原型鏈最終都可以上溯到Object.prototype。Object.prototype的__proto__是null。null沒有任何屬性和方法,也沒有自己的原型。因此,原型鏈的盡頭就是null。
來看一個例子:
function Test() {}
var test = new Test();
test.__proto__===Test.prototype
Test.prototype.__proto__===Object.protype
Object.protype.__proto__===null
test.__proto__.__proto__.__proto__ === null
Test.__proto__===Function.prototype
獲取物件原型API: Object.getPrototypeOf(a) 即a.proto (tip: 方法在Object上而不是Object.prototype上)
為了更清晰地理解,看以下練習:
var obj= {}
obj.__proto__ === Object.prototype // 為 true
var fn = function(){}
fn.__proto__ === Function.prototype // 為 true
fn.__proto__.__proto__ === Object.prototype // 為 true
var array = []
array.__proto__ === Array.prototype // 為 true
array.__proto__.__proto__ === Object.prototype // 為 true
Array.__proto__ === Function.prototype // 為 true, Array的本質為一個建構函式
實際上,看a.__proto__是什麼,就看a的本質是什麼:
1.new出來的物件,則指向其建構函式的prototype;
2.建構函式,則Function.prototype
new
new的過程:
初始化一個新物件
該物件的__proto__屬性指向建構函式的原型prototype
將建構函式的this指向新物件,並執行函式
將新物件返回
[如果建構函式有返回一個物件(null除外),則將建構函式內的物件返回]
手寫new:
function myNew(func, ...params) {
const obj = {};
obj.__proto__ = func.prototype
const temp = func.apply(obj, params)
if(temp && typeof temp === 'object') {
return temp
}else{
return obj
}
}
繼承
既然通過原型鏈例項能訪問到上層的一些方法和屬性,那麼,自然而然繼承可以由他來實現。
##原型鏈繼承
最容易想到的是,讓建構函式的原型指向另一個你想要繼承的建構函式的例項,如下:
// 原型鏈繼承
// 缺點1:任何一個例項改變原型的屬性,所有例項的原型屬性都會變,因為指向的是同一個
// 缺點2:沒有辦法在不影響所有物件例項的情況下,給超型別的建構函式傳遞引數
function Father(firstName) {
this.firstName = firstName
//屬性為引用型別會被共享
this.ver = ['1.0', '2.0']
}
function Son(lastName) {
this.lastName = lastName
}
Son.prototype = new Father('lee')
const a = new Son('mei')
const b = new Son('lei')
a.ver[0] = '3.0'
console.log(a)
console.log(b)
<script>
// 借用建構函式,解決了上面的兩個問題
// 缺點1:方法都在建構函式中定義,函式無法複用
// 缺點2:在超型別的原型中定義的方法,對子型別而言也是不可見的
function Father(firstName) {
this.firstName = firstName
//屬性為引用型別會被共享
this.ver = ['1.0', '2.0']
}
function Son(firstName, lastName) {
Father.call(this, firstName)
this.lastName = lastName
}
const a = new Son('lee', 'mei')
const b = new Son('han', 'lei')
a.ver[0] = '3.0'
console.log(a)
console.log(b)
</script>
<script>
// 組合原型鏈和建構函式,解決了上面的四個問題
// 缺點1:會執行建構函式兩遍,父類的屬性會建立兩遍
function Father(firstName) {
this.firstName = firstName
//屬性為引用型別會被共享
this.ver = ['1.0', '2.0']
}
function Son(firstName, lastName) {
//第二次呼叫
Father.call(this, firstName)
this.lastName = lastName
}
//第一次呼叫
Son.prototype = new Father('tan')
const a = new Son('lee', 'mei')
const b = new Son('han', 'lei')
a.ver[0] = '3.0'
console.log(a)
console.log(b)
</script>
<script>
// 原型式繼承
// 如果目的是讓一個物件與另一個物件保持類似的情況.
// 在傳入一個引數的情況下,Object.create()與 object()方法的行為相同。
// Object.create(null)建立一個空物件,不繼承object的任何方法
function object(o) {
function F() {}
F.prototype = o
return new F()
}
let person = {
name: "Nicholas",
friends: ["Shelby", "Court", "Van"]
}
const a = object(person)
console.log(a)
</script>
<script>
// 終極版,利用原型式繼承,讓子建構函式原型能夠呼叫父建構函式原型的方法
function Father(firstName) {
this.firstName = firstName
this.ver = ['1.0', '2.0']
}
function Son(firstName, lastName) {
Father.call(this, firstName)
this.lastName = lastName
}
Father.prototype.say = function () {
console.log(this.firstName)
}
// 不直接指向父類的原型,因為這樣兩者指向了同一個物件,實現了繼承,但改變任何一個,另一個也會受影響
Son.prototype = Object.create(Father.prototype)
const a = new Son('lee', 'mei')
const b = new Son('han', 'lei')
a.ver[0] = '3.0'
console.log(a)
console.log(b)
</script>
通過上面的流程也可以很好地去理解ES6中的extends。
es6 extends super
class Father {
constructor(firstName) {
this.firstName = firstName
this.ver = ['1.0', '2.0']
}
update(firstName) {
this.firstName = firstName
}
}
class Son extends Father{
constructor(firstName, lastName) {
// 繼承父類屬性,必須有這句之後才能呼叫父類方法
super(firstName)
this.lastName = lastName
}
update(firstName, lastName) {
// 呼叫父類方法
super.update(firstName)
this.lastName = lastName
}
}
// 通過觀察xiao的結構可以看出,原理大概是終極版
let xiao = new Son('lee', 'mei')
```
相關文章
- 原型、原型鏈與繼承原型繼承
- 原型,繼承——原型繼承原型繼承
- JS原型鏈繼承JS原型繼承
- 徹底搞懂原型、原型鏈和繼承原型繼承
- 建構函式、原型、原型鏈、繼承函式原型繼承
- 【機制】JavaScript的原型、原型鏈、繼承JavaScript原型繼承
- javascript原型鏈及繼承JavaScript原型繼承
- javascript - 繼承與原型鏈JavaScript繼承原型
- 面試題之原型、原型鏈、和繼承面試題原型繼承
- JS原型鏈、prototype、__proto__、原型鏈繼承詳解JS原型繼承
- [JavaScript]原型、原型鏈、建構函式與繼承JavaScript原型函式繼承
- 淺談JS物件的建立、原型、原型鏈繼承JS物件原型繼承
- javascript原型鏈繼承的使用JavaScript原型繼承
- 物件、原型鏈、類、繼承【上】物件原型繼承
- 小議JS原型鏈、繼承JS原型繼承
- 圖解JavaScript原型鏈繼承圖解JavaScript原型繼承
- JS的原型鏈和繼承JS原型繼承
- 白話JavaScript原型鏈和繼承JavaScript原型繼承
- javascript基礎-原型鏈與繼承JavaScript原型繼承
- 深入理解原型鏈和繼承原型繼承
- JS中的繼承與原型鏈JS繼承原型
- 原型和繼承原型繼承
- 物件-原型-繼承物件原型繼承
- JS的物件導向(理解物件,原型,原型鏈,繼承,類)JS物件原型繼承
- js 原型鏈實現類的繼承JS原型繼承
- 深入理解JavaScript原型鏈與繼承JavaScript原型繼承
- MDN繼承和原型鏈章筆記繼承原型筆記
- 原型繼承:子類原型繼承
- js 原型鏈繼承__proto__、prototype簡析JS原型繼承
- 原型鏈實現繼承的6種方式原型繼承
- javascript的原型和繼承JavaScript原型繼承
- Javascript繼承4:潔淨的繼承者—-原型式繼承JavaScript繼承原型
- es5 原型式繼承原型繼承
- 原型繼承(翻譯 vjeux 文章)原型繼承UX
- 【前端詞典】繼承(一) - 原型鏈你真的懂嗎?前端繼承原型
- 【JS面試向】深入原型鏈之class的繼承JS面試原型繼承
- 前端面試必殺技:原型、原型鏈以及繼承(一張圖搞定面試)前端面試原型繼承
- 徹底弄懂JS原型與繼承JS原型繼承