[JavaScript學習筆記]對象、原型、原型鏈

對象&原型鏈

面向對象編程(Object Oriented Programming,OOP),將現實世界的復雜關系抽象為一個個對象,由對象之間的分工合作完成對真實世界的模擬。

對象

對象是一個容器,有屬性(property)和方法(method)。

JavaScript中,每一個對象都繼承自另一個對象。對象的構建由構造函數實現,構造函數可理解為對象的模板。由于實例對象由其構造函數創建,每一個對象都繼承于另一個對象,在這里被繼承的對象稱之為“原型(prototype)”對象。其中只有null除外,它沒有自己的原型對象。

在JavaScript,通過構造函數生成實例對象時,會為實例對象分配原型對象。同時每個構造函數中都有一個prototype屬性,該屬性就是實例對象的原型對象,定義在prototype對象上的屬性和方法,都會被所有實例對象共享。因此,每一個實例對象都會有其原型對象,表示實例對象繼承于原型對象,同時實例對象可以調用其屬性或方法:

function animal (name){
  this.name = name
}
animal.prototype.color = "white"  // 給animal的prototype屬性定義一個屬性

var dog = new animal('阿毛')
dog.color  // 'white'             // 繼承其構造函數的屬性

對象最大的作用就是節省了公有屬性的內存占用,并且提供了復用。

原型鏈

對象的屬性或方法,可能定義在其自身或者定義在它的原型對象。由于原型對象本身也是對象,有自己的原型,這就形成了原型鏈。所有對象的原型最終追溯到Object.prototype,Object的原型指向null,原型鏈到null終止,null沒有任何屬性。

原型鏈的作用是,讀取對象某個屬性,JavaScript引擎優先尋找自身的屬性,如果找不到再到原型找,如果還是找不到則到原型的原型找,如果到Object.prototype還是找不到就返回undefined。如果自身和原型都有同名屬性,優先讀取自身屬性,這個稱之為“覆蓋”(overriding)。

constructor

prototype對象有一個constructor屬性,prototype所在的構造函數。需要注意對象的繼承不一定要有構造函數

function Constr(){}
var x = new Constr()

var y = new x.constructor() 
y instanceof Constr // true
// 可以通過實例對象間接生成新的實例對象

Constr.prototype.createCopy = function(){
  return new this.constructor()
}
// 提供了實例方法中調用自身構造函數

this 指向

構造函數中的this指向實例對象,其原理為:

var fn = function (){
  this.foo = 'bar'
}
var f = new fn()
// 等同于
var f = Object.setPrototypeOf({}, fn.prototype)
fn.call(f)   // this指向實例對象
// new相當于語法糖? call的第一個參數為實例對象,所以this指向實例對象

instanceof 運算符

返回一個布爾值,表示指定對象是否為某個構造函數的實例,其實質為檢查右邊的構造函數是否在左邊的原型鏈上。

var v new Vehicle()
v instanceof Vehicle // true
// 等同于
Vehicle.prototype.isPrototypeOf(v)

常用方法

Object.getPrototypeOf():

// 獲取原型對象的標準方法
Object.getPrototypeOf()  
Object.getPrototypeOf([]) === Array.prototype // true 

Object.setPrototypeOf():

// 設置原型對象,返回新對象,接受兩個參數(現有對象,原型對象)
Object.setPrototypeOf()  
var a = {x:1}
var b = Object.setPrototypeOf({},a)
b.x // 1  
//  b繼承于a 所以可以使用a對象的所有屬性和方法,但是b不是由名為a的構造函數生成,有區別
//  等同于var b = Object.create(a)

var fn = function (){
  this.foo = 'bar'
}
var f = new fn()
// 等同于
var f = Object.setPrototypeOf({}, fn.prototype)
fn.call(f)  // 因此this指向實例對象

Object.create():

var A = {
  print: function(){
    console.log('hello')
  }
}
var B = Object.create(a)
B.print() //hello
B.print === A.print // true
// 等同于
var A = function(){}
A.prototype = {
  print:function(){
    console.log('hello')
  }
}
var B = new A()
B.print === A.prototype.print

個人認為還是有區別的,一個是在原型上的屬性,一個是A自身的屬性,雖然都是可調用

new

new命令執行構造函數,返回一個實例對象:

var a = function (){
  this.bala = 123;
}
var b = new a()
b.bala // 123

其原理:

  1. 創建一個空對象,作為將要返回的對象實例
  2. 將這個空對象的原型指向prototype屬性
  3. 將這個空對象的值賦給函數內部的this關鍵字
  4. 開始執行構造函數內部代碼

參考前面的方法,總結為

var a = function(){
  this.balaba = 123;
}
var b 
b = {}
Object.setPrototypeOf(b, a)
a.call(b)

忘記new的處理方式

// 使用嚴格模式
function foo (a,b){
  'use strict';
  this.a = 'a';
  this.b = 'b';
}
foo() // 嚴格模式下,this不能指向全局對象,默認指向undefined,JavaScript不允許對undefined添加屬性,于是報錯

// instanceof

function bar (){
  if(!(this instanceof bar)){
    return new bar()
  }
  this.a = 1
  this.b = 2
}
bar() 

命名

構造函數大寫開頭,實例也是,表示一種對象

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容