每一個物件都是基於引用型別建立的, 也可以是開發人員自己建立的
建立物件
字面量模式
- 缺點:
- 會產生大量重複程式碼, 使用變體
工廠模式
- 會產生大量重複程式碼, 使用變體
let Person = {
name: 'agan',
age: 30,
job: 'it',
sayName: function() {
console.log(this.name)
}
}
複製程式碼
工廠模式
這種模式抽象了建立具體函式的過程, 用函式來封裝以特定介面建立物件的細節
function createPerson(name, age, job) {
let o = new Object()
o.name = name
o.age = age
o.job = job
o.sayName = function() {
console.log(this.name)
}
return o
}
let p1 = createPerson('agan1', 30, 'it')
let p2 = createPerson('agan2', 40, 'cs')
複製程式碼
函式 createPerson
接受三個引數來構建一個物件, 該函式可以多次呼叫. 工廠模式雖然解決了建立多個相似物件的問題, 卻沒有解決物件識別的問題(即怎樣知道一個物件的型別)
建構函式模式
建構函式可以用來建立特定型別的物件, 我們可以建立自定義的建構函式, 從而可以定義自定義物件型別的屬性和方法
- 任何函式都可以當成建構函式
- 只要把一個函式通過new的方式來進行呼叫, 我們就把這一次函式的呼叫方式稱之為: 建構函式的呼叫
function Person(name, age, job) {
this.name = name
this.age = age
this.job = job
this.sayName = function() {
console.log(this.name)
}
}
let p1 = new Person('agan1', 30, 'it')
let p2 = new Person('agan2', 40, 'cs')
複製程式碼
Person
與 createPerson
不同:
- 沒有顯式建立物件
- 將屬性與方法給了
this
- 沒有
return
語句
p1
和 p2
儲存著 Person
的兩個不同例項, 但都有一個 constructor
(建構函式) 屬性, 指向 Person
console(p1.constructor == Person); //true
console(p2.constructor == Person); //true
複製程式碼
constructor
是用來表示物件型別的, instanceof
檢測物件型別更靠譜
alert(person1 instanceof Object); //true
alert(person1 instanceof Person); //true
alert(person2 instanceof Object); //true
alert(person2 instanceof Person); //true
複製程式碼
建構函式的執行過程
var p1=new Person();
- 1、建立一個物件 (我們把這個物件稱之為Person建構函式的例項)-
_p1
- 2、建立一個內部物件,
this
,將this指向該例項(_p1) - 3、執行函式內部的程式碼,其中,操作this的部分就是操作了該例項(_p1)
- 4、返回值:
-
a、如果函式沒有返回值(沒有return語句),那麼就會返回建構函式的例項(p1)
-
b、如果函式返回了一個基本資料型別的值,那麼本次建構函式的返回值是該例項(_p1)
function fn(){} var f1 = new fn(); //f1就是fn的例項 function fn2(){ return "abc"; } var f2 = new fn2(); //f2是fn2建構函式的例項 複製程式碼
-
c、如果函式返回了一個複雜資料型別的值, 那麼本次函式的返回值就是該值
function fn3(){ return [1,3,5]; //陣列是一個物件型別的值, //所以陣列是一個複雜資料型別的值 //-->本次建構函式的真正返回值就是該陣列 //-->不再是fn3建構函式的例項 } var f3 = new fn3(); //f3還是fn3的例項嗎?錯 //f3值為[1,3,5] 複製程式碼
-
將建構函式當函式
- 任何函式都可以是建構函式, 必須使用
new
呼叫 - 任何函式只要不通過
new
呼叫就是普通函式
// 當作建構函式
let person = new Person("agan", 29, "se");
person.sayName() // 'agan'
// 作為普通函式呼叫
Person("agan1", 27, "IT"); // 新增到 window
window.sayName(); // "agan1"
// 在另一個物件的作用域中呼叫
var o = new Object();
Person.call(o, "agan3", 25, "Nurse")
o.sayName(); // "agan3"
複製程式碼
建構函式問題
就是每個方法都要在每個例項上重新建立一遍,
sayName
指向不同的地址
person1.sayName === person2.sayName // true
複製程式碼
可以將 sayName 方法轉移到建構函式外部解決這個問題
function Person(name, age, job) {
this.name = name
this.age = age
this.job = job
this.sayName = sayName
}
function sayName() {
console.log(this.name)
}
let person1 = new Person("agan1", 29, "se");
let person2 = new Person("agan2", 27, "it");
person1.sayName === person2.sayName // true
複製程式碼
上面程式碼中將 sayName
函式轉移到了建構函式的外部, 在函式內部我們將 sayName
指向這個全域性函式, 這樣就解決了多個函式做一件事情的問題, 但又帶來了兩個新的問題:
- 全域性作用域的函式只能被某個物件呼叫
- 如果物件需要多個方法那麼就需要定義多個全域性函式
原型模式
我們建立的每一個函式都有一個
prototype
屬性, 這是一個指標, 指向了一個物件, 這個物件包含了特定型別物件的所有公共屬性和方法, 相當於一個父類
function Person() {
}
Person.prototype.name = 'agan'
Person.prototype.age = '30'
Person.prototype.job = 'it'
Person.prototype.sayName = function() {
console.log(this.name)
}
let p1 = new Person()
let p2 = new Person()
p1.sayName() // 'agan'
p2.sayName() // 'agan'
console.log(p1.sayName === p2.sayName) // true
複製程式碼
上述程式碼中 person1
和 person2
訪問的是同一組物件和方法
假如我們想在所有的例項中共享一個資料, 那麼我們可以使用原型模式, 可是一般例項都有自己的屬性
組合使用建構函式與原型模式
建構函式用於建立例項屬性, 原型模式用於定義方法和共享的屬性
function Person(name, age, job) {
this.name = name
this.age = age
this.job = job
this.friends = ['a', 'b']
}
Person.prototype = {
constructor: Person,
sayName: function() {
console.log(this.name)
}
}
var p1 = new Person('agan', 29, 'it')
var p2 = new Person('agan2', 30, 'cs')
p1.friends.push('c')
console.log(p1.friends) // 'a' 'b' 'c'
console.log(p2.friends) // 'a' 'b'
console.log(p1.friends === p2.friends) // false
console.log(p1.sayName === p2.sayName) // true
複製程式碼
在上面的程式碼中, 例項屬性是定義在建構函式中的, 例項共享的屬性和方法是在原型中定義的
動態原型模式
動態原型模式將所有資訊都封裝在了建構函式中, 在建構函式中初始化原型
function Person(name, age, job) {
this.name = name
this.age = age
this.job = job
// 方法
if (typeof this.sayName !== 'function') {
Person.prototype.sayName = function() {
console.log(this.name)
}
}
}
let f = new Person('agan', 30, 'it')
f.sayName()
複製程式碼
上述程式碼中, sayName方法只有在不存在的情況下, 才會新增到原型中, 這段程式碼只會在初次呼叫建構函式時才會執行
寄生建構函式模式
基本思想: 建立一個函式, 該函式用來封裝建立物件的程式碼, 然後再返回建立的物件 跟工廠模式類似
function Person(name, age, job) {
let o = new Object()
o.name = name
o.age = age
o.job = job
o.sayName = function() {
console.log(this.name)
}
return o
}
let f = new Person('agan', 20, 'it')
f.sayName() // 'agan'
複製程式碼
- 說明:
- 返回的物件與建構函式或者與建構函式的原型屬 性之間沒有關係
- 建構函式返回的物件與在建構函式外部建立的物件沒有什麼不同
- 不能依賴 instanceof 操作符來確定物件型別
穩妥建構函式
穩妥物件: 指的是沒有公共屬性, 其方法也不引用
this
物件
- 穩妥建構函式與寄生建構函式不同點:
- 函式內部新建立物件的例項方法不引用
this
- 函式外部不使用
new
操作符呼叫建構函式 - 函式內部中變數不能掛在到要返回的變數上
- 函式內部新建立物件的例項方法不引用
function Person(name, age, job) {
let o = new Object()
// 定義私有變數和函式
let name = name
let age = age
let job = job
// 新增方法
o.sayName = function() {
console.log(name)
}
return o
}
複製程式碼
這種模式中建立的物件, 除了使用 sayName
方法外沒有其他方法訪問name的值
let f = Person('agan', 30, 'it')
f.sayName() // agan
複製程式碼