關注前端小謳,閱讀更多原創技術文章
建立對象
- 建立單個對象:Object 構造函數 和 對象字面量
- 缺點:使用一個接口建立不少對象,產生大量重複代碼
相關代碼 →javascript
工廠模式
- 抽象了建立具體對象的過程
- 用函數來封裝以特定接口建立對象的細節
function createPerson(name, age, job) {
var o = new Object()
o.name = name
o.age = age
o.job = job
o.sayName = function () {
console.log(this.name)
}
return o
}
var person1 = createPerson('Nicholas', 29, 'Engineer')
var person2 = createPerson('Greg', 27, 'Doctor')
console.log(person1)
console.log(person2)
- 工廠模式解決了建立多個類似對象的問題,但沒有解決對象識別問題——即怎樣知道一個對象的類型
構造函數模式
- 除了 Object 和 Array 等原生構造函數,還能夠建立自定義的構造函數
function Person(name, age, job) {
this.name = name
this.age = age
this.job = job
this.sayName = function () {
console.log(this.name)
}
}
var person1 = Person('Nicholas', 29, 'Software Engineer')
var person2 = Person('Greg', 27, 'Doctor')
- 構造函數模式 vs 工廠模式:① 不顯式的建立對象;② 直接將屬性和方法賦給 this 對象;③ 沒有 return
- 構造函數 new 一個對象後:① 建立了一個新對象;② 將構造函數的做用域(即 this)賦給新對象;③ 執行構造函數中的代碼(即:爲這個對象添加新屬性);④ 返回新對象
- 構造函數用大寫字母開頭,建立實例時用 new 操做符
- 建立的對象的 constructor 屬性指向構造函數
- 建立的對象既是 Object 的實例,又是構造函數的實例
console.log(person1.constructor === Person) // true,constructor 屬性指向構造函數
console.log(person2.constructor === Person) // true,constructor 屬性指向構造函數
console.log(person1 instanceof Object) // true,是 Object 的實例
console.log(person1 instanceof Person) // true,也是 Person 的實例
console.log(person2 instanceof Object) // true,是 Object 的實例
console.log(person2 instanceof Person) // true,也是 Person 的實例
- 能夠將自定義構造函數的實例標識爲一種特定的類型,這是構造函數模式賽過工廠模式的地方
- 以該方法定義的構造函數是定義在 Global 對象中的,在瀏覽器中則是 window 對象
// 構造函數vs普通函數
var person3 = new Person('Nicholas', 29, 'Software Engineer') // 用構造函數建立對象
person3.sayName() // 'Nicholas'
Person('Greg', 27, 'Doctor') // 不使用new操做符,直接調用
global.sayName() // 直接調用函數時,this指向Global對象(瀏覽器中指向window對象)
var o = new Object() // 新對象o
var p = new Object() // 新對象p
Person.call(o, 'Kristen', 25, 'Nurse') // 擴充做用域,在對象o中調用Person()函數,call()分別傳入每一個參數
Person.apply(p, ['Kristen', 25, 'Nurse']) // 擴充做用域,在對象p中調用Person()函數,apply()傳入參數數組
o.sayName() // 'Kristen'
p.sayName() // 'Kristen'
- 構造函數的問題在於,對象的每一個方法都要在每一個實例上從新建立一遍,既「每定義一個函數,就實例化一個對象」
- 而建立 2 個完成一樣任務的 Function 實例沒有必要
function Person2(name, age, job) {
this.name = name
this.age = age
this.job = job
this.sayName = new Function(console.log(this.name)) // 與聲明函數邏輯等價,每建立一個對象就要建立一個Function實例
}
console.log(person1.sayName === person2.sayName) // false,新對象的2個方法的做用域鏈和標識符解析不一樣
- 將對象的方法移到構造函數外部,避免屢次建立 Function 實例
function Person3(name, age, job) {
this.name = name
this.age = age
this.job = job
this.sayName = sayName
}
function sayName() {
console.log(this.name) // 將sayName設置成全局函數
}
var person4 = new Person('Nicholas', 29, 'Software Engineer')
- 構造函數仍未解決的問題:① 建立的全局函數實際上只須要被某個對象中調用;② 若對象有多個方法,則需建立不少全局對象
原型模式
- 每一個函數都有 prototype 原型屬性,該屬性是一個指針,指向函數的原型對象,幷包含特定類型的全部實例共享的屬性和方法,即「經過調用構造函數-而建立的那個對象實例的-原型對象」
- 使用原型對象的好處是,其全部對象實例共享其所包含的屬性和方法
理解原型對象
function PersonPrototype() {}
PersonPrototype.prototype.name = 'Nicholas' // 爲PersonPrototype的原型對象添加屬性
PersonPrototype.prototype.age = 29 // 爲PersonPrototype的原型對象添加屬性
PersonPrototype.prototype.job = 'Software Engineer' // 爲PersonPrototype的原型對象添加屬性
PersonPrototype.prototype.sayName = function () {
// 爲PersonPrototype的原型對象添加方法
console.log(this.name)
}
var person5 = new PersonPrototype()
var person6 = new PersonPrototype()
person5.sayName() // 'Nicholas'
person6.sayName() // 'Nicholas'
console.log(person5.sayName === person6.sayName) // true,prototype上建立的屬性和方法,由新對象的全部實例共享
- 原型對象自動得到 constructor(構造函數)屬性,指向 prototype 屬性所在函數的指針,即構造函數
console.log(PersonPrototype.prototype.constructor) // Function: PersonPrototype構造函數
console.log(PersonPrototype === PersonPrototype.prototype.constructor) // true,都指向構造函數
- 實例內部包含[[Prototype]]指針,指向實例的構造函數的原型對象,但沒有標準的方式訪問[[Prototype]]
- 在瀏覽器中,可用
__proto__
屬性實現[[Prototype]]的功能
console.log(person5.__proto__) // 原型對象,PersonPrototype {name: 'Nicholas',age: 29,job: 'Software Engineer',sayName: [Function] }
console.log(person5.__proto__ === PersonPrototype.prototype) // true,都指向原型對象
console.log(person5.__proto__.constructor) // Function: PersonPrototype構造函數
- 原型對象的 isPrototypeOf()方法,檢測實例中否有指向原型對象的指針
console.log(PersonPrototype.prototype.isPrototypeOf(person5)) // true,person5包含指向PersonPrototype的原型對象的指針
console.log(PersonPrototype.prototype.isPrototypeOf(person1)) // false,person1不包含指向PersonPrototype的原型對象的指針
- ES5 追加 Object.getPrototypeOf()方法,參數爲實例,返回實例的構造函數的原型對象
console.log(Object.getPrototypeOf(person5)) // 原型對象
console.log(Object.getPrototypeOf(person5) === person5.__proto__) // true,都指向原型對象
console.log(Object.getPrototypeOf(person5) === PersonPrototype.prototype) // true,都指向原型對象
console.log(Object.getPrototypeOf(person5).name) // 'Nicholas'
console.log(Object.getPrototypeOf(person5).constructor) // Function: PersonPrototype構造函數
代碼讀取對象屬性的搜索過程:前端
- 1.搜索對象實例自己 -> 有屬性 → 返回屬性值 -> 結束
- 2.對象實例自己無屬性 -> 搜索原型對象 → 有/無屬性 → 返回屬性值/undefined → 結束
- 能夠經過實例訪問原型中屬性的值,但沒法經過實例重寫原型中屬性的值
- 若是添加的實例屬性與原型的屬性同名,則實例屬性屏蔽原型中的屬性
- 刪除同名的實例屬性,可恢復被屏蔽的原型的屬性
var person7 = new PersonPrototype()
person7.name = 'Greg'
console.log(person7.name) // 'Greg',來自實例
console.log(person5.name) // 'Nicholas',來自原型
delete person7.name
console.log(person7.name) // 'Nicholas',來自原型
- 使用 hasOwnProperty('屬性') 檢測屬性存在於實例 or 原型,存在於實例返回 true
var person8 = new PersonPrototype()
var person9 = new PersonPrototype()
console.log(person8.hasOwnProperty('name')) // false,name不存在在person8的實例中
person8.name = 'Simon'
console.log(person8.name) // 'Simon',來自實例
console.log(person8.hasOwnProperty('name')) // true,name存在在person8的實例中
console.log(person9.name) // 'Nicholas',來自原型
console.log(person9.hasOwnProperty('name')) // false,name不存在在person8的實例中
delete person8.name
console.log(person8.name) // 'Nicholas',來自原型
console.log(person8.hasOwnProperty('name')) // false,person8實例的name屬性已被刪除
- 可在原型對象上調用 Object.getOwnPropertyDescriptor(),獲取原型屬性的描述符
console.log(Object.getOwnPropertyDescriptor(person8, 'name')) // undefined,person8實例上沒有name屬性
console.log(Object.getOwnPropertyDescriptor(person8.__proto__, 'name')) // {value: 'Nicholas',writable: true,enumerable: true,configurable: true},原型對象的name屬性描述符
原型與 in 操做符
- 單獨使用 in:對象可以訪問指定屬性則返回 true,不管屬性在實例中仍是原型中
function PersonIn() {}
PersonIn.prototype.name = 'Nicholas'
PersonIn.prototype.age = 29
PersonIn.prototype.job = 'Software Engineer'
PersonIn.prototype.sayName = function () {
console.log(this.name)
}
var person9 = new PersonIn()
var person10 = new PersonIn()
console.log(person9.hasOwnProperty('name')) // false,實例person9中不含name屬性
console.log('name' in person9) // true,經過person9能夠訪問到name屬性
person9.name = 'Greg'
console.log(person9.name); // 'Greg',來自實例
console.log(person9.hasOwnProperty('name')) // true,實例person9中包含name屬性
console.log('name' in person9) // true,經過person9能夠訪問到name屬性
console.log(person10.name); // 'Nicholas',來自原型
console.log(person10.hasOwnProperty('name')) // false,實例person10中不含name屬性
console.log('name' in person10) // true,經過person10能夠訪問到name屬性
delete person9 'name'
console.log(person9.name); // 'Nicholas',來自原型
console.log(person9.hasOwnProperty('name')) // false,實例person9中不含name屬性
console.log('name' in person9) // true,經過person9能夠訪問到name屬性
- 同時使用 hasOwnProperty 和 in,判斷屬性存在於 對象 or 原型
function hasPrototypeProperty(object, name) {
return !object.hasOwnProperty(name) && name in object
}
var person11 = new PersonIn()
console.log(hasPrototypeProperty(person11, 'name')) // true,!false && true
person11.name = 'Greg'
console.log(hasPrototypeProperty(person11, 'name')) // false,!true && true
- for-in 循環使用 in:返回全部可以經過對象訪問的、可枚舉的屬性(不管來自實例仍是原型),屏蔽了原型中不可枚舉的屬性([[Enumerable]]爲 false 的屬性,如 constructor 和 prototype)
for (var attr in person11) {
console.log(`${attr}:${person11[attr]}`)
/*
name:Greg
age:29
job:Software Engineer
sayName:function () {
console.log(this.name)
}
*/
}
- Object.keys():接收一個對象做爲參數,返回該對象上(僅該對象自身)可枚舉的屬性的數組
var keys = Object.keys(PersonIn.prototype) // 原型對象的全部可枚舉屬性
console.log(keys) // [ 'name', 'age', 'job', 'sayName' ]
var person12 = new PersonIn()
person12.name = 'Bob'
person12.age = 31
var p12keys = Object.keys(person12) // person12的全部可枚舉屬性
console.log(p12keys) // [ 'name', 'age' ]
- Object.getOwnPropertyNames():獲取該對象上(僅該對象自身)全部屬性的數組,不管是否可枚舉
var keys = Object.getOwnPropertyNames(PersonIn.prototype) // 原型對象的全部屬性,包含不可枚舉
console.log(keys) // [ 'constructor', 'name', 'age', 'job', 'sayName' ],原型對象都包含constructor屬性,指向構造函數
var p12keys = Object.getOwnPropertyNames(person12) // person12的全部屬性,包含不可枚舉
console.log(p12keys) // [ 'name', 'age' ]
更簡單的原型語法
- 用包含全部屬性和方法的新對象字面量來重寫整個原型對象
function PersonLiteral() {}
PersonLiteral.prototype = {
name: 'Nicholas',
age: 29,
job: 'Software Engineer',
sayName: function () {
console.log(this.name)
},
}
- 將構造函數的 prototype 屬性設置爲一個以對象字面量形式建立新對象,其 constructor 屬性再也不指向原構造函數,而是
新對象的 constructor 屬性,即 Object 構造函數
var friend = new PersonLiteral()
console.log(friend instanceof Object) // true,friend是Object的實例
console.log(friend instanceof PersonLiteral) // true,friend是PersonLiteral的實例
console.log(friend.constructor === PersonLiteral) // false,constructor屬性變成了新對象——即對象字面量的constructor
console.log(friend.constructor === Object) // true,新對象的constructor指向Object
- 能夠在對象字面量裏設置 constructor 屬性,讓其指向原構造函數
- 這樣設置 constructor 屬性屬於「直接在對象上定義的屬性」,會致使 PersonLiteral2.prototype 的 constructor 屬性的[[Enumerable]]爲 true,能夠被循環返回
function PersonLiteral2() {}
PersonLiteral2.prototype = {
constructor: PersonLiteral2, // 直接在對象上定義constructor,指向原構造函數
name: 'Nicholas',
age: 29,
job: 'Software Engineer',
sayName: function () {
console.log(this.name)
},
}
var friend2 = new PersonLiteral2()
console.log(friend2.constructor === PersonLiteral2) // true,constructor再次指向原構造函數
console.log(friend2.constructor === Object) // false
var keys = Object.keys(PersonLiteral2.prototype)
console.log(keys) // [ 'constructor', 'name', 'age', 'job', 'sayName' ],由於constructor是「直接在對象上定義的屬性」
- 用 Object.defineProperty()修改對象字面量中 constructor 屬性的特性,以兼容 ES5 的 javascript 引擎
Object.defineProperty(PersonLiteral2.prototype, 'constructor', {
enumerable: false,
value: PersonLiteral2,
})
var keys = Object.keys(PersonLiteral2.prototype)
console.log(keys) // [ 'name', 'age', 'job', 'sayName' ],constructor的enumerable已被設置爲false
原型的動態性
- 對原型對象所作的任何修改都當即從實例上反映出來,即便先建立實例後修改原型
function Person4() {}
var friend3 = new Person4()
Person4.prototype.sayHi = function () {
console.log('Hi')
}
friend3.sayHi() // 'Hi',先在friend3實例中搜索sayHi屬性,沒有找到則繼續找原型對象
- 重寫整個原型對象,會切斷構造函數與最初原型之間的聯繫,而實例的[[Prototype]]指針指向最初的原型對象
Person4.prototype = {
constructor: Person4,
name: 'Nicholas',
age: 29,
job: 'Software Engineer',
sayName: function () {
console.log(this.name)
},
}
console.log(friend3.__proto__) // Person4 { sayHi: [Function] },最初的原型對象
console.log(friend3.__proto__ === Person4.prototype) // false,實例的__proto__指向最初的原型對象,重寫整個原型切斷了構造函數與最初原型之間的聯繫
friend3.sayName() // error:friend3.sayName is not a function
原生對象的原型
- 全部原生的引用類型(Array、Object、String...),都是用原型模式建立的,在其構造函數的原型上定義了方法
console.log(Array.prototype) // 在瀏覽器中查看Array的原型對象,包含sort()等方法
console.log(String.prototype) // 在瀏覽器中查看Array的原型對象,包含substring()等方法
- 能夠像修改自定義對象的原型同樣,修改原生對象的原型,添加或刪除方法
- 不推薦修改原生對象的原型,可能會引發衝突或重寫原生方法
String.prototype.startsWith = function (text) {
// 給String的原型對象添加startsWith方法
return this.indexOf(text) === 0
}
var msg = 'Hello World'
console.log(msg.startsWith('Hello')) // true
console.log(msg.startsWith('World')) // false
delete String.prototype.startsWith
console.log(msg.startsWith('Hello')) // error
原型對象的問題
- 原型模式最大的問題是由其共享的本性致使的,尤爲對於包含引用類型的屬性,對實例的數組、對象等引用類型的屬性進行增刪改而非從新定義時,會對原型的引用類型屬性形成影響
function PersonProblem() {}
PersonProblem.prototype = {
constructor: PersonProblem,
name: 'Nicholas',
age: 29,
job: 'Software Engineer',
friends: ['Shelby', 'Court'],
sayName: function () {
console.log(this.name)
},
}
var person13 = new PersonProblem()
var person14 = new PersonProblem()
person13.name = 'Greg' // 從新定義,在實例中屏蔽原型的屬性
person13.friends.push('Van') // 非從新定義,而是向原型的數組中添加一個字符串
console.log(person13.name) // 'Greg',從實例得到
console.log(person14.name) // 'Nicholas',從原型中得到
console.log(person13.friends) // [ 'Shelby', 'Court', 'Van' ],從原型中得到
console.log(person14.friends) // [ 'Shelby', 'Court', 'Van' ],從原型中得到
console.log(person13.friends === person14.friends) // true
var person15 = new PersonProblem()
person15.friends = [] // 從新定義,在實例中屏蔽原型的屬性
console.log(person15.friends) // [],從實例得到
console.log(person13.friends) // [ 'Shelby', 'Court', 'Van' ],從原型中得到
console.log(person14.friends) // [ 'Shelby', 'Court', 'Van' ],從原型中得到
組合使用構造函數模式和原型模式
- 構造函數模式用於定義實例屬性,原型模式用域定義方法和共享的屬性
- 每一個實例都有本身的一份實例屬性的副本,同時共享着對方法的引用,最大限度節省內存
- 該模式是目前 ECMAScript 中使用最普遍、認同度最高的建立自定義類型的方法
function PersonMix(name, age, job) {
// 在構造函數定義實例屬性
this.name = name
this.age = age
this.job = job
this.friends = ['Shelby', 'Court']
}
PersonMix.prototype = {
// 在原型定義方法和共享的屬性
constructor: PersonMix, // 直接在對象上定義constructor屬性,指向構造函數,[[Enumerable]]爲true
sayName: function () {
console.log(this.name)
},
}
var person16 = new PersonMix('Nicholas', 29, 'Software Engineer')
var person17 = new PersonMix('Greg', 27, 'Doctor')
person16.friends.push('Van') // 僅向person16實例自己的friends數組push數據
console.log(person16.friends) // [ 'Shelby', 'Court', 'Van' ]
console.log(person17.friends) // [ 'Shelby', 'Court' ]
console.log(person16.friends === person17.friends) // false,person16實例的friends數組push了數據
console.log(person16.sayName === person17.sayName) // true,共享方法sayName
動態原型模式
- 將全部信息(屬性、方法)都封裝在構造函數中,經過檢查某個方法是否有效,來決定是否須要初始化原型
function PersonDynamic(name, age, job) {
// 屬性
this.name = name
this.age = age
this.job = job
// 方法:①只有方法不存在時才添加到原型;②只在初次調用構造函數時執行;③會當即在實例中體現
if (typeof this.sayName !== 'function') {
PersonDynamic.prototype.sayName = function () {
console.log(this.name)
}
}
}
var person18 = new PersonDynamic('Nicholas', 29, 'Software Engineer')
person18.sayName() // 'Nicholas'
console.log(person18 instanceof PersonDynamic) // true,person18是PersonDynamic的實例
- 只有在方法不存在時,纔會將方法添加到原型
- 只在初次調用構造函數時纔會執行,此後原型已經完成初始化
- 對原型所作的修改,會當即在實例中體現
- 與原型的動態性同理,若是已經建立了實例後再用對象字面量重寫原型,會切斷實例與新原型之間的聯繫,致使修改無效
PersonDynamic.prototype = {
newName:
typeof this.newName !== 'function'
? function () {
console.log('prototype:', this.name)
}
: this.newName,
}
person18.newName() // error,person18指向最初的原型,沒有newName方法
var person19 = new PersonDynamic('Greg', 27, 'Doctor') // person19是重寫原型後建立的實例
person19.newName() // prototype: Greg
person19.sayName() // Greg
console.log(person18 instanceof PersonDynamic) // false,person18不是重寫原型後的PersonDynamic的實例,person18指向最初的原型
console.log(person19 instanceof PersonDynamic) // true,person19是重寫原型後的PersonDynamic的實例
寄生構造函數模式
- 構造函數僅封裝建立對象的代碼,在構造函數內部用原生引用類型建立新對象,通過操做後再返回這個新對象
- 構造函數內部跟工廠模式如出一轍,調用時用 new 操做符,相似於工廠模式與構造函數模式的結合體
function PersonParasitic(name, age, job) {
var o = new Object() // 用原生引用類型建立對象
o.name = name // 添加值
o.age = age // 添加值
o.job = job // 添加值
// 添加方法
o.sayName = function () {
console.log(this.name)
}
return o
}
var person20 = new PersonParasitic('Nicholas', 29, 'Software Engineer')
person20.sayName() // 'Nicholas'
- 寄生構造函數模式建立的實例,與構造函數或構造函數的原型沒有關聯,不能依賴 instanceof 肯定對象類型
- 若是能夠用其餘模式,不建議該模式建立自定義對象
function SpecialArray() {
var values = new Array() // 用原生引用類型建立數組
values.push.apply(values, arguments) // 添加值
// 添加方法
values.toPipedString = function () {
return this.join('|')
}
return values // 返回通過操做後的數組
}
var colors = new SpecialArray('red', 'blue', 'green')
console.log(colors.toPipedString()) // red|blue|green
console.log(SpecialArray.prototype) // SpecialArray{},構造函數的原型對象
console.log(colors.__proto__) // [],構造函數內部經過new Array()從新初始化,其原型對象是原生對象Array
console.log(SpecialArray.prototype === colors.__proto__) // false,兩者無關聯
console.log(colors instanceof SpecialArray) // false,兩者無關聯
穩妥構造函數模式
- 與計生構造函數相似相似,在構造函數中用原生引用類型建立新對象,但沒有公共屬性、其方法也不引用 this、不使用 new 調用構造函數
- 除了構造函數內部的方法,沒有其餘辦法可訪問到構造函數內部的原始數據(即使有其餘代碼給對象添加方法或屬性),該模式適合在安全環境下使用
- 同寄生構造函數模式,該模式下建立的實例,與構造函數或構造函數的原型沒有關聯,不能依賴 instanceof 肯定對象類型
function PersonSafe(name, age, job) {
var o = new Object() // 用原生引用類型建立對象
o.sayName = function () {
console.log(name)
}
return o
}
var person21 = new PersonParasitic('Nicholas', 29, 'Software Engineer')
person21.sayName() // 'Nicholas'
總結 & 問點
建立對象 |
過程 |
缺點 |
Object 構造函數 |
1.建立 Objecty 實例 2.添加屬性和方法 |
同一接口建立多個對象,大量重複代碼 |
對象字面量 |
直接建立包含屬性和方法的對象 |
同一接口建立多個對象,大量重複代碼 |
工廠模式 |
1.用函數封裝建立 Object 實例的過程(添加屬性和方法、返回該實例對象) 2.調用該函數 |
沒有解決對象識別問題,即怎樣知道一個對象的類型 |
構造函數模式 |
1.構造函數封裝(不顯示的建立對象、屬性和方法賦給 this 對象、無 return) 2.new 調用構造函數 |
每一個實例從新建立方法,機制相同的 Function 對象被屢次實例化 |
原型模式 |
1.構造函數封裝(空的,無屬性和方法) 2.原型對象上添加屬性和方法 3.new 調用構造函數 |
對實例的引用類型屬性修改而非從新定義時,會對原型的引用類型屬性形成影響 |
組合使用構造函數模式和原型模式 |
1.構造函數封裝實例屬性 2.原型模式定義方法和共享屬性 3.new 調用構造函數 |
無明顯缺點,目前最普遍認同度最高,是默認模式 |
動態原型模式 |
1.構造函數封裝全部信息(屬性、檢查某個方法是否有效來決定是否初始化原型) 2.new 調用構造函數 |
無明顯缺點,不要使用對象字面量重寫原型 |
寄生構造函數模式 |
1.構造函數封裝(原生引用類型建立新對象、添加值或方法、返回新對象) 2.new 調用構造函數 |
實例與構造函數或構造函數的原型沒有關聯,不能依賴 instanceof 肯定對象類型 |
穩妥構造函數模式 |
1.同寄生構造函數模式的構造函數封裝(但方法不引用 this) 2.做爲普通函數調用構造函數 |
實例與構造函數或構造函數的原型沒有關聯,不能依賴 instanceof 肯定對象類型 |
對象 |
屬性 |
指向 |
用法 |
任何函數 |
prototype |
原型對象 |
Person.prototype → 構造函數的原型對象 |
實例、原型 |
constructor |
構造函數 |
person1.constructor === Person.prototype.constructor === Person |
實例 |
[[Prototype]] |
原型對象 |
person1.__proto__ === Person.prototype (沒有標準方式訪問[[Prototype]] ,但可用 __proto__ ) |
操做符 |
含義 |
用法 |
new |
建立構造函數的實例(四個步驟) |
var person = new Person() |
in |
可否經過對象訪問到屬性(不管屬性在實例仍是原型中) |
console.log('name' in person) |
for-in |
返回全部能經過對象訪問到的、可枚舉的屬性(不管屬性在實例仍是原型中) |
for(var attr in person){console.log(attr)} |
delete |
刪除實例屬性 |
delete person.name |
對象 |
方法 |
含義 |
參數 |
返回值 |
用法 |
Object 對象 |
getPrototypeOf() |
獲取實例對象的原型 |
實例 |
原型對象 |
Object.getPrototypeOf('person') === Person.prototype |
任何對象 |
hasOwnProperty() |
對象自身(不包括原型)是否含有該屬性 |
屬性 |
true/false |
console.log(Person.hasOwnProperty('name')) |
Object 對象 |
keys() |
獲取對象上全部可枚舉的屬性(僅對象自身) |
對象 |
屬性的字符串數組 |
Object.keys(person) |
Object 對象 |
getOwnPropertyNames() |
獲取對象上全部屬性(僅對象自身,不管是否可枚舉) |
對象 |
屬性的字符串數組 |
Object.getOwnPropertyNames(person) ,原型對象會包含 constructor 屬性 |
- 建立單個對象有哪些方法?這些方法有什
麼缺點?
- 工廠模式作出了怎樣的優化?該模式有什麼缺點?
- 相比工廠模式,構造函數模式有哪些區別?如何建立其實例?
- 構造函數在 new 的過程當中都發生了什麼?
- 構造函數建立出的對象,其 construtor 屬性指向哪裏?這樣的對象是哪些構造函數的實例?
- 相比工廠模式,構造函數有什麼優點?
- 構造函數與普通函數有什麼相同點和區別?
- 自定義對象的方法時,構造函數模式有什麼缺點?
- 用全局函數代替構造函數內部的對象屬性的方法,仍有什麼缺點?
- prototype 屬性的含義和用法?使用原型對象的好處是什麼?
- 原型對象的 constructor 屬性的含義和用法?
- 用什麼方法檢測實例是否含有指向原型對象的指針?
- 構造函數、實例的哪些屬性或方法可得到原型對象?如何經過實例得到構造函數?
- 代碼讀取對象屬性時,經歷了怎樣的搜索過程?
- 是否能夠經過實例訪問和修改原型中的屬性值?
- 在實例中添加與原型的同名屬性會怎樣?再刪除這個實例中的屬性呢?
- 用什麼方法檢測屬性存在與實例 or 原型?
- 用什麼方法獲取原型屬性的描述符?
- 單獨使用 in 的用法是什麼?其和 hasOwnProperty()方法的區別是什麼?
- for-in 的用法是什麼?其返回哪些屬性屏蔽哪些屬性?
- Object.keys()的和 Object.getOwnPropertyNames()用法分別是什麼?
- Object.getOwnPropertyNames()、Object.leys()、for-in 的區別是什麼?
- 用一個對象字面量的新對象重寫整個原型對象時,原型對象的 constructor 指向發生了怎樣的改變?
- 寫一段代碼,用對象字面量重寫構造函數的原型對象,且讓原型對象的 constructor 仍指向原構造函數,並保留 construtor 的[[Enumerable]]特性爲 false
- 建立實例後再修改原型的屬性,實例會受到影響麼?爲何?
- 重寫整個原型對象後,構造函數的 prototype 指向哪裏?實例的[[Prototype]]屬性指向哪裏?爲何?
- 原生引用類型的方法是如何建立的?爲何不推薦修改原生引用類型的原型?
- 原型模式的「共享」本性,在修改包含引用類型的屬性時,有怎樣的問題?
- ECMAScript 使用最普遍、認同度最高的建立自定義類型的方法是什麼?其原理和優點是什麼?
- 動態原型模式是如何初始化原型的?其對原型的修改有哪些注意點?
- 寄生構造函數模式的原理是什麼?爲何其建立的實例,與其構造函數或構造函數的原型沒有關聯?
- 穩妥構造函數模式的原理是什麼?爲何該模式比較「穩妥」?