關注前端小謳,閲讀更多原創技術文章
創建對象
- 創建單個對象:Object 構造函數 和 對象字面量
- 缺點:使用一個接口創建很多對象,產生大量重複代碼
相關代碼 →
工廠模式
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 等原生構造函數,還可以創建自定義的構造函數
-
構造函數模式 vs 工廠模式
- 不顯式的創建對象
- 直接將屬性和方法賦給 this 對象
- 沒有 return
function Person(name, age, job) {
this.name = name
this.age = age
this.job = job
this.sayName = function () {
console.log(this.name)
}
}
var person1 = new Person('Nicholas', 29, 'Software Engineer')
var person2 = new Person('Greg', 27, 'Doctor')
- 構造函數用大寫字母開頭,創建實例時用
new 操作符
-
構造函數 new 一個對象後:
- 創建了一個新對象(實例)
- 新對象內部的
[[Prototype]]特性被賦值為構造函數的prototype屬性(共同指向原型)
- 將構造函數的作用域(即 this)賦給新對象
- 執行構造函數中的代碼(即:為這個對象添加新屬性)
- 返回新對象或非空對象
- 創建的對象(實例)既是 Object 的實例,又是構造函數的實例,其
constructor 屬性指向構造函數
- 可以確保自定義構造函數的實例被標識為特定的類型,是構造函數模式勝過工廠模式的地方
console.log(person1.constructor === Person) // true,constructor 屬性指向構造函數
console.log(person2.constructor === Person) // true,constructor 屬性指向構造函數
console.log(person1 instanceof Object) // true,person1是Object的實例
console.log(person1 instanceof Person) // true,person1是Person的實例
console.log(person2 instanceof Object) // true,person2是Object的實例
console.log(person2 instanceof Person) // true,person2是Person的實例
- 構造函數也可以使用函數表達式表示,實例化不傳參數時,構造函數後面的括號可加可不加
var PersonExpression = function () {
// 構造函數的函數表達式
this.name = 'Jake'
this.sayName = function () {
console.log(this.name)
}
}
var personNoBrackets = new PersonExpression() // 實例化不傳參數,可不加括號
構造函數也是函數
- 構造函數與普通函數唯一的區別是調用方式不同:使用
new操作符調用的就是構造函數,不使用的是普通函數
- 默認情況下,調用函數時的
this 指向 Global 對象(瀏覽器中指向 window 對象)
var person3 = new Person('Nicholas', 29, 'Software Engineer') // 用構造函數創建對象
person3.sayName() // 'Nicholas'
Person('Greg', 27, 'Doctor') // 'Greg',不使用new操作符,直接調用
global.sayName() // 直接調用函數,this指向Global對象(瀏覽器中指向window對象)
var o = new Object() // 新對象o
var p = new Object() // 新對象p
Person.call(o, 'Kristen', 25, 'Nurse') // 將對象o指定為Person()內部的this值,call()分別傳入每個參數
Person.apply(p, ['Kristen', 25, 'Nurse']) // 將對象o指定為Person()內部的this值,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屬性,指回與之關聯的構造函數
console.log(PersonPrototype.prototype.constructor) // PersonPrototype構造函數,原型對象的constructor屬性指向與之關聯的構造函數
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構造函數
console.log(person5.__proto__ === person6.__proto__) // true,共享同一個原型對象
instanceof檢查實例的原型鏈中,是否包含指定構造函數的原型
console.log(person5 instanceof PersonPrototype) // true,person5是PersonPrototype的實例
console.log(person5 instanceof Object) // true,person5是Object的實例
console.log(PersonPrototype.prototype instanceof Object) // true,所有實例對象和原型對象都是Object的實例
- 原型對象的
isPrototypeOf()方法,檢測實例中是否有指向原型對象的指針
console.log(PersonPrototype.prototype.isPrototypeOf(person5)) // true,person5包含指向PersonPrototype的原型對象的指針
console.log(PersonPrototype.prototype.isPrototypeOf(person1)) // false,person1不包含指向PersonPrototype的原型對象的指針
Object.getPrototypeOf()方法(參數一般為實例),返回參數的[[Prototype]]的值(一般為原型對象)
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構造函數
Object.setPrototypeOf()方法,向實例(參數一)的[[Prototype]]寫入一個新值(參數二),從而重寫一個對象的原型繼承關係
var biped = {
numLegs: 2,
}
var person = {
name: 'Matt',
}
Object.setPrototypeOf(person, biped)
console.log(person.name) // 'Matt'
console.log(person.numLegs) // 2
console.log(person.__proto__) // { numLegs: 2 },person的[[Prototype]]指針指向biped
- 為避免
Object.setPrototypeOf()可能嚴重影響代碼性能,可使用Object.create()創建一個新對象,同時為其指定原型(參數)
var biped2 = {
numLegs: 3,
}
var person = Object.create(biped2)
console.log(person.numLegs) // 3
console.log(person.__proto__) // { numLegs: 3 },person的[[Prototype]]指針指向biped2
原型層級
-
代碼讀取對象屬性的搜索過程:
- 1.搜索對象實例本身 -> 有屬性 → 返回屬性值 -> 結束
- 2.對象實例本身無屬性 -> 搜索原型對象 → 有/無屬性 → 返回屬性值/undefined → 結束
- 可以通過實例訪問原型中屬性的值(如 constructor 屬性),但無法通過實例重寫原型中屬性的值
- 如果添加的實例屬性與原型的屬性同名,則實例屬性屏蔽原型中的屬性
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()方法,檢測屬性是否存在於實例中(存在返回 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操作符,判斷屬性存在於實例還是原型
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 循環:返回對象所有能夠訪問的、可枚舉的屬性(無論來自實例還是原型),屏蔽了不可枚舉([[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' ]
console.log(Object.getOwnPropertyNames(PersonIn)) // [ 'length', 'name', 'arguments', 'caller', 'prototype' ]
- ES6 新增
Object.getOwnPropertySymbols()方法,返回對象(自身)所有符號鍵屬性(無論是否可枚舉)的數組,參數為該對象
var k1 = Symbol('k1')
var k2 = Symbol('k2')
var o = {
[k1]: 'k1', // 符號作為屬性,需使用“計算屬性”語法,即[屬性名]
[k2]: 'k2',
}
console.log(Object.getOwnPropertySymbols(o)) // [ Symbol(k1), Symbol(k2) ]
屬性枚舉順序
var k1 = Symbol('k1')
var k2 = Symbol('k2')
var o = {
1: 1,
first: 'first',
[k2]: 'sym2',
third: 'third',
0: 0,
}
o[k1] = 'sym1'
o[3] = 3
o.second = 'second'
o[2] = 2
console.log(Object.getOwnPropertyNames(o)) // [ '0', '1', '2', '3', 'first', 'third', 'second' ]
console.log(Object.getOwnPropertySymbols(o)) // [ Symbol(k2), Symbol(k1) ]
對象迭代
- ES7 新增
Object.values()和Object.entries()方法,接收參數對象,分別返回對象值和對象鍵/值對數組
var o = {
foo: 'bar',
baz: 1,
qux: {},
}
console.log(Object.values(o)) // [ 'bar', 1, {} ],迭代值
console.log(Object.entries(o)) // [ [ 'foo', 'bar' ], [ 'baz', 1 ], [ 'qux', {} ] ],迭代鍵值對
var o = {
qux: {},
}
console.log(Object.values(o)) // [ {} ]
console.log(Object.entries(o)) // [ [ 'qux', {} ] ]
console.log(Object.values(o)[0] === o.qux) // true,淺複製,複製對象的引用
console.log(Object.entries(o)[0][1] === o.qux) // true,淺複製,複製對象的引用
var sym = Symbol()
var o = {
[sym]: 'foo', // 符號屬性
}
console.log(Object.values(o)) // [],符號屬性被忽略
console.log(Object.entries(o)) // [],符號屬性被忽略
其他原型語法
- 用包含所有屬性和方法的新對象字面量來重寫整個原型對象
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屬性屬於直接在對象上定義的屬性,會導致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
console.log(Object.keys(PersonLiteral2.prototype)) // [ 'constructor', 'name', 'age', 'job', 'sayName' ],因為constructor是“直接在對象上定義的屬性”,可被枚舉出來
- 不在對象字面量內設置
constructor屬性,而用Object.defineProperty()修改對象字面量中constructor屬性的特性,以兼容 JavaScript 引擎
Object.defineProperty(PersonLiteral2.prototype, 'constructor', {
enumerable: false,
value: PersonLiteral2,
})
console.log(Object.keys(PersonLiteral2.prototype)) // [ 'name', 'age', 'job', 'sayName' ],constructor的enumerable已被設置為false
原型的動態性
- 對原型對象所做的任何修改都立即從實例上反映出來,即使先創建實例後修改原型
function Person4() {}
var friend3 = new Person4() // 先創建實例
Person4.prototype.sayHi = function () {
// 後修改原型對象
console.log('Hi')
}
friend3.sayHi() // 'Hi',實例受影響,實例指向原型
- 重寫整個原型,會切斷構造函數與最初原型之間的聯繫,(重寫原型前創建的)實例的[[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] },friend3在重寫原型前創建,[[Prototype]]指向最初的原型對象
console.log(friend3.__proto__ === Person4.prototype) // false,重寫整個原型切斷了構造函數與最初原型之間的聯繫
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' ],從原型中獲得
總結 & 問點
| 創建對象 |
過程 |
缺點 |
| Object 構造函數 |
1.創建 Objecty 實例 2.添加屬性和方法 |
同一接口創建多個對象,大量重複代碼 |
| 對象字面量 |
直接創建包含屬性和方法的對象 |
同一接口創建多個對象,大量重複代碼 |
| 工廠模式 |
1.用函數封裝創建 Object 實例的過程(添加屬性和方法、返回該實例對象) 2.調用該函數 |
沒有解決對象識別問題,即怎樣知道一個對象的類型 |
| 構造函數模式 |
1.構造函數封裝(不顯示的創建對象、屬性和方法賦給 this 對象、無 return) 2.new 調用構造函數 |
每個實例重新創建方法,機制相同的 Function 對象被多次實例化 |
| 原型模式 |
1.構造函數封裝(空的,無屬性和方法) 2.原型對象上添加屬性和方法 3.new 調用構造函數 |
對實例來自原型的引用類型屬性修改而非重新定義時,會對原型造成影響 |
| 對象 |
屬性 |
默認指向 |
用法 |
| 任何函數 |
prototype |
原型對象 |
Person.prototype → 構造函數的原型對象 |
| 實例、原型 |
constructor |
構造函數 |
person1.constructor === Person.prototype.constructor === Person |
| 實例 |
[[Prototype]] |
原型對象 |
person1.__proto__ === Person.prototype(沒有標準方式訪問[[Prototype]],但可用 __proto__) |
| 操作符 |
含義 |
用法 |
| new |
創建構造函數的實例(四個步驟) |
var person = new Person() |
| delete |
刪除實例屬性 |
delete person.name |
| in |
能否通過對象訪問到屬性(無論屬性在實例還是原型中) |
console.log('name' in person) |
| for-in |
返回所有能通過對象訪問到的、可枚舉的屬性(無論屬性在實例還是原型中) |
for(var attr in person){console.log(attr)} |
| 方法 |
含義 |
參數 |
返回值 |
| isPrototypeOf() |
實例是否有指向原型對象的指針 |
實例 |
true/false |
| Object.getPrototypeOf() |
獲取實例[[Prototype]]的值 |
實例 |
原型對象 |
| Object.setPrototypeOf() |
向實例的[[Prototype]]寫入新值(指定原型) |
① 實例 ② 指定原型 |
|
| Object.create() |
創建一個新對象,同時為其指定原型 |
指定原型 |
|
| hasOwnProperty() |
屬性是否存在於實例中(非原型中) |
屬性 |
true/false |
| Object.keys() |
獲取對象(自身)所有可枚舉的屬性 |
對象 |
屬性的字符串數組 |
| Object.getOwnPropertyNames() |
獲取對象(自身)所有屬性(無論是否可枚舉) |
對象 |
屬性的字符串數組(原型對象包含 constructor 屬性) |
| Object.getOwnPropertySymbols() |
獲取對象(自身)所有符號鍵屬性(無論是否可枚舉) |
對象 |
屬性的符號鍵數組(原型對象包含 constructor 屬性) |
| 方法/操作符 |
枚舉順序 |
| for-in |
枚舉順序不確定,取決於瀏覽器的 JS 引擎 |
| Object.keys() |
枚舉順序不確定,取決於瀏覽器的 JS 引擎 |
| Object.getOwnPropertyNames() |
枚舉順序確定:先以升序枚舉數值鍵,後以插入順序枚舉字符串和符號鍵 |
| Object.getOwnPropertySymbols() |
枚舉順序確定:先以升序枚舉數值鍵,後以插入順序枚舉字符串和符號鍵 |
- 創建單個對象有哪些方法?這些方法有什麼缺點?
- 工廠模式做出了怎樣的優化?該模式有什麼缺點?
- 相比工廠模式,構造函數模式有哪些區別和優勢?其在 new 的過程中都發生了什麼?
- 構造函數創建出的對象,其 construtor 屬性指向哪裏?這樣的對象是哪些構造函數的實例?
- 相比普通函數,構造函數有什麼相同點和區別?
- 構造函數模式有什麼缺點?用全局函數代替構造函數內部對象的方法,仍有什麼缺點?
- 函數的 prototype 屬性是什麼?使用原型對象的好處是什麼?如何理解原型對象的 constructor 屬性?
- 構造函數、實例、原型對象之間,分別可以用什麼方式相互獲取?用什麼方法檢測實例是否含有指向原型對象的指針?
- Object.getPrototypeOf()、Object.setPrototypeOf()、Object.create()分別的含義和用法是什麼?
- 代碼讀取對象屬性時,經歷了怎樣的搜索過程?是否可以通過實例訪問和修改原型中的屬性值?
- 在實例中添加與原型的同名屬性會怎樣?再刪除這個實例中新增的屬性呢?
- 單獨使用 in 操作符的含義是什麼?其和 hasOwnProperty()方法的區別是什麼?
- 請寫一段代碼,判斷某個屬性存在於實例還是原型
- for-in 的用法是什麼?其返回哪些屬性屏蔽哪些屬性?
- Object.keys()、Object.getOwnPropertyNames()、Object.getOwnPropertySymbols()、Object.values()、Object.entries()的用法分別是什麼?
- for-in、Object.keys()、Object.getOwnPropertyNames()、Object.getOwnPropertySymbols()在屬性枚舉時的順序有什麼區別?
- 用一個對象字面量的新對象重寫整個原型對象時,原型對象的 constructor 指向發生了怎樣的改變?
- 寫一段代碼,用對象字面量重寫構造函數的原型對象,且原型對象的 constructor 仍指向原構造函數,並保留 construtor 屬性“不可被枚舉”的特性
- 創建實例後再修改原型的屬性,實例會受到影響麼?為什麼?
- 重寫整個原型對象後,構造函數的 prototype 指向哪裏?重寫前創建的實例的[[Prototype]]屬性指向哪裏?為什麼?
- 原生引用類型的方法是如何創建的?為什麼不推薦修改原生引用類型的原型?
- 原型模式的“共享”本性,在修改包含引用類型的屬性時,會產生怎樣的問題?