1. 程式人生 > >[Javascript 高階程式設計]學習心得記錄9 js面向物件

[Javascript 高階程式設計]學習心得記錄9 js面向物件

    感覺最難的部分就是面向物件了,大學期間學習的是面向過程的c/c++,工作之後也沒有深入瞭解過面向物件,通過這次的學習和回顧,也算是對面向物件有了新的認識。不過,就我在書上學到了結合個人理解隨便說說,很可能有理解錯誤或者不準確的地方。js的物件和其他語言的物件並不完全一樣,可以理解為散列表。物件的每個屬性或方法都有一個名字,而每個名字都對映到一個值。

    首先需要知道一個定義,特性。我覺得可以理解為物件的屬性的屬性,用於描述屬性的各種特徵。可以用來對物件的屬性進行管理,比如屬性資料的讀寫和訪問許可權。但是我看完了之後,覺得這個功能雖然強大,不過目前用不到,以後碰到的時候再補充吧。下面好好講講建立物件和繼承。

一,建立物件

    前面一篇文章提到了建立物件的一些方法,new Object和物件字面量。建立一個物件還行,建立多個的話程式碼量就太多了,所以現在有很多其他的建立物件的方法。

1,工廠模式

    一句話,用函式建立物件。兩句話,建立一個新物件,然後把這個物件指標賦給新變數。

        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("Nicholas", 29, "Software Engineer");
        var person2 = createPerson("Greg", 27, "Doctor");
        
        person1.sayName();   //"Nicholas"
        person2.sayName();   //"Greg"

    但是這樣建立的話,每個物件的型別都是object,無法識別區別。

2,建構函式模式

    像Object和Array這樣的原生建構函式,可以通過new呼叫來直接建立物件。而我們也可以自定義建構函式,從而自定義物件型別的屬性和方法。

        function Person(name, age, job){
            this.name = name;
            this.age = age;
            this.job = job;
            this.sayName = function(){
                alert(this.name);
            };    
        }
        
        var person1 = new Person("Nicholas", 29, "Software Engineer");
        var person2 = new Person("Greg", 27, "Doctor");
        
        person1.sayName();   //"Nicholas"
        person2.sayName();   //"Greg"
        
        alert(person1 instanceof Object);  //true
        alert(person1 instanceof Person);  //true
        alert(person2 instanceof Object);  //true
        alert(person2 instanceof Person);  //true
        
        alert(person1.constructor == Person);  //true
        alert(person2.constructor == Person);  //true
        
        alert(person1.sayName == person2.sayName);  //false  
用new操作符呼叫建構函式實際上經過了四步:(1),建立一個新物件。(2),將建構函式的作用域賦給新物件。(3),執行建構函式的程式碼,給新物件增加屬性。(4),返回新物件。這兩個生成的物件都有一個建構函式屬性constructor,指向建構函式Person。對於建構函式這一類特殊函式,還有值得說明的地方。

首先,建構函式是可以直接呼叫的,直接把屬性賦給window物件。

        function Person(name, age, job){
            this.name = name;
            this.age = age;
            this.job = job;
            this.sayName = function(){
                alert(this.name);
            };
        }
        
        var person = new Person("Nicholas", 29, "Software Engineer");
        person.sayName();   //"Nicholas"
        
        Person("Greg", 27, "Doctor");  //adds to window
        window.sayName();   //"Greg"
        
        var o = new Object();
        Person.call(o, "Kristen", 25, "Nurse");
        o.sayName();    //"Kristen"
其次,建構函式有一個問題就是,如果像上面那麼定義的話,會定義很多sayName函式,但是都是做同一件事情(每次例項建構函式都會執行內部程式碼)。明顯不太好,把方法定義放在函式外部可以解決這個問題。
        function Person(name, age, job){
            this.name = name;
            this.age = age;
            this.job = job;
            this.sayName = sayName;
        }
        
        function sayName(){
            alert(this.name);
        }
        
        var person1 = new Person("Nicholas", 29, "Software Engineer");
        var person2 = new Person("Greg", 27, "Doctor");
        
        person1.sayName();   //"Nicholas"
        person2.sayName();   //"Greg"
        
        alert(person1 instanceof Object);  //true
        alert(person1 instanceof Person);  //true
        alert(person2 instanceof Object);  //true
        alert(person2 instanceof Person);  //true
        
        alert(person1.constructor == Person);  //true
        alert(person2.constructor == Person);  //true
        
        alert(person1.sayName == person2.sayName);  //true    
    不過這樣也有問題,直接打字書上的原話:在全域性作用域中定義的函式實際上只能被某個物件呼叫,這讓全域性作用域有點名不副實。而更讓人無法接受的是,如果物件需要定義很多方法,那麼就要定義很多個全域性函式,於是我們這個定義的引用型別就沒有絲毫的封裝性可言了。好在這些問題都可以通過原型模式解決。

3,原型模式
    我們建立的每個函式都有一個prototype(原型)屬性,這個屬性是一個指標,指向一個物件,而這個物件的用途是包含可以由特定型別的所有例項共享的屬性和方法。實際上,prototype就是原型物件,不必在建構函式中定義物件例項的資訊,而是可以直接把這些資訊新增到原型物件中。

        function Person(){
        }
        
        Person.prototype.name = "Nicholas";
        Person.prototype.age = 29;
        Person.prototype.job = "Software Engineer";
        Person.prototype.sayName = function(){
            alert(this.name);
        };
        
        var person1 = new Person();
        person1.sayName();   //"Nicholas"
        
        var person2 = new Person();
        person2.sayName();   //"Nicholas"
      
        alert(person1.sayName == person2.sayName);  //true
        
        alert(Person.prototype.isPrototypeOf(person1));  //true
        alert(Person.prototype.isPrototypeOf(person2));  //true
        
        //only works if Object.getPrototypeOf() is available
        if (Object.getPrototypeOf){
            alert(Object.getPrototypeOf(person1) == Person.prototype);  //true
            alert(Object.getPrototypeOf(person1).name);  //"Nicholas"
        }
    個人理解,這種方法就是改良工廠方法,相當於把工廠方法中的新建物件和賦值的過程,變成了把原型物件作為原型物件屬性賦給新物件的過程。同時我們這個時候修改例項的屬性時,會在例項下新增屬性。而在我們呼叫例項的屬性時,會先查詢新物件新增加的屬性,然後找找原型物件的屬性。可以通過in,判斷屬性是否存在。可以通過hasOwnProperty(),判斷屬性是在例項還是原型。(就不舉例了)
        function Person(){
        }
        
        Person.prototype.name = "Nicholas";
        Person.prototype.age = 29;
        Person.prototype.job = "Software Engineer";
        Person.prototype.sayName = function(){
            alert(this.name);
        };
        
        var person1 = new Person();
        var person2 = new Person();
        
        person1.name = "Greg";
        alert(person1.name);   //"Greg" ?from instance
        alert(person2.name);   //"Nicholas" ?from prototype
可以直接重寫原型物件,這樣更方便,不過這樣一來,本來從建構函式獲取的原型物件的建構函式屬性就變成Object了,需要重定義一下。
        function Person(){
        }
        
        Person.prototype = {
            constructor : Person,
            name : "Nicholas",
            age : 29,
            job: "Software Engineer",
            sayName : function () {
                alert(this.name);
            }
        };

        var friend = new Person();
        
        alert(friend instanceof Object);  //true
        alert(friend instanceof Person);  //true
        alert(friend.constructor == Person);  //true
        alert(friend.constructor == Object);  //false
與此同時,重寫原型物件會切斷現有原型和任何之前已經存在的物件例項之間的聯絡;它們引用的仍然是最初的原型。

然而,原型物件方法有一個致命缺點,對於包含引用型別的屬性,修改例項物件會影響原型物件從而影響所有物件。

        function Person(){
        }
        
        Person.prototype = {
            constructor: Person,
            name : "Nicholas",
            age : 29,
            job : "Software Engineer",
            friends : ["Shelby", "Court"],
            sayName : function () {
                alert(this.name);
            }
        };
        
        var person1 = new Person();
        var person2 = new Person();
        
        person1.friends.push("Van");
        
        alert(person1.friends);    //"Shelby,Court,Van"
        alert(person2.friends);    //"Shelby,Court,Van"
        alert(person1.friends === person2.friends);  //true
4,組合使用建構函式模式和原型模式

    講了這麼多有問題的方法,講一個能用而且最常用的方法吧。組合了建構函式模式和原型模式,建構函式模式用於定義例項屬性,原型模式用於定義方法和共享屬性。

        function Person(name, age, job){
            this.name = name;
            this.age = age;
            this.job = job;
            this.friends = ["Shelby", "Court"];
        }
        
        Person.prototype = {
            constructor: Person,
            sayName : function () {
                alert(this.name);
            }
        };
        
        var person1 = new Person("Nicholas", 29, "Software Engineer");
        var person2 = new Person("Greg", 27, "Doctor");
        
        person1.friends.push("Van");
        
        alert(person1.friends);    //"Shelby,Court,Van"
        alert(person2.friends);    //"Shelby,Court"
        alert(person1.friends === person2.friends);  //false
        alert(person1.sayName === person2.sayName);  //true
5.動態原型模式

    這個我看了幾遍都沒看懂,看了知乎上面的一個解釋瞬間明白了。可以注意到第四種組合方法裡面,原型物件是採用重寫方法的,當必須採用字面量方法時,那就會碰到建構函式模式的問題,多次定義相同函式,這個時候加個判斷問題就迎刃而解了。

        function Person(name, age, job){
        
            //properties
            this.name = name;
            this.age = age;
            this.job = job;
            
            //methods
            if (typeof this.sayName != "function"){
            
                Person.prototype.sayName = function(){
                    alert(this.name);
                };
                
            }
        }

        var friend = new Person("Nicholas", 29, "Software Engineer");
        friend.sayName();
此外還有寄生建構函式模式和穩妥建構函式模式就不說了。

二,繼承

    繼承和建立有很多類似的地方,但是現在用的少,以後想起來再寫吧。







相關推薦

[Javascript 高階程式設計]學習心得記錄9 js面向物件

    感覺最難的部分就是面向物件了,大學期間學習的是面向過程的c/c++,工作之後也沒有深入瞭解過面向物件,通過這次的學習和回顧,也算是對面向物件有了新的認識。不過,就我在書上學到了結合個人理解隨便說說,很可能有理解錯誤或者不準確的地方。js的物件和其他語言的物件並不完全

[Javascript 高階程式設計]學習心得記錄10 js函式表示式

    在前面說物件的時候已經提到了函式物件,對函式的定義引數的傳遞包括通過argumentd.callee實現遞迴。這篇部落格我會繼續深入講解js中的函式表示式。 一,閉包     關於閉包的概念,可以先看看http://www.jb51.net/article/2410

[Javascript 高階程式設計]學習心得記錄11 js的BOM

    BOM(瀏覽器物件模型)是web中js的核心,而BOM的核心物件就是window物件。在瀏覽器中,window物件有雙重角色,它既是通過js訪問瀏覽器的一個介面,又是規定的Global物件。此外,還有location等物件,因為整體比較簡單,下面說一些值得注意的東西

[Javascript 高階程式設計]學習心得記錄7 引用型別(上)

    js引用型別的值(物件)是引用型別的例項,引用型別和類很像但是又不是同一個東西,引用型別被稱為対象定義,描述一類物件所具有的屬性和方法。     一,object型別     object型別是js基礎原生型別,建立該型別例項有兩種方法:new和物件字面量表示法。

Javascript高階程式設計學習筆記(20)—— 建立物件

由於今天有點事,加上物件原型鏈的東西有點多,所以今天這篇就講一個小的知識點吧 也算為明天的物件繼承做鋪墊   工廠模式 雖然使用物件字面量來建立一個物件十分地便捷,但是這個方法有一個顯著的缺點 那就是如果我們需要為一個介面建立多個物件,那麼將會產生大量的重複程式碼 所以工廠模式氤氳而生

Javascript高階程式設計學習筆記(三)—— JS中的資料型別(1)

 前一段時間由於事情比較多,所以筆記耽擱了一段時間,從這一篇開始我會盡快寫完這個系列。 文章中有什麼不足之處,還望各位大佬指出。 JS中的資料型別 上一篇中我寫了有關JS引入的Script標籤相關的東西。 那麼這一篇,我們可以正式進入JS的世界了,emmm 前面的東西應該比較基礎,大佬們不

JavaScript高階程式設計學習筆記之事件

1、事件流 事件流描述的是從頁面中接收事件的順序。 事件冒泡 IE的事件流叫做事件冒泡(event bubbling),即事件開始時由最具體的元素(文件中巢狀層次最深的那個節點)接收,然後逐級向上傳播到較為不具體的節點(文件)。(DOM樹向上傳播)(通俗解釋(個人理解: 當一個元素上的事件被觸發的時候,比如

Javascript高階程式設計學習筆記(6)—— 流程控制語句

話不多說,我們直接開始進入今天的主題 流程控制語句 首先什麼是流程控制語句呢? 顧名思義,就是控制流程的語句。 在JS中語句定義了ECMAScript中的主要語法,讓我們可以使用一系列的關鍵字來完成指定任務。 語句也是構成一門程式語言旳基礎,所以還是有必要記錄一下的。 雖然有些語句不常用,但存在即

JavaScript高階程式設計學習筆記——建立物件的幾種方式

建立物件的幾種方式 工廠模式 function createPerson(name, age, job){ var o = new Object(); o.name = name; o.age = age; o.job =

javascript高階程式設計學習筆記(第四章)

-基本型別和引用型別的值 ECMAScript變數可能包含兩種資料型別的值:基本型別值指簡單的資料段,引用型別值指可能由多個值構成的物件。對於5種基本型別值,可以直接操作儲存在變數中的值;而javascript不允許直接訪問物件的記憶體空間,只能通過操作物件的

Javascript高階程式設計學習筆記(7)—— 函式

前幾天有事耽擱了,今天繼續更新 今天的主要內容是JS中的函式 這一篇主要講函式的定義等內容,至於變數提升、執行環境、閉包、記憶體回收等內容在後面講,高玩們可以不用看下面的正文了。   函式 首先來講,函式對於任何程式語言都是一個十分核心的概念。 Js中的函式通過function關鍵字來宣

Javascript高階程式設計學習筆記(2)

script標籤 js在瀏覽器中的使用,肯定會涉及到script標籤。 那麼script標籤有哪些屬性呢? 1.async:非同步載入(不讓頁面等待該指令碼的載入執行,非同步載入頁面的其他部分) 2.charset:設定js的字符集編碼 3.defer:延遲指令碼(當瀏覽器解析到/html 結束標籤時才執行

Javascript高階程式設計學習筆記(10)—— 作用域、作用域鏈

昨天介紹了,JS中函式的作用域 什麼詞法環境之類的,可能很多小夥伴不太明白。 在今天的內容開始之前,先做個簡短的宣告:   詞法環境這一概念是在ES5中提出的,因為詞法環境主要用於儲存let、const宣告的變數、函式 而在ES3中對變數相關的資訊都儲存在變數物件上; 從功能上來說變數物

Javascript高階程式設計學習筆記(12)—— 引用型別(1)Object型別

前面的文章中我們知道JS中的值分為兩種型別 基礎型別的值和引用型別的值 基礎型別的值我已經大概介紹了一下,今天開始後面幾天我會為大家介紹一下引用型別的值   Object型別 物件是引用型別的值的例項,在ECMA中引用型別是一種資料結構 用於將資料和功能組織到一起,在其它程式語言中通常也

JavaScript高階程式設計學習8_BOM

1、視窗位置 IE、Safari、Opera和Chrome都提供了screenLeft和screenTop屬性,Firefox則提供了screenX和screenY屬性,Safari和Chrome同時也支援這兩個屬性,使用下列程式碼可ku跨瀏覽器取瀏覽器視窗的位置: le

Oracle Sql 高階程式設計學習過程記錄

sqlplus工具使用:     SQL>help index  @             COPY 

JavaScript高階程式設計學習10_DOM

1、node型別 DOM1級定義了一個Node介面,javascript中得所有節點都繼承Node型別,因此所有節點都共享著相同的屬性和方法。每個節點都有一個nodeType屬性,用於表明節點的型別。它由下列12個數值常量表示,任何節點型別必居其一: Node.ELEME

Javascript高階程式設計學習筆記(15)—— 引用型別(4)RegExp型別

JS中處理字串最常用的應該就是正則了 同樣正則(RegExp)型別也是JS中引用型別的一種 ECMAScript通過 RegExp型別 來支援正則表示式   建立正則 var expression = / pattern / flags; 在上面建立正則例項中 pattern

Javascript高階程式設計學習筆記(16)—— 引用型別(5) Function型別

JS中許多有趣的地方都和函式脫不了聯絡 那麼是什麼讓JS中的函式這麼有趣呢?  我們一起來看看吧   Function型別 在JS中函式實際上就是物件,每個函式都是Function型別的例項,和JS的其他引用型別都擁有屬性和方法 正是由於這個原因,函式名實際上就是一個指標,指向

Javascript高階程式設計學習筆記(17)—— 引用型別(6)基本包裝類

基本包裝類 基本包裝類這個概念或許有的小夥伴沒有聽說過 但是小夥伴們有沒有想過,為什麼基本資料型別的例項也有方法呢? 其實這些方法都來自基本包裝型別 這是JS為了方便操作基礎資料型別而建立的特殊引用型別   基本包裝類有三種 1. Boolean型別 2. Number型別 3.