從零開始學習前端JAVASCRIPT — 12、JavaScript面向對象編程

来源:https://www.cnblogs.com/witkeydu/archive/2018/02/28/8481648.html
-Advertisement-
Play Games

未完待續。。。。。。 1:構造函數的使用 2:構造函數和對象的關係 3:使用JSON創建對象 4:使用構造函數創建對象 5:面向對象和麵向過程的區別 6:類的概念 7:類和對象 8:JSON字元串和對象直接的轉換 ...


一、閉包

1 . 概念:閉包就是能夠讀取其他函數內部變數的函數。在JS中,只有函數內部的子函數才能讀取局部變數,因此可以把閉包簡單理解為”定義在一個函數內部的函數”。

2 . 閉包的特點

1)可以讀取函數內部的變數。

2)讓這些變數的值始終保存在記憶體中。

3 . 閉包的原理

理解閉包,首先必須理解JS變數的作用域。變數的作用域無非就是兩種(es5):全局變數和局部變數。

JS語言的特殊之處,就在於函數內部可以直接讀取全局變數。另一方面,函數外部自然無法讀取函數內的局部變數。

註意:

1)函數內部聲明變數的時候,一定要使用var聲明。如果不用的話,你實際上聲明瞭一個全局變數。

2)局部變數的作用域,在函數定義的時候就已經確定下來了。

出於各種原因,我們有時候需要得到函數內部的局部變數。但是正常情況下這是辦不到的。只有變通一下才能實現,那就是在函數內部再定義一個函數。外部變數不能訪問內部變數,內部變數卻能訪問外部變數,這正是因為JS特有的”鏈式作用域”結構(chain scope),子對象會一級一級地向上尋找所有父對象的變數。所以父對象的所有變數,對子對象都是可見的,反之則不成立。我們只需要把子函數返回出來,我們就可以在外部讀取內部變數了。

4 . 閉包的應用場景

1)函數作為返回值。 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>閉包</title>
</head>
<body>
    <script>
        function f1() {
            var n = 999;
            nAdd = function () {
                n += 1;
            }
            function f2() {
                console.log(n)
            }
            return f2;
        }
        var result = f1();
        console.log("result的第一次執行")
        result();//999
        console.log("nAdd的執行")
        nAdd();//無輸出
        console.log("result的第二次執行")
        result();//1000
    </script>
</body>
</html>

   2)函數作為參數被傳遞。 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>閉包</title>
</head>
<body>
    <script>
        function fun(n, o) {
            console.log(o);
            return {
                fun: function (m) {
                    return fun(m, n);
                }
            };
        }
        var a = fun(0); //undefined
        // 執行完並未銷毀保存在記憶體中
        a.fun(1); //0
        a.fun(2); //0
        a.fun(3); //0

        fun(0).fun(1).fun(2).fun(3);
        //undefined、0、1、2
        var a = fun(0).fun(1); 
        //undefined、0
        a.fun(2); 
        //undefined、1
        a.fun(3);        
        //undefined、1
    </script>        
</body>
</html>

 5 . 使用閉包註意點

1)由於閉包會使得函數中的變數都被保存在記憶體中,記憶體消耗很大,所以不能濫用閉包。否則會造成網頁性能問題,在IE中可能導致記憶體泄漏。解決方法就是在函數退出之前,將不使用的局部變數刪除(值置為null,垃圾回收機制就會處理)

2)閉包會在父函數外部,改變父函數內部變數的值。所以不要隨便改變父函數內部變數的值。

6 . demo通過js閉包實現滑鼠滑過隔行換色的效果 

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>閉包實現各行換色</title>
    <style type="text/css">
        *{
            margin: 0;
            padding: 0;
        }
        h3{
            text-align: center;
            font-size: 24px;
            line-height: 60px;
        }
        .newList{
            width: 80%;
            margin: 0 auto;
            list-style: none;
        }
        .newList li{
            text-indent: 24px;
            line-height: 50px;
            font-size: 16px;
            border-top: 1px dashed #eeeeee;
        }
    </style>
</head>
<body>
    <h3>新聞列表</h3>
    <ul id="newList" class="newList">
        <li>這是第1條新聞</li>
        <li>這是第2條新聞</li>
        <li>這是第3條新聞</li>
        <li>這是第4條新聞</li>
        <li>這是第5條新聞</li>
        <li>這是第6條新聞</li>
        <li>這是第7條新聞</li>
        <li>這是第8條新聞</li>
        <li>這是第9條新聞</li>
    </ul>
</body>
<script type="text/javascript">
    var oNewList=document.getElementById('newList');
    var oNewListArr=Array.from(oNewList.children);
    oNewListArr.forEach(function (v,i) {
        // 隔行換色
        if(i % 2 === 0) {
            oNewListArr[i].style.background = '#f3f3f3';
        };
        //滑鼠滑過改變背景色
        v.onmouseover=function () {
            this.style.background="#87ceeb";
        };
        //滑鼠滑過恢複原背景色
        (function (m){
            oNewListArr[m].onmouseout=function () {
                if(m % 2 === 0) {
                    oNewListArr[i].style.background = '#f3f3f3';
                }
                else{
                    oNewListArr[i].style.background = '#ffffff';
                }
            }
        })(i);
    })
</script>
</html>

 二、構造函數的繼承

 所謂"構造函數",其實就是一個普通函數,但是內部使用了this變數。對構造函數使用new 運算符,就能生成實例,並且this變數會綁定在實例對象上。

 詳解new的執行過程:

  1. 在記憶體生成一個實例對象obj。
  2. 指定實例對象的__proto__到構造函數的prototype。
  3. 運行構造函數,相當於運行fn.call(obj)。
  4. 檢查返回值,如果返回值為基本數據類型,則無視該返回值,而將生成的對象返回。如果為引用類型,則將該返回值返回。

構造函數很好用,但是存在浪費記憶體的問題。

JS規定,每一個構造函數都有一個prototype屬性,指向另一個對象。此對象的所有屬性和方法,都會被構造函數的實例繼承。 

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>構造函數</title>
</head>
<body>    
</body>
<script type="text/javascript">
    //構造函數的命名首字母一般大寫
    function Car(carlogo) {
        this.carLogo = carlogo;
        this.whistle = function () {
            console.log("正在鳴笛......")
        }
    }
    Car.prototype.run = function() {
        console.log("正在行走......")
    };
    var bmw=new Car("BMW");
    var audi=new Car("Audi");
    console.log("構造函數創建的實例:")
    console.log(bmw === audi)
    console.log(bmw,audi)
    console.log("構造函數whisle屬性:")
    console.log(bmw.whisle === audi.whisle)
    console.log("原型run方法:");
    console.log(bmw.run === audi.run);

</script>
</html>

  •  instanceof運算符:判斷對象是不是構造函數的實例,是則true,否則false。
    console.log(bmw instanceof Car)//true
    console.log(bmw instanceof Array)//false
  • isPrototypeOf:判斷prototype對象和某個實例之間的關係。
    console.log(Car.prototype.isPrototypeOf(bmw));//true
    console.log(Array.prototype.isPrototypeOf(bmw));//false
  • hasOwnProperty:每個實例對象都有一個hasOwnProperty方法,用來判斷某一個屬性到底是本地屬性,還是繼承自prototype對象的屬性。
    bmw.hasOwnProperty('carlogo');//true
    bmw.hasOwnProperty('run');//false
  •  in運算符:in運算符可以用來判斷,某個實例是否含有某個屬性,不管是不是本地屬性。
console.log('run' in bmw);//true

 註:

詳解instanceof 運算符。         // 會沿著原型鏈查找。

詳解hasOwnProperty方法。 // 不會沿著原型鏈查找。

詳解isPrototypeOf方法。   // 會沿著原型鏈查找。

詳解in運算符。                        // 會沿著原型鏈查找。 


三、call/apply繼承

call和apply都是為了改變某個函數運行時的context即上下文而存在的,即改變函數內部this的指向。

Fn.call(obj, arg1, arg2 [, argN]);

fn,.apply(obj, [arg1, arg2,…, argN]);

作用相同,apply以數組的形式傳參,call是以列表的形式。 

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>構造函數</title>
</head>
<body>    
</body>
<script type="text/javascript">
//構造函數
function Person(name) {
    this.name = name;
}

var xiaoJun = {
    name: '小軍'
};

var xiaoHua = {
    name: '小花',
    sing: function (where, music) {
        console.log(this.name + '正在' + where + '' + music);
    }
};

xiaoHua.sing('馬路上', '葫蘆娃');
//列表形式傳參
xiaoHua.sing.call(xiaoJun, '音樂廳', '屋頂');
//數組方式傳參
xiaoHua.sing.apply(xiaoJun, ['KTV', '我們不一樣']);
//列表形式傳參,且與call、apply的執行方式不同,需要調用
var func = xiaoHua.sing.bind(xiaoJun);
func('教室', '蕩起雙槳');

</script>
</html>

apply和call實現繼承:

將父對象的構造函數綁定在子對象上,即在子對象構造函數中加一行:

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>繼承</title>
</head>
<body>    
</body>
<script type="text/javascript">
    function Animal () {
        this.eyes = 2;
    }
    function Dog(name) {
        Animal.call(this);
        this.name = name;
    }
    var oWangCai = new Dog('旺財');
    console.log(oWangCai);

</script>
</html>

四、prototype的概念

  1. 一切引用類型都是對象。
  2. 對象是屬性的集合。
  3. 對象都是通過構造函數創建的。
  4. 每個函數都有一個prototype屬性,即原型。對象都有__proto__屬性,可以成為隱式原型。這個__proto__屬性是一個隱藏的屬性,JS並不希望開發者能夠用到這個屬性,有的低版本瀏覽器甚至不支持這個屬性值。
  5. 每個對象的__proto__屬性指向創建該對象的函數的prototype。
  6. Object.prototype.__proto__指向null。

原型鏈:

訪問一個對象的屬性時,先在本地屬性中查找,如果沒有,再沿著__proto__這條鏈向上找,這就是原型鏈。

 

constructor屬性:函數的prototype有一個constructor屬性,該屬性指向了函數本身。對象沒有constructor屬性,它沿著原型鏈使用的是對象的構造函數的constructor屬性。

五、this的使用情況

1:構造函數(在new的情況下)

     this指向的是新構建出來的對象。

2:函數作為對象的一個屬性

     函數中的this指向的是該對象。

3:函數call或者apply

     當函數被call或者apply調用時,this的值就取傳入對象的值。

4:全局函數 & 普通函數

     全局函數或者普通函數中,this指向的是window。

5:在prototype原型對象中,this指向的是調用構造函數實例出來的對象。

註:this關鍵字的值在函數運行的時候才會被指定。

六、原型鏈的繼承:

將一個構造函數的原型指向另一個構造函數的實例對象來實現繼承。 

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>原型鏈繼承</title>
</head>
<body>    
</body>
<script type="text/javascript">
function Person() {
    this.age = 0;
}

function Man() {
    this.beard = '鬍子';
}

var person = new Person();
//改變man的原型指向
Man.prototype = person;
var man1  = new Man();
console.log(man1.beard);
console.log(man1.age);

</script>
</html>

原型鏈的繼承必須將Man的prototype.constructor指向更改過來,否則它將會指向People,發生原型混亂。也就是下一點講述的混合繼承


七、混合繼承:

原型鏈與構造函數的優點,組合而成的一個模式,即原型鏈繼承方法,而在構造函數繼承屬性

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>混合繼承</title>
</head>
<body>    
</body>
<script type="text/javascript">
function Person() {
    this.age = 0;
}

Person.prototype.introduce = function () {
    console.log('我有' + this.beard + ';今年' + this.age + '');
}

function Man() {
    Person.call(this);
    this.beard = '鬍子';
}

var person = new Person();
console.log("prototype.constructor指向未改:")
Man.prototype = person;//改變函數原型指向
person.age = 1;
var man1  = new Man();
var man2 = new Man();
console.log(man1.age);
console.log(man2.age);

console.log("prototype.constructor指向改變:")
Man.prototype.constructor = Man;//改變構造函數指向
var man3  = new Man();
var man4 = new Man();
man3.age = 1;
console.log(man3);
console.log(man4);
man3.introduce();
man4.introduce();

</script>
</html>

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 退出但不關閉: 這是Android對於Linux的優化。當 Android 應用程式退出時,並不清理其所占用的記憶體,Linux 內核進程也相應的繼續存在,所謂“退出但不關閉”。從而使得用戶調用程式時能夠在第一時間得到響應。 應用切換到後臺是暫停的,完全不耗cpu和電量,只保留了運行狀態。如果app需 ...
  • We can create subclass within a class cluster that defines a class that embeds within it an object. These class objects are composite objects. So you ...
  • LSCacheFile.h LSCacheFile.m ...
  • 前言: 上一期實現了簡單的QQ登錄效果,這一期繼續對上一期進行擴展 本期的知識點: Toast彈窗,三種方法實現按鈕的點擊事件監聽 正文: Toast彈窗其實很簡單,在Android Studio中打上toast,之後按下tab鍵就能快捷生成一個Toast 這樣就能實現彈出Toast,我將之前彈出對 ...
  • 如果你決定用UIView動畫或Core Animation,一定要編寫一些測試用例,模擬游戲可能遇到的要求最高的動畫,另外不要忘記播放聲音。不要等到最後才增加聲音,因為在iPhone上播放音樂和音效確實會極大地耗費處理能力。必須將播放聲音作為模擬的一部分。 從用戶反饋給我們的崩潰日誌來看,我們發現F ...
  • All Objective-C programs are composed of the following two fundamental elements: Program statements (code): This is the part of a program that perform ...
  • Android項目的目錄結構 Activity:應用被打開時顯示的界面 src:項目代碼 R.java:項目中所有資源文件的資源id Android.jar:Android的jar包,導入此包方可使用Android的api libs:導入第三方jar包 assets:存放資源文件,比方說mp3、視頻 ...
  • (註:本章講解涉及部分後端知識,將以php提供數據的方式進行相應的demo實現) 1:ajax的概念 全稱:Asynchronous Javascript And Xml AJAX不是一種新的編程語言,而是一種用於創建更快更好以及交互性更強的WEB應用程式技術,該技術在98年前後得到了應用。通過AJ ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...