js面向對象淺談(一)

引言

對於面向對象,相信你們必定不陌生。最近看了一些關於es6面向對象的知識,正好經過這篇文章把關於面向對象的東西給串起來分享給你們。前端

什麼是對象

不少人會鄙視我,說你這篇文章是騙騙剛入行的小朋友的吧,什麼是對象我還能不知道?罵個人吃瓜羣衆先冷靜一下,你可能對對象一無所知。vue

{
    name: '李小花',
    sayname () {
        console.log(this.name)
    }
}

這是咱們最多見的對象,這個對象是經過對象字面量形式建立的。程序員

對象的含義是無序的集合,其屬性能夠包含基本值、對象或者函數。es6

屬性類型

js中有兩種內置的屬性,數據屬性和訪問器屬性,這兩個屬性是隻有內部才能訪問的屬性,因此這些屬性都放在了兩對方括號中,如[[enumerable]],你們在vue中常常面試

數據屬性

數據屬性包含一個數據值的位置。在這個位置能夠讀取和寫入值。數據屬性有 4 個描述其行爲的特性。
  • [[Configurable]]:表示可否經過 delete
    刪除屬性從而從新定義屬性,可否修改屬性的特性,或者可否把屬性修改成訪問器屬性。後端

  • [[Enumerable]]:表示可否經過 for-in 循環返回屬性。瀏覽器

  • [[Writable]]:表示可否修改屬性的值。閉包

  • [[Value]]:包含這個屬性的值。讀取屬性值的時候,從這個位置讀;寫入屬性值的時候,把新值保存在這個位置。這個特性的默認值爲
    undefined。函數

如今有一個對象經過字面量建立性能

var person =  {
         name: '張全蛋'
     }

[[Configurable]]、[[Enumerable]]、[[Writable]]屬性都會被設置爲true,[[Value]]被設置爲了‘張全蛋’。若是想修改這幾個屬性任意一個值,必須使用大名鼎鼎的Object.defineProperty()方法,爲啥說它大名鼎鼎,由於若是你接觸過vue,就知道他核型就是經過這個方法實現的。

var person = {};

Object.defineProperty(person, 'name', {
    writable: false,
    value: '張全蛋'
})
Object {name: "張全蛋"}

clipboard.png

如今的name屬性是隻讀的,若是是嚴格模式的話,

clipboard.png

這樣作還會報錯。一樣的也適用於其餘屬性,我這裏就不一一演示了。

注意⚠️,Object.defineProperty()方法只有現代瀏覽器才支持,IE8只是部分實現。

訪問器屬性

訪問器屬性不包含數據值,它們包含一對 getter 和 setter 函數(這兩個函數都不是必須的)。在讀取訪問器屬性時,會調用
getter 函數,這個函數負責返回有效的值;在寫入訪問器屬性時,會調用 setter
並傳入新值,這個函數負責決定如何處理數據。訪問器屬性有以下 4 個特性。

  • [[Configurable]]:表示可否經過 delete 刪除屬性從而從新定義屬性,可否修改屬性的特性,或者可否把屬性修改成數據屬性。

  • [[Enumerable]]:表示可否經過 for-in 循環返回屬性。

  • [[Get]]:在讀取屬性時調用的函數。默認值爲 undefined。

  • [[Set]]:在寫入屬性時調用的函數。默認值爲 undefined。

訪問只能經過bject.defineProperty()方法來定義。

var book = {
  _year: 2004,
  edition: 1
};

Object.defineProperty(book, 'year', {
  get: function() {
    return this._year;
  },
  set: function(newValue) {
    if (newValue > 2004) {
      this._year = newValue;
      this.edition += newValue - 2004;
    }
  }
});

book.year = 2005;
alert(book.edition);  // 2
alert(book.year); // 2005

訪問器屬性 year 則包含一個 getter 函數和一個 setter 函數。getter 函數返回 _year 的值,setter 函數經過計算來肯定正確的版本。所以,把 year 屬性修改成 2005 會致使 _year 變成 2005,而 edition 變爲 2。這是使用訪問器屬性的常見方式,即設置一個屬性的值會致使其餘屬性發生變化。

注意⚠️,訪問器屬性只有IE9以上才支持,這就是爲何VUE只能支持到IE9的緣由。

建立對象

js面向對象第一步是什麼?答:建立對象。建立對象有不少中方式,咱們最經常使用的是對象字面量來建立對象,var obj = {},你看我這不就建立了一個對象了嗎,我還幹嗎要繼續瞭解那些奇葩的方法呢?這麼想的人活該單身,多掌握些找對象只有好處沒有壞處哈。正經的,高階上有這麼一句話,使用對象字面量建立單個對象,有個明顯的缺點,使用同一個接口建立不少對象,會產生大量重複的代碼。爲了解決這個問題,咱們須要瞭解下面?這些方式。

工廠模式

工廠模式很簡單,貼上一段代碼。

function createPerson (name, age, job) {
    var o = new Object();
    o.name = name;
    o.age = age;
    o.job = job;
    o.sayName = function () {
        alert(this.name)
    }
    return o;
}
var person1 = createPerson("鐵蛋", 20, '工頭')

var person2 = createPerson("李四", 30, '挖掘機駕駛員')

工廠模式的優勢沒必要多說,若是我想建立兩個對象,上面一樣有name、age、job屬性,這樣就省去了建立包含一樣屬性多個對象的麻煩,可是卻沒有解決對象識別的問題。
有人會問,對象識別是什麼鬼。咱們建立對象是爲了模仿類的概念,這裏的person1,person2應該都屬於「人」一類,可是顯然咱們如今沒辦法將他們歸爲一類,因此這個時候逼格更高的方法出現了。

構造函數模式

function Person (name, age, job) {
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = function () {
        alert(this.name)
    }
}
var person1 = new Person("鐵蛋", 20, '工頭')
var person2 = new Person("李四", 30, '挖掘機駕駛員')

這裏有個顯然很突出的地方,就是這個Person的P是大寫的,其實大寫不是必須的,聽說這種習慣是不少後端程序員轉前端帶過來的。構造函數模式跟工廠模式不同的地方還在於,沒有用new Object顯式地建立對象,一樣沒有return語句。
那咱們在new完一個構造函數,實則產生一個實例,咱們new一個構造函數,會經歷如下神奇的四步。

建立對象 將this指向這個新對象 爲這個對象添加屬性 返回這個對象

person一、person2 是咱們經過 new Person這個構造函數獲得的,因此這兩個的構造函數都是Person,constructor(構造函數)屬性就都是Person,我之前一直都不能理解constructor是什麼東西,如今才理解原來constructor的中文翻譯就是構造函數?,也難怪我英文最熟的一句就是"hello kugou"了。咱們能夠經過使用instanceof操做符來檢測對象的類型。

let arr = new Array(2)
 arr instanceof Array  // true
 arr instanceof Object // true

構造函數優於工廠模式也是在於它能夠經過instanceof辨識出一類的對象。

接下來你們看一段通常沒品的面試官會考的問題

this.name = "張全蛋"
function Person (name, age, job) {
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = function () {
        alert(this.name)
    }
}
var person1 = new Person("鐵蛋", 20, '工頭')
var person2 = Person("李小花", 30, "廠花")

person1.sayName()  // 鐵蛋
person2.sayName() // 報錯

咱們首先要肯定一個概念,構造函數也是函數,若是不用new 的方式來調用它,它跟普通函數沒有半毛錢的區別,咱們知道在函數的做用域是window,因此this指向的是window,因此這段代碼person2對象this就是window,window沒有sayName屬性,因此會報錯。若是經過的是new方式調用的話,咱們上面也講了,爲將this賦值給這個對象,因此this就是person1這個實例。那麼構造函數是否是沒有缺點呢?顯然是不對的,由於我已經這麼問了。構造函數的缺點,每一個方法都要在實例上從新建立一遍,js中函數也是對象,定義函數就是實例化對象

function Person (name, age, job) {
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = new Function () {
        alert(this.name)
    }
}

每次new一個function就會多一次標識符解析,標識符(一般指命名)的解析是有代價的,實際上沒有那種計算機操做能夠不產生性能開銷。在執行環境的做用域鏈(扯到做用域鏈就必定會扯到閉包問題,之後有空再仔細聊聊閉包)中,一個標識符所在的位置越深,它的讀寫速度也就越慢。也就是說函數中讀寫局部變量老是最快的,而讀寫全局變量老是最慢的。由於全局變量老是在執行環境做用域的末端。其實咱們能夠將函數移出來當全局函數來處理,但那樣會形成全局函數污染,這裏就很少作介紹。

相關文章
相關標籤/搜索