本文是學習中傳思客在慕課網開的課程《前端跳槽面試必備技巧》的學習筆記。課程地址:https://coding.imooc.com/class/evaluation/129.html#Anchor。
目錄
- 創建對象有幾種方法
- 原型、構造函數、實例、原型鏈
- instanceof的原理
- new運算符
創建對象的方法
在了解原型鏈之前,首先先了解一下創建對象的幾種方式,介紹以下三種。
代碼:
<script type="text/javascript"> // 第一種方式:字面量 var o1 = {name: 'o1'} var o2 = new Object({name: 'o2'}) // 第二種方式:構造函數 var M = function (name) { this.name = name; } var o3 = new M('o3') // 第三種方式:Object.create var p = {name: 'p'} var o4 = Object.create(p)
console.log(o1)
console.log(o2)
console.log(o3)
console.log(o4)
</script>
打印結果:
對象是創建出來了,但你可能對結果很詫異,為什么不同呢?別急,慢慢來。
原型及原型鏈
先來一張容易讓人懵逼的圖
什么是原型對象?實例?構造函數?
概念就不多說了,看代碼吧
var M = function (name) { this.name = name; } var o3 = new M('o3')
- 實例就是對象,在本例中o3就是實例,M就是構造函數。
- 實例通過new一個構造函數生成的。
- 從上圖中可以知道,實例的__protpo__指向的是原型對象。
- 實例的構造函數的prototype也是指向的原型對象。
- 原型對象的construor指向的是構造函數。
再來通過下面這個圖來理解一下
那什么是原型鏈呢?
簡單理解就是原型組成的鏈,對象的__proto__它的是原型,而原型也是一個對象,也有__proto__屬性,原型的__proto__又是原型的原型,就這樣可以一直通過__proto__想上找,這就是原型鏈,當向上找找到Object的原型的時候,這條原型鏈就算到頭了。
原型對象和實例之間有什么作用呢?
通過一個構造函數創建出來的多個實例,如果都要添加一個方法,給每個實例去添加並不是一個明智的選擇。這時就該用上原型了。
在實例的原型上添加一個方法,這個原型的所有實例便都有了這個方法。
接着上面的例子繼續演示:
var M = function (name) { this.name = name; }
var o3 = new M('o3')
var o5 = new M()
o3.__proto__.say=furnction(){ console.log('hello world') } o3.say() o5.say()
打印結果
按照JS引擎的分析方式,在訪問一個實例的屬性的時候,現在實例本身中找,如果沒找到就去它的原型中找,還沒找到就再往上找,直到找到。這就是原型鏈。
補充:
只有函數有prototype,對象是沒有的。
但是函數也是有__proto__的,因為函數也是對象。函數的__proto__指向的是Function.prototype。
也就是說普通函數是Function這個構造函數的一個實例。
instanceof原理
instanceof是判斷實例對象的__proto__和生成該實例的構造函數的prototype是不是引用的同一個地址。
是返回true,否返回false。
注意:實例的instanceof在比較的時候,與原型鏈上想上找的的構造函數相比都是true。
繼續上面的代碼
那怎么判斷實例是由哪個構造函數生成的呢?這時候就要用到constructor了。
實例的原型的構造函數, obj.__proto__.constructor
new運算符
new運算符的原理
- 一個新對象被創建。它繼承自foo.prototype。
- 構造函數返回一個對象。在執行的時候,相應的傳參會被傳入,同時上下文(this)會被指定為這個新的實例。
- new foo等同於new foo(), 只能用在不傳遞任何參數的情況
- 如果構造函數反悔了一個對象,那個這個對象會取代整個new出來的結果。如果構造函數沒有返回對象,那個new出來的結果為步驟1創建的對象。
下面根據new的工作原理通過代碼手動實現一下new運算符
var new2 = function (func) { var o = Object.create(func.prototype); //創建對象 var k = func.call(o); //改變this指向,把結果付給k if (typeof k === 'object') { //判斷k的類型是不是對象 return k; //是,返回k } else { return o; //不是返回返回構造函數的執行結果 } }
驗證
經過上圖一系列折騰,不難看出,我們手動編寫的new2和new運算符的作用是一樣的。
通過這個例子,你是不是已經熟知了new的工作原理了呢
最后,回到回到第一節創建對象的方法那里。
幾種創建對象方法的不同,了解了原型鏈和new之后,是不是此題的答案就出來了,這里我就不細說了,留給讀者一些思考的時間。。。
覺得本文對你有幫助的話,點個贊再走吧