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

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

建立對象

  • 建立單個對象: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 使用最普遍、認同度最高的建立自定義類型的方法是什麼?其原理和優點是什麼?
  • 動態原型模式是如何初始化原型的?其對原型的修改有哪些注意點?
  • 寄生構造函數模式的原理是什麼?爲何其建立的實例,與其構造函數或構造函數的原型沒有關聯?
  • 穩妥構造函數模式的原理是什麼?爲何該模式比較「穩妥」?
相關文章
相關標籤/搜索