本文主題git
// 經過字面量
const obj1 = { name: 'guodada' }
const obj11 = new Object({ name: 'guodada' })
// 經過構造函數
function Pershon() {
this.name = 'guodada'
}
const obj2 = new Pershon()
// Object.create
const obj3 = Object.create({ name: 'guodada' })
複製代碼
這裏着重點講原型、構造函數、實例、原型鏈他們之間的關係,由於這也是面試常問、也是容易混淆的點。github
函數被 new 關鍵字調用時就是構造函數。面試
new
關鍵字的內部實現機制(舉例說明):瀏覽器
function Person(name) {
this.name = name
}
const person = new Person('guodada')
複製代碼
Person.prototype
;var obj = {} // 建立一個空對象
obj.__proto__ = constructor.prototype //添加 __proto__ 屬性,並指向構造函數的 prototype 屬性。
constructor.call(this) // 綁定this
return obj
複製代碼
(建議看下去再回來看 new 操做符作了什麼。。。)函數
每個函數都有一個 prototype 屬性。這個屬性指向函數的原型對象。post
Person.prototype // {constructor: Pershon(),__proto__: Object}
複製代碼
__proto__
那麼咱們該怎麼表示實例與實例原型 ?學習
每個 JavaScript 對象(除了 null )都具備的一個屬性,叫
__proto__
,這個屬性會指向該對象的原型ui
person.__proto__ === Person.prototype // true
複製代碼
既然實例對象和構造函數均可以指向原型,那麼原型是否有屬性指向構造函數或者實例呢?this
Person.prototype.constructor === Person
複製代碼
總結一下構造函數、實例原型、和實例之間的關係spa
Person.prototype // 構造函數['prototype'] 指向函數原型
person.__proto__ === Person.prototype // 實例['__proto__'] 指向函數原型
Person.prototype.constructor === Person // 函數原型['constructor'] 指向構造函數
複製代碼
每個實例都包含一個指向原型對象的
__proto__
指針,依賴這條關係,層層遞進,就造成了實例與原型的鏈條。
function Person() {}
Person.prototype.name = 'Kevin'
var person = new Person()
person.name = 'Daisy'
console.log(person.name) // Daisy
delete person.name
console.log(person.name) // Kevin
複製代碼
在這個例子中,咱們給實例對象 person
添加了 name
屬性,當咱們打印 person.name
的時候,結果天然爲 Daisy
。
可是當咱們刪除了 person
的 name
屬性時,讀取 person.name
,從 person 對象中找不到 name 屬性就會從 person
的原型也就是 person.__proto__
,也就是 Person.prototype
中查找,幸運的是咱們找到了 name
屬性,結果爲 Kevin
。
原型的終點是
null
,由於null
沒有proto
屬性。
關係圖也能夠更新爲:
順便還要說一下,圖中由相互關聯的原型組成的鏈狀結構就是原型鏈,也就是藍色的這條線。
js 的基本類型有 String
, Undefined
, Boolean
, Number
, Null
, Symbol
, 咱們通常能夠經過 typeof
來判斷值的類型
typeof 1 === 'number'
typeof function() {} === 'function'
typeof null === 'object' // 注意!
// 判斷引用類型
typeof {} === 'object'
typeof [] === 'object'
複製代碼
而引用類型的判斷這是經過 instanceof
,用來判斷實例是否是另外一個對象的引用.
person instanceof Person // true
複製代碼
原理就是: 實例['proto'] === 構造函數['prototype'], 可是值得注意的是 instanceof
會經過原型鏈繼續往下找。
person instanceof Object // true
person.__proto__ === Person.prototype // true
person.__proto__.constructor === Person // true
複製代碼
function A() {
B = function() {
console.log(10)
}
return this
}
A.B = function() {
console.log(20)
}
A.prototype.B = function() {
console.log(30)
}
var B = function() {
console.log(40)
}
function B() {
console.log(50)
}
A.B()
B()
A().B()
B()
new A.B()
new A().B()
// 請在瀏覽器環境下運行
複製代碼
上述題目答案是多少呢,你們不妨試試。在看下去(ps 這題還涉及到了執行上下文的概念--考察了函數聲明和函數表達式)
答案就在筆者以前寫過的文章中 經過一道面試題來學習原型/原型鏈-函數聲明/函數表達式
思考完揭曉答案
A.B()
=> 在 A 原型對象上找到 A.B = function() { console.log(20) }
answer 20B()
=> 同名的函數表達式和函數聲明同時存在時 老是執行表達式 answer 40A().B()
A()
執行函數 A ==> 1.變量 B 從新賦值函數 2.返回 this(window).B()
執行全局下的 B 函數 已經被從新賦值 因此輸出 10B()
=> 上面的代碼執行過 A 函數了,此時全局下的 B 函數輸出 10new A.B()
=> new 執行了 A.B = function () {console.log(20)};
new A().B()
new
執行構造函數 A => objA.__proto__ = A.prototype
.B()
在 A 的原型對象中查找 B; A.prototype
指向函數的原型對象A.prototype.B = function () {console.log(30)}
輸出 30A.B() // 20
B() // 40
A().B() // 10
B() // 10
new A.B() // 20
new A().B() // 30
複製代碼
若有不對之處,請指出~