1. 程式人生 > >JS高階(原型/函式)

JS高階(原型/函式)

原型

Javascript 規定,每一個建構函式都有一個 prototype 屬性,指向另一個物件。這個物件的所有屬性和方法,都會被建構函式的例項繼承。這也就意味著,我們可以把所有物件例項需要共享的屬性和方法直接定義在 prototype 物件上。

function Person (name, age) {
  this.name = name
  this.age = age
}

console.log(Person.prototype)

Person.prototype.type = 'human'

Person.prototype.sayName = function () {
  console.log(this.name)
}

var p1 = new Person(...)
var p2 = new Person(...)

console.log(p1.sayName === p2.sayName) // => true

這時所有例項的 type 屬性和 sayName() 方法, 都是同一個記憶體地址,指向 prototype 物件,因此就提高了執行效率。

 

建構函式、例項、原型三者之間的關係

任何函式都具有一個 prototype 屬性,該屬性是一個物件。

建構函式的 prototype 物件預設都有一個 constructor 屬性,指向 prototype 物件所在函式。

通過建構函式得到的例項物件內部會包含一個指向建構函式的 prototype 物件的指標 __proto__

 

總結:

  • 任何函式都具有一個 prototype 屬性,該屬性是一個物件

  • 建構函式的 prototype 物件預設都有一個 constructor 屬性,指向 prototype 物件所在函式

  • 通過建構函式得到的例項物件內部會包含一個指向建構函式的 prototype 物件的指標 __proto__

  • 所有例項都直接或間接繼承了原型物件的成員

 

原型物件的問題

  • 共享陣列

  • 共享物件

原型物件使用建議

  • 私有成員(一般就是非函式成員)放到建構函式中

  • 共享成員(一般就是函式)放到原型物件中

  • 如果重置了 prototype 記得修正 constructor 的指向

 

繼承

建構函式的屬性繼承:借用建構函式

function Person (name, age) {
  this.type = 'human'
  this.name = name
  this.age = age
}

function Student (name, age) {
  // 借用建構函式繼承屬性成員
  Person.call(this, name, age)
}

var s1 = Student('張三', 18)
console.log(s1.type, s1.name, s1.age) // => human 張三 18

原型繼承

function Person (name, age) {
  this.type = 'human'
  this.name = name
  this.age = age
}

Person.prototype.sayName = function () {
  console.log('hello ' + this.name)
}

function Student (name, age) {
  Person.call(this, name, age)
}

// 利用原型的特性實現繼承
Student.prototype = new Person()

var s1 = Student('張三', 18)

console.log(s1.type) // => human

s1.sayName() // => hello 張三

 

函式宣告與函式表示式的區別

  • 函式宣告必須有名字

  • 函式宣告會函式提升,在預解析階段就已建立,宣告前後都可以呼叫

  • 函式表示式類似於變數賦值

  • 函式表示式可以沒有名字,例如匿名函式

  • 函式表示式沒有變數提升,在執行階段建立,必須在表示式執行之後才可以呼叫

 

函式內 this 指向的不同場景

普通函式呼叫         window                        嚴格模式下是 undefined

建構函式呼叫        例項物件                      原型方法中 this 也是例項物件

物件方法呼叫        該方法所屬物件           緊挨著的物件

事件繫結方法       繫結事件物件

定時器函式          window

 

函式也是物件

所有函式都是 Function 的例項

call:

call() 方法呼叫一個函式, 其具有一個指定的 this 值和分別地提供的引數(引數的列表)。

注意:該方法的作用和 apply() 方法類似,只有一個區別,就是 call() 方法接受的是若干個引數的列表,而 apply() 方法接受的是一個包含多個引數的陣列。

 

apply

apply() 方法呼叫一個函式, 其具有一個指定的 this 值,以及作為一個數組(或類似陣列的物件)提供的引數。

<p class="danger"> 注意:該方法的作用和 call() 方法類似,只有一個區別,就是 call() 方法接受的是若干個引數的列表,而 apply() 方法接受的是一個包含多個引數的陣列。

 

bind

bind() 函式會建立一個新函式(稱為繫結函式),新函式與被調函式(繫結函式的目標函式)具有相同的函式體(在 ECMAScript 5 規範中內建的call屬性)。當目標函式被呼叫時 this 值繫結到 bind() 的第一個引數,該引數不能被重寫。繫結函式被呼叫時,bind() 也接受預設的引數提供給原函式。一個繫結函式也能使用new操作符建立物件:這種行為就像把原函式當成構造器。提供的 this 值被忽略,同時呼叫時的引數被提供給模擬函式。

 

總結

  •  call 和 apply 特性一樣

    • 都是用來呼叫函式,而且是立即呼叫

    • 但是可以在呼叫函式的同時,通過第一個引數指定函式內部 this 的指向

    • call 呼叫的時候,引數必須以引數列表的形式進行傳遞,也就是以逗號分隔的方式依次傳遞即可

    • apply 呼叫的時候,引數必須是一個數組,然後在執行的時候,會將陣列內部的元素一個一個拿出來,與形參一一對應進行傳遞

    • 如果第一個引數指定了 null 或者 undefined 則內部 this 指向 window

  • bind

    • 可以用來指定內部 this 的指向,然後生成一個改變了 this 指向的新的函式

    • 它和 call、apply 最大的區別是:bind 不會呼叫

    • bind 支援傳遞引數,它的傳參方式比較特殊,一共有兩個位置可以傳遞

        1. 在 bind 的同時,以引數列表的形式進行傳遞

        1. 在呼叫的時候,以引數列表的形式進行傳遞

      • 那到底以誰 bind 的時候傳遞的引數為準呢還是以呼叫的時候傳遞的引數為準

      • 兩者合併:bind 的時候傳遞的引數和呼叫的時候傳遞的引數會合併到一起,傳遞到函式內部