js基礎(二):構造函數與原型(看完必懂)

寫在最前:構造函數和原型模式的使用場景很普遍,但由於對概念的混淆不清致使沒法熟練掌握。切圖帶你從代碼和流程圖一步步攻克,純乾貨,建議收藏詳看,原型模式理解圖很是重要,務必多看幾遍!

前往查看demo源碼
js基礎(一):判斷類型html

構造函數

構造函數與普通函數區別

  • 構造函數的首字母必須大寫,用來區分於普通函數(駝峯命名),此爲約定俗成
  • 構造函數內部使用的this對象,來指向即將要生成的實例對象,而普通函數中的this指向調用函數的對象(沒有對象時默認爲window)
  • 構造函數默認return this,但也能夠用return語句,返回值會根據return值的類型而有所不一樣。普通函數可以使用return返回值
  • 構造函數使用New來生成實例對象進行調用,普通函數直接調用
// 構造函數
function Person(name, age) {
  this.name = name
  this.age = age
  this.introduction = function() {
    console.log(`my name is ${this.name}, I'm ${this.age} years old`)
  }
  //return this //構造函數默認有這句
}
var p = new Person('qietuniu', 18) // this=Person
p.introduction()

// 普通函數
function person(name, age) {
  this.name = name
  this.age = age
  this.introduction = function() {
    console.log(`my name is ${this.name}, I'm ${this.age} years old`)
  }
  return `直接返回:個人名字 ${this.name}, 我 ${this.age} 歲`
}
console.log(person('qietuniu', 18)) //this=window
window.introduction()

構造函數內的上下文this指向即將要生成的實例對象Person,普通函數內使用this,指向window時容易形成全局污染。該構造函數將陪着咱們讀完這篇文章,以後的示例將在這基礎上演示!vue

隱藏的構造函數

  • var a={}是var a=new Object()的語法糖
  • var a=[]是var a=new Array()的語法糖
  • function Person(){}是var Person=new Function()的語法糖
語法糖:更簡單表達一個操做的語法,可以增長程序的可讀性,在性能上不會帶來損失的同時提升開發編碼的效率,從而減小程序代碼出錯的機會!

instanceof

可以使用instanceof判斷一個函數是不是一個變量的構造函數.jquery

解析:instanceof的判斷邏輯是實例p的__proto__一層一層往上,可否對應到Person.prototype,一樣也能到Object.prototype.

查看instanceof具體使用>>git

new一個對象的過程

  1. 建立一個新對象
  2. this指向這個新對象
  3. 執行代碼,即對this賦值
  4. 返回this
解析(以Person函數爲例):
1.建立一個新對象p
2.將構造函數Person()中的this指向新建立的對象p
3.p的_proto_(隱式原型)屬性指向Person函數的prototype(顯示原型),建立構造函數與原型以及對象的關係
4.調用對象,執行Person內屬性或方法

原型模式

原型模式理解圖

在這裏插入圖片描述

  • 構造函數Fn的prototype(顯式原型)是原型對象
  • 構造函數可經過new實例化一個實例
  • 原型對象的constructor是構造函數Fngithub

    Person.prototype.constructor = Person
  • 實例的構造函數屬性(constructor)指向構造函數segmentfault

    p的__proto__.constructor = Person
  • 實例的__proto__(隱式原型)是原型對象數組

    p的__proto__ =Person.prototype
  • Fn.prototype是對象,它的__proto__是object的prototype瀏覽器

    Person.prototype的__proto__ = Object.prototype
  • Object的prototype的__proto__爲nullecharts

    Object.prototype的__proto__ = null
    Person.prototype的__proto__的__proto__ = null
熟記該圖,萬變不離其宗

原型的五大原則(學習原型鏈的基礎)

1. 全部的引用類型(數組、對象、函數),都具備對象特性,便可自由擴展屬性(null除外)函數

var obj = {} 
 obj.a = 100 //自由擴展屬性
 var arr = []
 arr.a = 100
 function fn() {}
 fn.a = 100

建立對象的三種方法

// 字面量
var o1 = {
  name: 'o1'
}
var o2 = new Object({
  name: 'o2'
})

// 構造函數
var M = function() {
  this.name = 'o3'
}
var o3 = new M()

// Object.create
var O = {
  name: 'o4'
}
var o4 = Object.create(O)
console.log(o1)
console.log(o2)
console.log(o3)
console.log(o4)

2. 全部的引用類型(數組、對象、函數),都有一個__proto__屬性(隱式原型),屬性是一個普通的對象

//隱式原型
 console.log(obj.__proto__)
 console.log(arr.__proto__)
 console.log(fn.__proto__)

3. 全部的函數,都有一個prototype屬性(顯式原型),屬性也是一個普通的對象

//顯式原型
console.log(fn.prototype)

4. 全部的引用類型(數組、對象、函數),_proto_屬性值指向它的構造函數的「prototype」的值

//_proto_屬性值指向它的構造函數的「prototype」的值
console.log(`arr.__proto__ === Array.prototype:${arr.__proto__ === Array.prototype}`)
console.log(`obj.__proto__ === Object.prototype:${obj.__proto__ === Object.prototype}`)
console.log(`fn.__proto__ === Function.prototype:${fn.__proto__ === Function.prototype}`)

5. 當試圖獲得一個對象的某個屬性時,若是這個對象自己沒有這個屬性,那麼會去它的__proto__(即它的構造函數的prototype)中尋找

Person.prototype.sayName = function() {
  console.log(`個人名字:${this.name}`)
}
p.introduction()
p.sayName()
執行sayName時的時候,對象p自己沒有該方法,會去它的__proto__即它的構造函數的prototype中尋找(p.__proto__或者Person.prototype),因而找到sayName.

原型對象

什麼是原型對象

Person這個構造函數的顯式原型是一個對象,簡稱原型對象。Person.prototype就是原型對象。
每一個構造函數都有一個原型對象,原型對象都包含一個指向構造函數的指針,而實例都包含一個指向原型對象的內部指針,即原型對象(Person.prototype)是 構造函數(Person)的一個實例。

Person.prototype = p.__proto__

原型對象的優勢

能夠 讓全部對象實例共享它所包含的屬性和方法。換句話說,沒必要在構造函數中定義對象實例的信息,而是 能夠將這些信息直接添加到原型對象中,好比下面的sayName方法。

Person.prototype.sayName = function() {
  console.log(`個人名字:${this.name}`)
}

如何查找對象自身的屬性

var item
  for (item in p) {
      // 高級瀏覽器已經在for in中屏蔽了來自原型的屬性
      // 如下的判斷可保證程序的健壯性,hasOwnProperty方法會返回一個布爾值,指示對象自身屬性中是否具備指定的屬性
      if (p.hasOwnProperty(item)) {
          // 輸出name和printName,沒有alerName
          console.log(item)
      }
  }

原型鏈

ECMAScript 中描述了原型鏈的概念,並將原型鏈做爲實現繼承的主要方法。其基本思想是利用原型讓一個引用類型繼承另外一個引用類型的屬性和方法。

p.toString()是如何調用的

在這裏插入圖片描述

執行toString方法時,p自己沒有該方法,p.__proto__也沒有,繼續往上p.__proto__.__proto__即Person.prototype.__proto__,Person.prototype就是普通對象,Person.prototype.__proto__ = Object.prototype,Object中存在toString方法。

原型鏈圖

在這裏插入圖片描述

每一個構造函數都有一個原型對象,原型對象都包含一個指向構造函數的指針,而實例都包含一個指向原型。對象的內部指針這麼一層一層的查找就是原型鏈查找,如此層層遞進,就構成了實 例與原型的鏈條,這種鏈式結構叫作「原型鏈「。

使用場景

jquery中原型的使用

jQuery.fn.init.prototype = jQuery.fn,將原型方法爲何放在jQuery.fn中,是由於要擴展插件以下面的printQT 方法, 只有$會暴露在window全局變量(太多會形成污染),將插件擴展統一到jQuery.fn.xxx這一個接口方便使用。

var jQuery = function() {
  return new jQuery.fn.init();
}
jQuery.fn = jQuery.prototype = {
   constructor: jQuery,
   init: function() {
     this.jquery = "1.9.1";
     return this;
   }
 }
 jQuery.fn.init.prototype = jQuery.fn;
 jQuery.fn.printQT = function() {
   console.log("切圖")
   return this;
 }
 window.jQuery = window.$ = jQuery;
 console.log(jQuery().printQT())

其餘

除了jquery中的運用,在vue中使用諸如echarts的插件時,咱們會使用Vue.prototype.$echarts = echarts,將echarts引入到全局使用,一樣自定義方法變量也能夠如此使用。

尊重原創,如需轉載請註明出處!
相關文章
相關標籤/搜索