1. 程式人生 > >JavaScript(面向物件+原型理解+繼承+作用域鏈和閉包+this使用總結)

JavaScript(面向物件+原型理解+繼承+作用域鏈和閉包+this使用總結)

JavaScript(面向物件+原型理解+繼承+作用域鏈和閉包+this使用總結)

一、面向物件

1、什麼是面向物件

☞ 面向物件就是把構成問題事物分解成多個物件,建立物件不是為了完成某個步驟,而是描述某個事物在這個解決問題的步驟中的行為。

1.面向物件是一種思維方法

2.面向物件是一種程式設計方法

3.面向物件並不只針對某一種程式語言

2、面向物件和麵向過程的區別和聯絡

1.面向過程過程側重整個問題的解決步驟,著眼區域性或者具體

2.面向物件側重具體的功能,讓某個物件具有這樣的功能。更加側重於整體。

  
  各自的優缺點:
  
  面向過程的優點:
  流程化使得程式設計任務明確,在開發之前基本考慮實現的方法和最終結果;
效率高,面向過程強調程式碼的短小精悍,善於結合資料結構來開發高效率程式; 流程明確,具體步驟清楚,便於節點分析。 面向過程的缺點: 需要深入的思考,耗費精力,程式碼重用性低,擴充套件能力差,維護起來難度比較高,       對複雜業務來說,面向過程的模組難度較高,耦合度也比較高。 面向物件的優點: 結構清晰,程式便於模組化,結構化,抽象化,更加符合人類的思維方式; 封裝性,將事務高度抽象,從而便於流程中的行為分析,也便於操作和自省; 容易擴充套件,程式碼重用率高,可繼承,可覆蓋; 實現簡單,可有效地減少程式的維護工作量,軟體開發效率高。
面向物件的缺點是: 效率低,面向物件在面向過程的基礎上高度抽象,從而和程式碼底層的直接互動非常少機會,從而不適合底層開發和遊戲甚至多媒體開發; 複雜性,對於事務開發而言,事務本身是面向過程的,過度的封裝導致事務本身的複雜性提高。

3、面向物件的實現方式

☞ 面向物件的實現主流有兩種方式:基於類的面向物件和基於原型的面向物件。

☞ 面向物件三大特徵:

封裝

也就是把客觀事物封裝成抽象的類或具體的物件,並且類或物件可以把自己的資料和方法只讓可信的類或者物件操作,對不可信的進行資訊隱藏。

繼承

可以讓某個型別的物件獲得另一個型別的物件的屬性的方法

多肽

不同例項的相同方法在不同情形有不同表現形式。多型機制使具有不同內部結構的物件可以共享相同的外部介面。

3.1 基於類的面向物件

☞ 典型的語言:Java、C#

物件(object)依靠 類(class)來產生

3.2 基於原型的面向物件

☞ 典型的語言:JavaScript

物件(object)則是依靠 構造器(constructor)利用 原型(prototype)構造出來的

4、多種建立物件的方式

4.1 使用new Object()建立

4.2 工廠模式建立

☞ 工廠模式是軟體工程領域一種廣為人知的設計模式,這種模式抽象了建立具體物件的過程,考慮到在 ECMAScript 中無法建立類,開發人員就發明了一種函式,用函式來封裝以特定介面建立物件的細節。

4.3 建構函式模式建立

☞ 為了解決物件型別識別問題,又提出了建構函式模式。這種模式,其實在我們建立一些原生物件的時候,比如Array、Object都是呼叫的他們的建構函式。

  <script type="text/javascript">
  function Person (name, age, sex) {
  this.name = name;
  
  this.age = age;
  this.sex = sex;
  
  this.eat = function () {
  alert(this.name + " Eat food");
  }
  }
  var p1 = new Person("Jack", 20, "man");
  p1.eat();//Jack Eat food
  var p1 = new Person("Mark", 30, "man");
  p1.eat();//Mark Eat food
  alert(p1 instanceof Person);
  </script>

5、建構函式與普通函式的關係

  1. 他們都是函式。建構函式也是函式,也可以像普通的函式一樣進行呼叫。 做普通函式呼叫的時候,因為沒有建立新的物件,所以this其實指向了window物件。

  
  function Person(){
    this.name = "Jack";// 把name屬性新增到了window物件上面
    alert(this === window);  //如果不作為構造方法呼叫,則 是true
  }
  Person();  // 把建構函式當做普通方法呼叫。這個時候內部的this指向了weindow
  alert(window.name);  //Jack
  function Human(){
    this.name = "Mark";
    alert(this instanceof window);  // false
    alert(this instanceof Human);  //true
  }
  var h = new Human();  //當做建構函式來呼叫,建立一個物件
  alert(h.name);
  1. 建構函式和普通函式僅僅也僅僅是呼叫方式的不同。也就是說,隨便一個函式你如果用new 的方式去使用,那麼他就是一個建構函式。

  2. 為了區別,如果一個函式想作為建構函式,作為國際慣例,最好把這個建構函式的首字母大寫。

二、原型理解

1、什麼是原型

☞ 原型就是JavaScript中的繼承的繼承,JavaScript的繼承就是基於原型的繼承。

2、與原型有關的幾個屬性和方法

2.1 prototype屬性

☞ prototype 存在於建構函式中 (其實任意函式中都有,只不過不是建構函式的時候prototype我們不關注而已) ,他指向了這個建構函式的原型物件。

2.2 constructor屬性

☞ constructor屬性存在於原型物件中,它指向了建構函式

  
  <script type="text/javascript">
  function Person () {
  }
  alert(Person.prototype.constructor === Person);// true
  var p1 = new Person();
    //使用instanceof 操作符可以判斷一個物件的型別。  
    //typeof一般用來獲取簡單型別和函式。而引用型別一般使用instanceof,因為引用型別用typeof 總是返回objece。
  alert(p1 instanceof Person);// true
    alert(typeof p1); // object
  </script>

1. 我們根據需要,可以為Person.prototype 屬性指定新的物件,來作為Person的原型物件。

2. 但是這個時候有個問題,新的物件的constructor屬性則不再指向Person構造函數了。

2.3 __proto__屬性(注意:左右各是2個下劃線)

☞ 用構造方法建立一個新的物件之後,這個物件中預設會有一個不可訪問的屬性 [[prototype]] , 這個屬性就指向了構造方法的原型物件。

☞ 但是在個別瀏覽器中,也提供了對這個屬性[[proto]]的訪問(chrome瀏覽器和火狐瀏覽器。ie瀏覽器不支援)。訪問方式:p1.__proto__

☞ 但是開發者儘量不要用這種方式去訪問,因為操作不慎會改變這個物件的繼承原型鏈。

2.4 hasOwnPrototype()方法

☞ 我們都知道,要去訪問一個物件的屬性的時候,這個屬性可能來自物件本身,也可能來自這個物件的[[proto]]屬性指向的原型

☞ hasOwnprotoype()方法,可以判斷一個物件是否來自物件本身。

  <script type="text/javascript">
  function Person () {
  
  }
  Person.prototype.name = "Jack";
  var p1 = new Person();
  p1.sex = "man";
    //sex屬性是直接在p1屬性中新增,所以是true
  alert("sex屬性是物件本身的:" + p1.hasOwnProperty("sex"));
    // name屬性是在原型中新增的,所以是false
  alert("name屬性是物件本身的:" + p1.hasOwnProperty("name"));
    //  age 屬性不存在,所以也是false
  alert("age屬性是存在於物件本身:" + p1.hasOwnProperty("age"));
  
  </script>

◆ 通過hasOwnProperty這個方法可以判斷一個屬性是否在物件本身新增的,但是不能判斷是否存在於原型中,因為有可能這個屬性不存在。

◆ 也即是說,在原型中的屬性和不存在的屬性都會返回fasle。

這個也是唯一的一個處理屬性而不查詢原型鏈的方法!

2.5 in操作符

☞ in操作符用來判斷一個屬性是否存在於這個物件中。但是在查詢這個屬性時候,先在物件本身中找,如果物件找不到再去原型中找。換句話說,只要物件和原型中有一個地方存在這個屬性,就返回true

  <script type="text/javascript">
  function Person () {
  
  }
  Person.prototype.name = "Jack";
  var p1 = new Person();
  p1.sex = "man";
  alert("sex" in p1);// 物件本身新增的,所以true
  alert("name" in p1);//原型中存在,所以true
  alert("age" in p1); //物件和原型中都不存在,所以false
  
  </script>

◆ 如果一個屬性存在,要麼在物件本身中,要麼在原型中。

  <script type="text/javascript">
  function Person () {
  }
  Person.prototype.name = "Jack";
  var p1 = new Person();
  p1.sex = "man";
  
  //定義一個函式去判斷原型所在的位置
  function propertyLocation(obj, prop){
  if(!(prop in obj)){
  alert(prop + "屬性不存在");
  }else if(obj.hasOwnProperty(prop)){
  alert(prop + "屬性存在於物件中");
  }else {
  alert(prop + "物件存在於原型中");
  }
  }
  propertyLocation(p1, "age");
  propertyLocation(p1, "name");
  propertyLocation(p1, "sex");
  </script>

3、組合使用原型模型和建構函式模型建立物件

3.1 原型模型建立物件的缺陷

☞ 原型中的屬性是共享的。就是說,用同一個建構函式建立的物件去訪問原型中的屬性的時候,大家都是訪問同一個物件,如果一個物件對原型的屬性進行更改,則會反映到所有物件上面。

這個共享特性對方法(屬性值是函式的屬性)又是非常合適的。所有的物件共享方法是最佳狀態。這種特性在c#和Java中是天生存在的。

3.2 使用建構函式模型建立物件的缺陷

☞ 在建構函式中新增的屬性和方法,每個物件都有自己獨有的一份,大家不會共享。這個特性對屬性比較合適,但是對方法又不太合適。因為對所有物件來說,他們的方法應該是一份就夠了,沒有必要每人一份,造成記憶體的浪費和效能的低下。

  <script type="text/javascript">
  function Person() {
      this.name = "Jack";
      this.age = 20;
      this.eat = function() {
          alert("Eat food");
      }
  }
  var p1 = new Person();
  var p2 = new Person();
  //每個物件都會有不同的方法
  alert(p1.eat === p2.eat); //fasle
  </script>

可以使用下面的方法解決

  <script type="text/javascript">
  function Person() {
      this.name = "Jack";
      this.age = 20;
      this.eat = eat;
  }
    function eat() {
      alert("Eat food");
      }
  var p1 = new Person();
  var p2 = new Person();
  //因為eat屬性都是賦值的同一個函式,所以是true
  alert(p1.eat === p2.eat); //true
  </script>

但是上面的這種解決方法有個致命的缺點:封裝性太差。使用面向物件。目的之一就是封裝程式碼,這個時候為了效能又要把程式碼抽出物件之外,這是反人類的設計。

3.3 使用組合模式解決上述兩種缺陷

☞ 原型模式適合封裝方法,構造方法模式適合封裝屬性,綜合兩種模式的優點就有了組合模式。

  
  <script type="text/javascript">
        //在構造方法內部封裝屬性
        function Person(name, age) {
            this.name = name;
            this.age = age;
        }
        //在原型物件內封裝方法
        Person.prototype.eat = function (food) {
            alert(this.name + "Eat" + food);
        }
        Person.prototype.play = function (playName) {
            alert(this.name + "Play" + playName);
        }
  
        var p1 = new Person("Jack", 20);
        var p2 = new Person("Mark", 30);
        p1.eat("apple");
        p2.eat("orange");
        p1.play("football");
        p2.play("games");
    </script>

4、動態原型模式建立物件

☞ 前面講到的組合模式,也並非完美無缺,有一點也是感覺不是很完美。把構造方法和原型分開寫,總讓人感覺不舒服,應該想辦法把構造方法和原型封裝在一起,所以就有了動態原型模式。

☞ 動態原型模式把所有的屬性和方法都封裝在構造方法中,而僅僅在需要的時候才去在構造方法中初始化原型,又保持了同時使用建構函式和原型的優點。

  
  <script type="text/javascript">
        //構造方法內部封裝屬性
        function Person(name, age) {
            //每個物件都新增自己的屬性
            this.name = name;
            this.age = age;
            /*
                判斷this.eat這個屬性是不是function,如果不是function則證明是第一次建立物件,
                則把這個funcion新增到原型中。
                如果是function,則代表原型中已經有了這個方法,則不需要再新增。
                perfect!完美解決了效能和程式碼的封裝問題。
            */
            if(typeof this.eat 
            
           

相關推薦

JavaScript(面向物件+原型理解+繼承+作用+this使用總結)

JavaScript(面向物件+原型理解+繼承+作用域鏈和閉包+this使用總結) 一、面向物件 1、什麼是面向物件 ☞ 面向物件就是把構成問題事物分解成多個物件,建立物件不是為了

JavaScript關於作用作用理解

作用域 先來談談變數的作用域  變數的作用域無非就是兩種:全域性變數和區域性變數。  全域性作用域:  最外層函式定義的變數擁有全域性作用域,即對任何內部函式來說,都是可以訪問的: <script>       var outerVar = "outer";

javascript 執行環境,作用

strong str AI UNC 形參 場景 我們 引用 pos 首先看下這條語句: (function($) {…})(jQuery); 1.原理: function(arg){…}這就定義了一個匿名函數,參數為arg 而調用函數時,

關於作用、防止作用汙染、作用理解

作用域 變數的作用域無非就是兩種:全域性作用域和區域性作用域。  全域性作用域:  最外層函式定義的變數擁有全域性作用域,即對任何內部函式來說,都是可以訪問的: <script> var outerVar = "outer";

作用作用

作用域 1.變數的作用域無非就是兩種:全域性變數和區域性變數; 全域性作用域:最外層函式定義的變數擁有全域性作用域,即對任何內部函式來說都是可以訪問的; <script>      var outerVar = "outer"; &nbs

【進階2-1期】深入淺出圖解作用

這是我在公眾號(高階前端進階)看到的文章,現在做筆記 https://github.com/yygmind/blog/issues/17 紅寶書(p178)上對於閉包的定義:閉包是指有權訪問另外一個函式作用域中的變數的函式關鍵在於下面兩點: 是一個函式 能訪問另外一個函式作用域中的變數

執行環境及作用、變數物件作用

一:執行環境及作用域 和 變數物件 執行環境是javascript中最為重要的一個概念。每個執行環境都有一個與之關聯的變數物件(儲存執行環境中所有定義的變數和函式)。二: 當代碼在執行環境中執行時,會建立一個作用域鏈。作用域鏈本質是一個指向變數物件的指標列表。

Javascript中的作用

Javascript中有兩個十分重要的概念–作用域鏈和閉包。 我是這樣理解作用域鏈的,在一個函式中,函式內部所有可以訪問的變數,排成了一個棧,想根據某個變數名訪問變數時,就從棧頂開始向下搜尋,搜到的第一個就算是找到了。那麼,這個棧是怎麼排列的呢?棧頂,是函式的內部變數

作用

重要 難點 返回 data- call 函數定義 code cee post 閉包(closure)是Javascript語言的一個難點,也是它的特色。非常多高級應用都要依靠閉包實現。 神馬是閉包 關於閉包的概念,是婆說婆有理。因而,我就翻閱了紅

JS詳細圖解作用

function 就會 挑戰 timer 重新 http 哈哈 comment bject JS詳細圖解作用域鏈與閉包 攻克閉包難題 初學JavaScript的時候,我在學習閉包上,走了很多彎路。而這次重新回過頭來對基礎知識進行梳理,要講清楚閉包,也是一個非常大的

js內存空間 執行上下文 變量對象詳解 作用 全方位解讀this

變量 詳解 tail bsp pin 上下 AR detail net 內存空間:https://blog.csdn.net/pingfan592/article/details/55189622 執行上下文:https://blog.csdn.net/pingfan592

函數的嵌套作用

global 變量 全局 如果 local 定義 其他 閉包 限制 函數嵌套: #指在第二個函數裏面調用第一個函數的結果 def func():   print(‘sss‘) def func1(f):   f() func1(func) 作用域鏈: #最內層的函數

js函式、作用作用、立即執行函式

1.函式 定義 1.函式宣告 ```java function test(){ 函式體 }; 2.函式表示式: - 命名函式表示式 - ```java var fn = function test(){}; (匿名)函式表示式 var fn = function(){};

函式表示式,遞迴,作用作用與變數

函式表示式 函式表示式是JavaScript中的一個既強大又容易令人困惑的特性。定義函式的方式有兩種:一種是函式宣告(沒錯,不同於C語言之類的),另一種就是函式表示式。 函式申明(這相當於C語言的函式定義)的語法是這樣的: function functionName(a

js深入(三)作用

在之前我們根絕物件的原型說過了js的原型鏈,那麼同樣的js 萬物皆物件,函式也同樣存在這麼一個鏈式的關係,就是函式的作用域鏈 作用域鏈 首先先來回顧一下之前講到的原型鏈的尋找機制,就是例項會先從本身開始找,沒有的話會一級一級的網上翻,直到頂端沒有就會報一個undefined 同樣的js的機制就是這樣的,函式

JavaScript面向物件原型繼承詳解

<html>     <head>         <meta charset="UTF-8"/>         <title></title>         <script type="text/java

JavaScript中的執行上下文、作用、變數物件

主要是理清執行上下文、作用域鏈和變數物件的關係 1.執行上下文 簡而言之,執行上下文就是當前 JavaScript 程式碼被解析和執行時所在環境的抽象概念, JavaScript 中執行任何的程式碼都是在執行上下文中執行。 執行上下文型別: 全域性執

javascript深入理解-從作用理解

contex num 位置 返回 ron 自由 spa 其中 alc 一、概要 紅寶書(P178)對於閉包的定義:閉包就是有權訪問另外一個函數作用域中變量的函數。 MDN,對於閉包的定義:閉包就是指能夠訪問自由變量的函數。 那麽什麽是自由變量?自由變量就是在函數中使用

JavaScript 面向物件之二 —— 函式上下文(call() apply())

本系列文章根據《愛前端邵山歡老師深入淺出的js面向物件》視訊整理歸納 call() 和 apply() 這兩個都是函式的方法,只有函式能夠通過點方法呼叫call()、apply(),表示用指定的上下文執行這個函式。 如下,定義一個函式 fun,當 fun 函式裡

1031 作用this

1.作用域鏈    作用域鏈:瀏覽器給js的一個生存環境(棧記憶體)。    作用域鏈:js中的關鍵字var和function都可以提前宣告和定義,提前宣告和定義的放在我們的記憶體地址(堆記憶體)中。然後js從上到下逐行執行,遇到變數就去記憶體地址查詢是否存在這個變