前兩天看完了 js 面向?qū)ο缶? 有所收獲, 因此趁熱打鐵做一下記錄. 這本書很薄, 只用了兩天午休的時(shí)間就看完了, 但作者的講述是十分清晰的.
引用類型和原始類型
js 中的數(shù)據(jù), 要么是一個(gè)對(duì)象, 要么從一個(gè)對(duì)象中獲取
原始類型
- 原始類型直接保存值, 引用類型保存指向內(nèi)存的指針
- 用原始類型給變量賦值時(shí), 值會(huì)直接被賦值到改變量中
- 對(duì)于原始類型, 用
typeof
函數(shù) - 原始類型擁有方法, 但它們不是對(duì)象, js 使它們用起來像對(duì)象, 是為了提供語言上的一致性體驗(yàn).
引用類型(對(duì)象)
創(chuàng)建對(duì)象
- 構(gòu)造函數(shù)和
new
操作符 - 使用字面量
內(nèi)建類型
- 六種內(nèi)建類型
- 鑒別引用類型使用
instanceof
函數(shù) - 所有類型都繼承自 Object
- 使用
Array.isArray
鑒別數(shù)組. 由于不同框架會(huì)實(shí)現(xiàn)不同的 Array 實(shí)例.
原始封裝類型
String, Number, Boolean
- 每當(dāng)讀取字符串, 數(shù)字或布爾值時(shí), 原始封裝對(duì)象會(huì)自動(dòng)創(chuàng)建
- 原始封裝對(duì)象僅存在于該語句
- 語句執(zhí)行完成后會(huì)自動(dòng)銷毀所創(chuàng)建的原始封裝對(duì)象
函數(shù)
函數(shù)也是一個(gè)對(duì)象, 內(nèi)部獨(dú)有一個(gè) [[call]]
屬性, 表明該對(duì)象可以被執(zhí)行.
兩種定義方式:
var a = function() {
}
// 這種寫法會(huì)有聲明提升, 函數(shù)定義會(huì)被提升到上下文的頂部
function a() {
}
函數(shù)的參數(shù)
- 保存在 arguments 對(duì)象中(這個(gè)對(duì)象的行為表現(xiàn)類似于數(shù)組)
- arguments 不是 Array 的實(shí)例
- arguments.length 表示期望的參數(shù)的長度
- js 不存在函數(shù)重載, 但可以根據(jù) arguments 的狀態(tài)去模擬
this 對(duì)象
- js 的每個(gè)函數(shù)的作用域中都有一個(gè) this 對(duì)象, 代表 調(diào)用改函數(shù)的對(duì)象
- 改變 this 指向的三種方法
- call(target, param1, param2, ...)
- apply(target, [params...])
- bind(target, 其余參數(shù)會(huì)被設(shè)為新函數(shù)中的命名參數(shù))
理解對(duì)象
- 屬性第一次被添加給對(duì)象時(shí), js 在對(duì)象上調(diào)用一個(gè)名為
[[put]]
的內(nèi)部方法 - 調(diào)用
[[put]]
方法在對(duì)象上創(chuàng)建了自有屬性 - 對(duì)已有屬性賦新值時(shí), 調(diào)用了內(nèi)部的
[[set]]
方法 - 使用
in
操作符判斷屬性是否存在于方法中, 其實(shí)就是在 hashtable 中查找一個(gè)鍵是否存在.in
操作符會(huì)檢查自由屬性和原型屬性 - delete 刪除一個(gè)屬性, 其實(shí)是調(diào)用了內(nèi)部的
[[delete]]
方法
屬性枚舉
- [[Enumerable]] 屬性表示屬性是否可枚舉
- 使用
for-in
循環(huán)枚舉一個(gè)對(duì)象的可枚舉屬性 - Object.keys() 得到一個(gè)對(duì)象的所有可枚舉自有屬性的名稱數(shù)組.
屬性類型
數(shù)據(jù)屬性和訪問器屬性
- 數(shù)據(jù)屬性包含值
- 訪問器屬性不包含值, 包含 setter 和 getter 兩個(gè)函數(shù)
- 只定義 getter 就是只讀
- [[Configureable]] 決定該屬性是否可配置, 手動(dòng)聲明的所有默認(rèn)屬性都是可枚舉, 可配置的
- 可以用
Object.defineProperty()
方法改變屬性特征. 需要為所有特征指定一個(gè)值, 否則布爾值類型的特征會(huì)被默認(rèn)設(shè)置為 false. - 無法將不可配置的屬性變成可配置的.
- 數(shù)據(jù)屬性有一個(gè) [[value]] 屬性, 里面保存了值. 還有一個(gè) [[writable]] 表示是否可寫.
- 訪問器屬性也有兩個(gè)額外特征, [[get]] 和 [[set]]
- 定義多重屬性使用 Object.defineProperties() 函數(shù)
- Object.getOwnPropertyDescriptor() 方法獲取屬性的特征
- [[extensible]] 是否可修改, false 時(shí)表示不可修改, 無法添加新屬性
- Object.preventExtensions() 創(chuàng)建一個(gè)不可擴(kuò)展的對(duì)象
- Object.isExtensible() 方法檢查 [[extensible]] 的值
- Object.seal() 方法創(chuàng)建一個(gè)封印的對(duì)象, [[extensible]] 為 false, 所有屬性的 [[Configureable]] 也是 false
- Object.isSealed() 方法判斷一個(gè)對(duì)象是否被封印
- Object.freeze() 方法凍結(jié)一個(gè)對(duì)象. 改對(duì)象是一個(gè)所有屬性都是只讀的 sealed 對(duì)象
- Object.isFrozen() 判斷是否被凍結(jié)
構(gòu)造函數(shù)和原型函數(shù)
構(gòu)造函數(shù)就是你用 new 創(chuàng)建對(duì)象時(shí)調(diào)用的函數(shù), 所有使用同一個(gè)構(gòu)造函數(shù)創(chuàng)建的對(duì)象都有相同的屬性和方法
- 與普通函數(shù)的區(qū)別: 首字母大寫
- 調(diào)用構(gòu)造函數(shù)時(shí), new 關(guān)鍵字會(huì)自動(dòng)創(chuàng)建 this 對(duì)象, 且類型就是構(gòu)造函數(shù)的類型.
- 也可以在構(gòu)造函數(shù)中顯式 return, 如果 return 的是對(duì)象, 則返回該對(duì)象實(shí)例; 如果是原始值, 會(huì)被忽略, 返回的依舊是對(duì)象實(shí)例.
- 忘記用 new 調(diào)用構(gòu)造函數(shù)時(shí), this 是 window
原型對(duì)象
幾乎所有函數(shù)都有 prototype
屬性, 所有創(chuàng)建的實(shí)例都共享原型對(duì)象, 且可以訪問到原型對(duì)象的屬性.
- 鑒別一個(gè)屬性是否是原型對(duì)象的屬性:
var hasPrototypeProperty(object, name) {
var result = (name in object) && !(Object.hasOwnProperty(name))
return result
}
- 對(duì)象實(shí)例通過 [[prototype]] 跟蹤其原型對(duì)象
- 當(dāng) new 一個(gè)對(duì)象時(shí), 構(gòu)造函數(shù)的原型對(duì)象會(huì)被賦值給該對(duì)象的 [[prototype]] 屬性
- 可以使用 Object.getPrototypeOf() 方法讀取 [[prototype]] 屬性的值
- 對(duì)于所有的泛用對(duì)象, 其 [[prototype]] 屬性始終指向 Object.prototype
- 使用
_proto_
屬性可直接讀寫 [[prototype]] 屬性. - 原型屬性無法賦值
- 可以直接用字面量替換原型對(duì)象, 但需要注意的是 constructor 屬性需要手動(dòng)替換, 不指定的話默認(rèn)是指向 Object
- 可以給內(nèi)建對(duì)象的 prototype 增加方法, 但不推薦, 因?yàn)榭赡軙?huì)誤導(dǎo)其他程序員
繼承
js 中的繼承是通過原型對(duì)象鏈繼承的.
對(duì)象通常都繼承自 Object.prototype, 因此也都有一下方法:
hasOwnProperty()
properTyIsEnumerable()
isPrototypeOf()
// 當(dāng)一個(gè)操作符作用于一個(gè)對(duì)象時(shí), 就會(huì)調(diào)用 valueOf() 方法
// 原始封裝類型重寫了該方法, 使 String 有不同的表現(xiàn)形式
valueOf()
// 當(dāng) valueOf 方法返回的是一個(gè)引用時(shí), 就會(huì)調(diào)用 toString 方法
toString()
對(duì)象的繼承
- 只需要指定哪個(gè)對(duì)象是新對(duì)象的 [[prototype]], 也可以用 Object.create() 方法顯式創(chuàng)建
- 所有繼承鏈的末端通常是 Object.prototype, 其 [[prototype]] 為 null
- 訪問父類方法時(shí), 使用 call 或 apply 指定 this
對(duì)象模式
私有成員:
- 通過
_name
來約束 - 通過立即函數(shù)來返回對(duì)象
- 通過閉包返回對(duì)象, 在閉包內(nèi)調(diào)用外部的變量(模塊模式)
混入:
一個(gè)對(duì)象在不改變?cè)蛯?duì)象鏈的情況下, 得到了另一個(gè)對(duì)象的屬性和方法, 被稱為混入
混入的實(shí)現(xiàn):
- 使用 for-in 淺拷貝
- 使用 foreach 實(shí)現(xiàn) mixin 函數(shù)
var mixin = function(receiver, supplier) {
Object.keys(supplier).forEach(function(property) {
var descriptor = Object.getOwnPropertyDescriptor(supplier, property)
Object.defineProperty(receiver, property, descriptor)
return receiver
})
}
作用域安全的構(gòu)造函數(shù)
在函數(shù)內(nèi)判斷自己是否被 new 調(diào)用, 處理不同的情況