令人頭疼的JavaScript對象

對象創建

創建對象的方式有兩種

第一種是使用對象字面量的方式,也就是聲明形式

let obj = {
    key: value
    //...
}

第二種是使用構造方式

let obj = new Object()
obj.key = value

這兩種方式雖然形式不同,但產生的是同一種對象

在實際應用中我們也會使用構造函數來生成實例對象

function Person(name) {
  this.name = name
}

Person.prototype.say = function () {
  return 'I am ' + this.name
}

let p = new Person('feng')

在ES6中,我們還可以利用class關鍵字來創建對象

class Person {
  constructor(name) {
    this.name = name
  }

  say() {
    return 'I am ' + this.name
  }
}
typeof Person // "function"
Person === Person.prototype.constructor // true
let p = new Person('feng')
p.say === Person.prototype.say // true

我們可以把ES6的class看做是一種語法糖,ES6的類作爲構造函數的另一種寫法。ES6的類與構造函數的主要區別在於:ES6類必須使用new調用,而構造函數可以當作普通函數執行

屬性

對象包含了存儲在特定命名的位置上的任意類型的值,我們稱這些值爲屬性。在對象中,屬性名是字符串

let obj = {
    a: 1 // 屬性
}

訪問在obj對象中的a,有兩種方法

第一種是使用.操作符,通常稱爲屬性訪問,只能訪問與標識符相同命名規範的屬性

obj.a // 1

第二種是使用[]操作符,通常稱爲鍵訪問,可以訪問任何兼容 UTF-8/unicode 的字符串類型的屬性名

let a = 'a'
obj['a'] // 1
obj[a] // 1

屬性描述符

在JavaScript中,對象的屬性用屬性描述符來表示。屬性有四種性質:值、可寫性、可配置性、可枚舉性

let obj = {
	a: 1
}
Object.getOwnPropertyDescriptor( obj, "a" )
// { value: 1, writable: true, enumerable: true, configurable: true }
可寫性

writablefalse時,屬性變爲不可寫,也就意味着值不可改變

let obj = {}
Object.defineProperty( obj, "a", {
	value: 1,
	writable: false, // 不可寫
	configurable: true,
	enumerable: true
} )
obj.a = 2
obj.a // 1
可配置性

configurablefalse時,就意味着我們不可以使用Object.defineProperty來修改該屬性的描述符定義,同時也無法使用delete操作符對它進行刪除

唯一例外的是,當configurablefalse時,writeable也可以從true變爲false

let obj = {
	a: 1
}

Object.defineProperty( obj, "a", {
	value: 2,
	writable: true,
	configurable: false,
	enumerable: true
} )

Object.defineProperty( obj, "a", {
	value: 3,
	writable: true,
	configurable: true,
	enumerable: true
} ) // TypeError

Object.defineProperty( obj, "a", {
	value: 3,
	writable: false,
	configurable: false,
	enumerable: true
} )

obj.a // 3
delete obj.a
obj.a // 3
可枚舉性

enumerablefalse時,就意味着這個屬性不會在特定的枚舉操作中出現

let obj = {
    a: 1,
    b: 2
}
Object.defineProperty( obj, "a", { enumerable: false } )
obj.a // 1
for (let k in obj) {
	console.log( k, obj[k] )
} // b 2

setter和getter

利用取值函數和存值函數可以攔截指定屬性的存取行爲

let obj = {
    b: 1,
    set a(value) {
        this.b = value*10
    }
}

obj.a = 1
obj.b // 10

Object.defineProperty( obj, "c", {
    get: function(){ return this.b * 2 },
    enumerable: true
} )
obj.c // 20

原型

在JavaScript中只有對象,沒有類

對象之間用原型聯繫起來

__proto__prototype

prototype 是構造函數的屬性,而 __proto__ 是對象的屬性

每個對象的__proto__屬性指向自身構造函數的prototype

Object.prototype.__proto__ // null
Object.__proto__ === Function.prototype // true
Function.__proto__ === Function.prototype //true
let obj = {}
obj.__proto__=== Object.prototype // true
Function.prototype // [Function]
Function.prototype.__proto__ === Object.prototype // true
function Foo() {}
Foo.__proto__ === Function.prototype //true
let f1 = new Foo()
f1.__proto__ === Foo.prototype // ture

雖然有一點不可思議,但是結合上述文字以及代碼,我們可以得到如下結論:

  • 原型鏈的最頂端是內建的Object.prototype,在向上思考就是哲學的範疇了
  • Object對象是由Funtion函數創建出來的
  • Function是一種對象,因爲對象是由函數創建出來的,所以它是Function函數創建出來的
  • 使用字面量的方式創建的對象是由Object對象創建的
  • Function.prototype指向一個對象,並且這個對象是由Object對象創建的
  • 一個自定義的函數是由Function函數創建的
  • 構造函數可以創建對象

簡單的說,對象以自己爲模版可以創建更多的對象,而函數既能創建函數又能創建對象,同時函數也是一種對象

鏈接

原型鏈的機制是一種存在於一個對象上的內部鏈接,它指向一個其他對象

原型鏈使得代碼的複用性增強,同時對象的的屬性的訪問變得複雜起來,

let obj = {
    a: 1
}
let obj1 = Object.create( obj )
obj1.__proto__ === obj //true
obj1.a // 1
obj.b = 2
obj1.b // 2
Object.defineProperty( obj, "b", { writable: false } )
obj1.b = 3
obj1.b // 2
Object.defineProperty( obj, "c", {
    get: function(){ return this.b * 2 },
    enumerable: true
} )
obj1.c // 4
for (let k in obj1) {
	console.log(k, obj1[k])
}
/*
a 1
b 2
c 4
*/

obj對象爲模板,創建出了obj1對象,或者說obj1對象的原型是obj對象。通過觀察,我們可以推論,在對對象的屬性進行訪問時,先查找對象內部是否存在名稱匹配的屬性,如果不存在,就沿着原型鏈向上查找,直到找到匹配的屬性,或者已經到達原型鏈的最頂端

構造器

構造器是什麼不重要,重要的是它做了什麼

function Foo() {}
Foo.prototype.constructor === Foo //true
function Bar() {}
Foo.prototype = new Bar()
Foo.prototype.constructor = Foo
let obj = new Foo()
obj // Foo{}
obj.__proto__.__proto__ === Bar.prototype // true

挖一個坑

由於javascript特有的原型機制,在實際項目中可能會遇到兩種截然不同的設計方式,一種是傳統的面向對象的設計方式,另一種則是我在《你不知道的JS》一書中所瞭解的面向委託的設計,讀者感興趣可以去自己去了解一下

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章