JavaScript面向對象核心知識概括

面向對象html

概念

  1. 面向對象就是使用對象。面向對象開發就是使用對象開發。
  2. 面向過程就是用過程的方式進行開發。面向對象是對面向過程的封裝。

三大特性

抽象性
所謂的抽象性就是:若是須要一個對象描述數據,須要抽取這個對象的核心數據程序員

  1. 提出須要的核心屬性和方法
  2. 不在特定的環境下沒法明確對象的具體意義

封裝性
對象是將數據與功能組合到一塊兒,即封裝編程

  1. JS對象就是鍵值對的集合,鍵值若是是數據(基本數據、符合數據、空數據)就稱爲屬性,若是鍵值是函數那麼就稱爲方法
  2. 對象就是將屬性與方法封裝起來
  3. 方法是將過程封裝起來

繼承性
所謂繼承性就是本身沒有可是別人有,拿過來成爲本身的,就是繼承,繼承是實現複用的一種手段數組

  • 在Java等語言中繼承知足一個class的規則,類是一個class,他規定了一個對象有什麼屬性和方法。
  • 在這些語言中繼承是class之間的繼承,一個class繼承另外一個class,那麼該class就有了另外一個class的成員,那麼由該class建立出來的對象就同時具備兩個class的成員。

在JS中沒有明確的繼承語法(ES6提供了class extend語法),通常都是按照繼承的理念實現對象的成員擴充實現繼承,所以JS中實現繼承的方法很是對多。緩存

傳統繼承基於類,JS繼承基於對象閉包

一個簡單的繼承模式:混入(mix)app

function mix ( o1, o2 ) { for ( var k in o2 ) { o1[ k ] = o2[ k ]; } }

關於面向對象的一些其餘概念

類class:在JS中就是構造函數編程語言

  • 在傳統的面嚮對象語言中,使用一個叫類的東西定義模板,而後使用模板建立對象。
  • 在構造方法中也具備相似的功能,所以也稱其爲類

實例(instance)與對象(object)函數

  • 實例通常是指某一個構造函數建立出來的對象,咱們稱爲XXXX 構造函數的實例
  • 實例就是對象。對象是一個泛稱
  • 實例與對象是一個近義詞

鍵值對與屬性和方法性能

  • 在JS中鍵值對的集合稱爲對象
  • 若是值爲數據(非函數),就稱該鍵值對爲屬性
  • 若是值爲函數(方法),就稱該鍵值對爲方法method

父類與子類(基類和派生類)

  • 傳統的面嚮對象語言中使用類來實現繼承那麼就有父類、子類的概念
  • 父類又稱爲基類,子類又稱爲派生類
  • 在JS中沒有類的概念,在JS中經常稱爲父對象,子對象,基對象,派生對象。

構造函數

構造函數是幹什麼用的

  1. 初始化數據的
  2. 在JS中給對象添加屬性用的,初始化屬性值用的

建立對象的過程

  1. 代碼:var p = new Person();
  2. 首先運算符new建立了一個對象,相似於{},是一個沒有任何(自定義)成員的對象。
    • 使用new 建立對象,那麼對象的類型就是建立他的構造函數名
    • 使用{}不管如何都是Object類型,至關於new Object
  3. 而後調用構造函數,爲其初始化成員
    • 構造函數在調用的一開始,有一個賦值操做,即this = 剛剛建立出來的對象。
    • 所以在構造函數中this表示剛剛建立出來的對象。
  4. 在構造函數中 利用 對象的動態特性 爲其對象添加成員。

做用域

什麼是做用域

域表示的就是範圍,即做用域,就是一個名字在什麼地方可使用,何時不能使用。

JS中詞法做用域的規則

  1. 函數容許訪問函數外部的數據
  2. 整個代碼結構中只有函數能夠限定做用域
  3. 做用規則首先使用提高規則分析
  4. 若是當前做用域中有了名字了,就不考慮外面的名字

屬性搜索原則

  • 所謂的屬性搜索原則,就是對象在訪問屬性或方法的時候,首先在當前對象中查找
  • 若是當前對象中存儲着屬性或方法,中止查找,直接使用該屬性或方法
  • 若是當前對象沒有該成員,那麼再在其原型對象中查找
  • 若是原型對象中含有該成員,那麼中止查找,直接使用
  • 若是原型中尚未,就到原型的原型中查找
  • 如此往復,直到Object.protitype尚未,那麼就返回undefined
  • 若是是調用方法就報錯,該xxx不是一個函數

閉包

閉包的概念

什麼是閉包?
閉包的含義就是閉合,包起來,簡單的來講,就是一個具備封閉功能與包裹功能的結構。所謂的閉包就是一個具備封閉的對外不公開的,包裹結構,或空間。

爲何函數能夠構成閉包?
閉包就是一個具備封閉與包裹功能的結構,是爲了實現具備私有訪問空間的函數的。函數能夠構成閉包。函數內部定義的數據函數外部沒法訪問,即函數具備封閉性;函數能夠封裝代碼即具備包裹性,因此函數能夠構成閉包。

閉包有什麼用

閉包不容許外部訪問,要解決的問題就是讓外部間接訪問函數內部的數據。事實上,經過使用閉包,咱們能夠作不少事情。好比模擬面向對象的代碼風格;更優雅,更簡潔的表達出代碼;在某些方面提高代碼的執行效率。利用閉包能夠實現以下需求:

  1. 匿名自執行函數
    一個匿名的函數,並當即執行它,因爲外部沒法引用它內部的變量,所以在執行完後很快就會被釋放,關鍵是這種機制不會污染全局對象。
  2. 緩存
    閉包正是能夠作到這一點,由於它不會釋放外部的引用,從而函數內部的值能夠得以保留。
  3. 實現封裝
  4. 模擬面向對象的代碼風格

閉包的基本模型

  1. 對象模式
    函數內部定義個一個對象,對象中綁定多個函數(方法),返回對象,利用對象的方法訪問函數內的數據
  2. 函數模式
    函數內部定義一個新函數,返回新函數,用新函數得到函數內的數據
  3. 沙箱模式
    沙箱模式就是一個自調用函數,代碼寫到函數中同樣會執行,可是不會與外界有任何的影響

閉包的做用舉例

  1. 得到超過一個數據,返回一個對象
  2. 完成讀取一個數據和修改這個數據,對象裏面的get\set方法

閉包的性能問題

函數執行須要內存,那麼函數中定義的變量,會在函數執行結束後自動回收,凡是由於閉包結構的,被引出的數據,若是還有變量引用這些數據的話,那麼這些數據就不會被回收。所以在使用閉包的時候若是不使用某些數據了,必定要賦值一個null

var f = (function () { var num = 123; return function () { return num; }; })(); // f 引用着函數,函數引用着變量num // 所以在不使用該數據的時候,最好寫上 f = null;

原型

什麼是原型

一句話說明什麼是原型:原型能存儲咱們的方法,構造函數建立出來的實例對象可以引用原型中的方法。

JS中一切皆對象,而每一個對象都有一個原型(Object除外),這個原型,大概就像Java中的父類,因此,基本上你能夠認爲原型就是這個對象的父對象,即每個對象(Object除外)內部都保存了它本身的父對象,這個父對象就是原型。通常建立的對象若是沒有特別指定原型,那麼它的原型就是Object(這就很相似Java中全部的類默認繼承自Object類)。

ES6經過引入class ,extends等關鍵字,以一種語法糖的形式把構造函數包裝成類的概念,更便於你們理解。是但願開發者再也不花精力去關注原型以及原型鏈,也充分說明原型的設計意圖和類是同樣的。

查看對象的原型

當對象被建立以後,查看它們的原型的方法不止一種,之前通常使用對象的__proto__屬性,ES6推出後,推薦用Object.getPrototypeOf()方法來獲取對象的原型

function A(){ this.name='lala'; } var a=new A(); console.log(a.__proto__) //輸出:Object {} //推薦使用這種方式獲取對象的原型 console.log(Object.getPrototypeOf(a)) //輸出:Object {}

不管對象是如何建立的,默認原型都是Object,在這裏須要說起的比較特殊的一點就是,經過構造函數來建立對象,函數A自己也是一個對象,而A有兩個指向表示原型的屬性,分別是proto和prototype,並且兩個屬性並不相同

function A(){ this.name='lala'; } var a=new A(); console.log(A.prototype) //輸出:Object {} console.log(A.__proto__) //輸出:function () {} console.log(Object.getPrototypeOf(A)) //輸出:function () {}

函數的的prototype屬性只有在看成構造函數建立的時候,把自身的prototype屬性值賦給對象的原型。而實際上,做爲函數自己,它的原型應該是function對象,而後function對象的原型纔是Object。

總之,建議使用ES6推薦的查看原型和設置原型的方法。

原型的用法

其實原型和類的繼承的用法是一致的:當你想用某個對象的屬性時,將當前對象的原型指向該對象,你就擁有了該對象的使用權了。

function A(){ this.name='world '; } function B(){ this.bb="hello" } var a=new A(); var b=new B(); //將b設置爲a的原型,此處有一個問題,即a的constructor也指向了B構造函數,可能須要糾正  Object.setPrototypeOf(a,b); a.constructor=A; console.log(a.bb); //hello

若是使用ES6來作的話則簡單許多,甚至不涉及到prototype這個屬性

class B{ constructor(){ this.bb='hello' } } class A extends B{ constructor(){ super(); this.name='world'; } } var a=new A(); console.log(a.bb+" "+a.name); //hello world console.log(typeof(A)) //"function"

怎麼樣?是否是已經徹底看不到原型的影子了?活脫脫就是類繼承,可是你也看獲得實際上類A 的類型是function,因此說,本質上class在JS中是一種語法糖,JS繼承的本質依然是原型,不過,ES6引入class,extends 來掩蓋原型的概念也是一個很友好的舉動,對於長期學習那些類繼承爲基礎的面對對象編程語言的程序員而言。

個人建議是,儘量理解原型,儘量用class這種語法糖。

好了,問本身兩個問題:

  1. 爲何要使用原型?——提升函數的複用性。
  2. 爲何屬性不放在原型上而方法要放在原型上?
    • 利用對象的動態特性:構造函數.prototype.xxxx = vvv
    • 利用直接替換
      Student.prototype = { sayHello : function(){}, study : function(){} };

原型鏈

什麼是原型鏈?
凡是對象就有原型,那麼原型又是對象,所以凡是給定一個對象,那麼就能夠找到他的原型,原型還有原型,那麼如此下去,就構成一個對象的序列,稱該結構爲原型鏈。

這個概念其實也變得比較簡單,能夠類比類的繼承鏈條,即每一個對象的原型往上追溯,一直到Object爲止,這組成了一個鏈條,將其中的對象串聯起來,當查找當前對象的屬性時,若是沒找到,就會沿着這個鏈條去查找,一直到Object,若是還沒發現,就會報undefined。

原型鏈的結構
凡是使用構造函數,建立出對象,而且沒有利用賦值的方式修改原型,就說該對象保留默認的原型鏈。
默認原型鏈結構是什麼樣子呢?

function Person(){} var p = new Person(); //p 具備默認的原型鏈

默認的原型鏈結構就是:當前對象 -> 構造函數.prototype -> Object.prototype -> null

在實現繼承的時候,有時候會利用替換原型鏈結構的方式實現原型繼承,那麼原型鏈結構就會發生改變

function DunizbCollection(){} DunizbCollection.prototype = []; var arr = new DunizbCollection();

此時arr對象的原型鏈結構被指向了數組對象的原型鏈結構了:arr -> [] -> Array.prototype -> Object.prototype -> null

用圖形表示對象的原型鏈結構
以以下代碼爲例繪製原型鏈結構

function Person(){} var p = new Person();

原型鏈結構圖爲:
圖片描述

使用原型須要注意兩點:

  1. 原型繼承鏈條不要太長,不然會出現效率問題。
  2. 指定原型時,注意constructor也會改變。

繼承

實現繼承有兩種常見方式:

  1. 混合式繼承:最簡單的繼承就是將別的對象的屬性強加到我身上,那麼我就有這個成員了。
    混合式繼承的簡單描述:
    var Person = function () {}; Person.prototype.extend = function ( o ) { for ( var k in o ) { this[ k ] = o[ k ]; } }; Person.prototype.extend({ run: function () { console.log( '我能跑了' ); }, eat: function () { console.log( '我能夠吃了' ); }, sayHello: function () { console.log( '我吃飽了' ); } });
  2. 原型繼承:利用原型也能夠實現繼承,不須要在我身上添加任何成員,只要原型有了我就有了。
  3. 借用構造函數繼承
    這種技術的基本思想至關簡單,即在子類型構造函數的內部調用超類型構造函數,而函數只不過是在特定環境中執行代碼的對象,所以經過使用apply()call()方法也能夠在(未來)新建立的對象上執行構造函數
    function Person ( name, age, gender ) { this.name = name; this.age = age; this.gender = gender; } // 須要提供一個 Student 的構造函數建立學生對象 // 學生也應該有 name, age, gender, 同時還須要有 course 課程 function Student ( name, age, gender, course ) { Person.call( this, name, age, gender ); this.course = course; }

在《JavaScript高級程序設計(第三版)》中詳細介紹了繼承的6種方式

函數的四種調用模式

函數模式

就是一個簡單的函數調用。函數名的前面沒有任何引導內容。

方法模式

方法必定式依附與一個對象,將函數賦值給對象的一個屬性,那麼就成爲了方法。

構造器調用模式

建立對象的時候構造函數作了什麼?因爲構造函數只是給 this 添加成員,沒有作其餘事情。而方法也能夠完成這個操做,就是 this 而言,構造函數與方法沒有本質的區別。

特徵

  1. 使用 new 關鍵字,來引導構造函數。
  2. 構造函數中的 this 與方法中的同樣,表示對象,可是構造函數中的對象是剛剛建立出來的對象
  3. 構造函數中不須要 return ,就會默認的 return this。
    • 若是手動添加return ,就至關於 return this
    • 若是手動的添加 return 基本類型,無效,仍是保留原來 返回 this
    • 若是手動添加的 return null,或 return undefined ,無效
    • 若是手動添加 return 對象類型,那麼原來建立的 this 就會被丟掉,返回的是 return 後面的對象

建立對象的模式

  1. 工廠方法,工廠就是用來生產的,所以若是函數建立對象並返回,就稱該函數爲工廠函數
  2. 構造方法
  3. 寄生式建立
  4. 混合式建立

上下文調用模式

上下文就是環境。就是本身定義設置 this 的含義。

語法

  1. 函數名.apply( 對象, [ 參數 ] );
  2. 函數名.call( 對象, 參數 );

描述

  1. 函數名就是表示函數自己,使用函數進行調用的時候默認 this 是全局變量
  2. 函數名也能夠是方法提供,使用方法調用的時候,this 是指向當前對象
  3. 使用 apply 進行調用後,不管是函數仍是方法都無效了,咱們的 this ,由 apply 的第一個參數決定

參數問題
不管是 call 仍是 apply 在沒有後面的參數的狀況下(函數無參數,方法五參數)是徹底一致的

function foo(){ console.log( this ); } foo.apply( obj ); foo.call( obj );

第一個參數的使用也是有規則的:

  1. 若是傳入的是一個對象,那麼就至關於設置該函數中的 this 爲參數
  2. 若是不傳入參數,或傳入 null 、undefined 等,那麼至關於 this 默認爲 window

    foo(); foo.apply(); foo.apply( null ); foo.call( undefined );
  3. 若是傳入的是基本類型,那麼 this 就是基本類型對應的包裝類型的引用

在使用上下文調用的時候,原函數(方法)可能會帶有參數,那麼這個參數再上下文調用中使用 第二個(第 n 個)參數來表示

function foo( num ) { console.log( num ); } foo.apply( null, [ 123 ] ); // 等價於 foo( 123 );

參考資料

做者: Dunizb 連接:http://www.imooc.com/article/13649來源:慕課網本文原創發佈於慕課網 ,轉載請註明出處,謝謝合做!

相關文章
相關標籤/搜索