博客 / 詳情

返回

《javascript高級程序設計》學習筆記 | 8.2.創建對象

關注前端小謳,閲讀更多原創技術文章

創建對象

  • 創建單個對象: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) ]

屬性枚舉順序

  • for-in循環和Object.keys()屬性枚舉順序不確定的,取決於瀏覽器的 JS 引擎
  • Object.getOwnPropertyNames()Object.getOwnPropertySymbols()Object.assign()屬性枚舉順序確定的:

    • 升序枚舉數值鍵
    • 插入順序枚舉字符串和符號鍵
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]]屬性指向哪裏?為什麼?
  • 原生引用類型的方法是如何創建的?為什麼不推薦修改原生引用類型的原型?
  • 原型模式的“共享”本性,在修改包含引用類型的屬性時,會產生怎樣的問題?
user avatar codepencil 頭像 jianqiangdepaobuxie 頭像 ailim 頭像 niumingxin 頭像 kevin_5d8582b6a85cd 頭像 user_p5fejtxs 頭像 yangkaiqiang 頭像
7 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.