一文徹底搞懂JavaScript中的prototype
prototype初步認識
在學習JavaScript中,遇到了prototype,經過一番了解,知道它是可以進行動態擴展的
function Func(){};
var func1 = new Func;
console.log(func1.var1) //undefined
Func.prototype.var1 = "Func進行了擴展"
console.log(func1.var1) //Func進行了擴展
即最開始創建的函數Func並沒有var1變數,但是我們可以進行擴展,並且讓根據其創建的對象也有var1變數
函數有prototype屬性,函數創建的對象沒有
這個時候,嘗試對var1變數進行擴展,但是居然報錯了
function Func(){};
var func1 = new Func;
console.log(func1.var1) //undefined
Func.prototype.var1 = "Func進行了擴展"
console.log(func1.var1) //Func進行了擴展
console.log(Func.var1)
func1.prototype.var1 = "func1進行了擴展" //Uncaught TypeError: Cannot set properties of undefined (setting 'var1')
獲得當前對象的屬性
那我們現在有一個疑問:func1應該是有var1變數的,那上面報錯意思是func1沒有prototype屬性/方法咯?我如何查看一個對象到底有沒有這個屬性呢?
我們知道,可以用in來查看對象是否有屬性
function Func(){};
var func1 = new Func;
console.log(func1.var1) //undefined
Func.prototype.var1 = "Func進行了擴展"
console.log(func1.var1) //Func進行了擴展
console.log(Func.var1)
// func1.prototype.var1 = "func1進行了擴展" //Uncaught TypeError: Cannot set properties of undefined (setting 'var1')
console.log("var1" in func1) //true
console.log("prototype" in func1) //false
現在我們知道了,func1確實沒有prototype屬性/方法,那func1也就是函數創建的對象都不能擴展了嗎?回答這個問題之前,我們還要明白一個問題,func1中的var1變數是自己的嗎?怎麼區分呢?
function Func(){};
var func1 = new Func;
console.log(func1.var1) //undefined
Func.prototype.var1 = "Func進行了擴展"
console.log(func1.var1) //Func進行了擴展
console.log(Func.var1)
// func1.prototype.var1 = "func1進行了擴展" //Uncaught TypeError: Cannot set properties of undefined (setting 'var1')
console.log("var1" in func1) //true
console.log("prototype" in func1) //false
console.log("-----接下來看hasOwnProperty函數-----")
func1.var2 = "func1自己的變數"
console.log(func1.hasOwnProperty("var2")) //true
console.log(func1.hasOwnProperty("var1")) //false
我們可以用hasOwnProperty
函數來知道變數是不是擴展的了
父和子的擴展
這裡我把Func當成父,把func1當成子來作為個人理解
function Func() { };
var func1 = new Func;
console.log(func1.var1) //undefined
Func.prototype.var1 = "Func進行了擴展"
console.log(func1.var1) //Func進行了擴展
console.log(Func.var1)
// func1.prototype.var1 = "func1進行了擴展" //Uncaught TypeError: Cannot set properties of undefined (setting 'var1')
console.log("var1" in func1) //true
console.log("prototype" in func1) //false
console.log("-----接下來看hasOwnProperty函數-----")
func1.var2 = "func1自己的變數"
console.log(func1.hasOwnProperty("var2")) //true
console.log(func1.hasOwnProperty("var1")) //false
console.log("-----接下來看proto-----")
console.log(Func.hasOwnProperty("__proto__")) //false
console.log(func1.hasOwnProperty("__proto__")) //false
console.log(func1.__proto__ === Func.prototype) // true
console.log(func1.__proto__ == Func.prototype) // true
console.log(func1.prototype == Func.prototype) // false
console.log(func1.__proto__.var1) //Func進行了擴展
console.log(func1.var1) //Func進行了擴展
這裡可以看到func1本身沒有__proto__
屬性,但是和Func的protype屬性是一樣的
子的proto和prototype的區別
到這裡你肯定想問,對於子func1的__proto__
和prototype有什麼區別呢?
首先子func1並沒有prototype屬性
其實雙下劃線表示隱藏的,不太想讓外界訪問到,這麼思考,父Func不僅創建了子func1,而且創建了子func2,這時候如果子func1通過__proto__
修改了var1,那麼父Func的var1跟著變化,並且func2的var1也會變化,但是如果func1直接修改var1,那麼父Func和子func2的var1都不會變化
function Func() { };
var func1 = new Func;
console.log(func1.var1) //undefined
Func.prototype.var1 = "Func進行了擴展"
console.log(func1.var1) //Func進行了擴展
console.log(Func.var1)
// func1.prototype.var1 = "func1進行了擴展" //Uncaught TypeError: Cannot set properties of undefined (setting 'var1')
console.log("var1" in func1) //true
console.log("prototype" in func1) //false
console.log("-----接下來看hasOwnProperty函數-----")
func1.var2 = "func1自己的變數"
console.log(func1.hasOwnProperty("var2")) //true
console.log(func1.hasOwnProperty("var1")) //false
console.log("-----接下來看proto-----")
console.log(Func.hasOwnProperty("__proto__")) //false
console.log(func1.hasOwnProperty("__proto__")) //false
console.log(func1.__proto__ === Func.prototype) // true
console.log(func1.__proto__ == Func.prototype) // true
console.log(func1.prototype == Func.prototype) // false
console.log(func1.__proto__.var1) //Func進行了擴展
console.log(func1.var1) //Func進行了擴展
console.log("-----接下來看proto和prototype的區別-----")
func1.var1 = "func1進行了擴展"
console.log(func1.var1) //func1進行了擴展
console.log(Func.prototype.var1) //Func進行了擴展
擴展得到的東西到底從哪來的
那麼子func1我們前面使用了hasOwnProperty屬性,但是func1本身並沒有這個屬性,那麼它從哪來的?
function Func() { };
var func1 = new Func;
console.log(func1.var1) //undefined
Func.prototype.var1 = "Func進行了擴展"
console.log(func1.var1) //Func進行了擴展
console.log(Func.var1)
// func1.prototype.var1 = "func1進行了擴展" //Uncaught TypeError: Cannot set properties of undefined (setting 'var1')
console.log("var1" in func1) //true
console.log("prototype" in func1) //false
console.log("-----接下來看hasOwnProperty函數-----")
func1.var2 = "func1自己的變數"
console.log(func1.hasOwnProperty("var2")) //true
console.log(func1.hasOwnProperty("var1")) //false
console.log("-----接下來看proto-----")
console.log(Func.hasOwnProperty("__proto__")) //false
console.log(func1.hasOwnProperty("__proto__")) //false
console.log(func1.__proto__ === Func.prototype) // true
console.log(func1.__proto__ == Func.prototype) // true
console.log(func1.prototype == Func.prototype) // false
console.log(func1.__proto__.var1) //Func進行了擴展
console.log(func1.var1) //Func進行了擴展
console.log("-----接下來看proto和prototype的區別-----")
func1.var1 = "func1進行了擴展"
console.log(func1.var1) //func1進行了擴展
console.log(Func.prototype.var1) //Func進行了擴展
console.log("-----接下來看hasOwnProperty從哪來的-----")
console.log(Func.__proto__.__proto__.hasOwnProperty("hasOwnProperty")) // true
console.log(Func.__proto__.hasOwnProperty("hasOwnProperty")) // false
console.log(func1.__proto__.__proto__.hasOwnProperty("hasOwnProperty")) // true
從父和子那節的那張圖也可以看出,使用兩次__proto__
即可找到hasOwnProperty屬性
那麼到此也就了解了prototype和__proto__
了
附上完整程式碼兩段供測試:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
function Fun(){
}
var func1 = new Fun()
console.log(typeof Fun) // function
console.log(typeof func1) // object
console.log(func1.prototype) // undefined
console.log(typeof func1.__proto__) // object
console.log(func1.__proto__) // 一個比較複雜的object
console.log(func1.__proto__ == Fun.prototype) // true
console.log(func1.prototype == Fun.prototype) // false
Fun.prototype.var1 = "hello"
console.log(func1.var1) // hello
console.log(func1.__proto__.var1) // hello
func1.var1 = "yes"
console.log(Fun.var1) // undefined
console.log(func1.var1) // yes
console.log(Fun.prototype.var1) // hello
console.log(func1.__proto__.var1) // hello
console.log(func1.prototype) // undefined
func1.__proto__.var1 = "hhh"
console.log(func1.__proto__.var1) // hhh
console.log(Fun.prototype.var1) // hhh
console.log(Fun.__proto__.var1) // undefined
console.log("------測試原型對象裡面的proto-------")
console.log(func1.hasOwnProperty("var1")) // true
console.log(func1.hasOwnProperty("__proto__"))
console.log(Fun.hasOwnProperty("hasOwnProperty")) // false
console.log(Fun.hasOwnProperty("__proto__")) // false
console.log(Fun.__proto__.__proto__.hasOwnProperty("hasOwnProperty")) // true
console.log(Fun.__proto__.hasOwnProperty("hasOwnProperty")) // false
console.log(func1.__proto__.__proto__.hasOwnProperty("hasOwnProperty")) // true
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
function Func() { };
var func1 = new Func;
console.log(func1.var1) //undefined
Func.prototype.var1 = "Func進行了擴展"
console.log(func1.var1) //Func進行了擴展
console.log(Func.var1)
// func1.prototype.var1 = "func1進行了擴展" //Uncaught TypeError: Cannot set properties of undefined (setting 'var1')
console.log("var1" in func1) //true
console.log("prototype" in func1) //false
console.log("-----接下來看hasOwnProperty函數-----")
func1.var2 = "func1自己的變數"
console.log(func1.hasOwnProperty("var2")) //true
console.log(func1.hasOwnProperty("var1")) //false
console.log("-----接下來看proto-----")
console.log(Func.hasOwnProperty("__proto__")) //false
console.log(func1.hasOwnProperty("__proto__")) //false
console.log(func1.__proto__ === Func.prototype) // true
console.log(func1.__proto__ == Func.prototype) // true
console.log(func1.prototype == Func.prototype) // false
console.log(func1.__proto__.var1) //Func進行了擴展
console.log(func1.var1) //Func進行了擴展
console.log("-----接下來看proto和prototype的區別-----")
func1.var1 = "func1進行了擴展"
console.log(func1.var1) //func1進行了擴展
console.log(Func.prototype.var1) //Func進行了擴展
console.log("-----接下來看hasOwnProperty從哪來的-----")
console.log(Func.__proto__.__proto__.hasOwnProperty("hasOwnProperty")) // true
console.log(Func.__proto__.hasOwnProperty("hasOwnProperty")) // false
console.log(func1.__proto__.__proto__.hasOwnProperty("hasOwnProperty")) // true
</script>
</body>
</html>