js之對象(經典)

來源:互聯網
上載者:User

標籤:

一、對象的定義:

  對象是JavaScript的一個基礎資料型別 (Elementary Data Type),是一種複合值,它將很多值(原始值或者其他對象)彙總在一起,可通過名字訪問這些值。即屬性的無序集合。

二、對象的建立(多種方法)

  1、對象直接量 / 字面量

  

       var obj = {           name: ‘lyl‘,           age: 18       }       console.log(obj.name); // lyl

  2、建構函式:

    (1)、系統內建的的, eg: new Object(), Array(), Number(),Boolean(), Date()...

    

 var obj = new Object();      obj.name = ‘lyl‘;      console.log(obj.name); //lyl

     (2)、自訂的:為了和普通函數區分,首字母大寫,採用大駝峰式寫法(普通函數採用小駝峰式寫法)

       function Obj (name) {          this.name = name;          this.age = 18;      }      var obj = new Obj(‘lyl‘);      console.log(obj.name); //lyl      console.log(obj.age); //18

    自訂建構函式的基本構造原理:

      首先,文字理論解釋一番,其實一切的關鍵全在與new這個操作符上,用new和不用new返回的結果大相徑庭。不用new,則Obj(‘lyl‘)根本就是一個函數的正常執行,沒有傳回值,則預設返回undefined,而是用new操作符後js引擎就會將該函數看作建構函式看待,經過內部的一系列隱士操作,傳回值就是一個對象了。

      看下如下demo:

  demo1:用new和不用new的區別示範:

function Obj () {          this.age = 18;      }    //   不用new      console.log(Obj()); // undefined    //  用new      console.log(new Obj()); //Obj {age: 18}

  demo2 用new傳回值為對象的基本原理:

  不用new,函數內的this指向的是window,所以this.xxx定義的變數都是window上的屬性,但為什麼使用new後其中的this就不是window對象了呢?那是因為

  用new後,js引擎會在函數被進行兩步隱士操作(假設建構函式名為Person):第一步, var this = Object.create(Peson.prototype);   (也是建立對象的一種方法,下邊會講到)  隱士的改變函數內this的含義,現在函數內的this是一個原型為Person.prototype, 建構函式為Person的對象(其實此過程就將想要的對象基本創造成功了,只是差些屬性而已,從此可是看出建構函式建立對象的最根本原理是借用Object.create()方法來實現的,只不過被封裝功能化了); 第二步, 在建立的對象設定完所需要的屬性後,隱士的將建立的對象this通過return返回  return this; 

  

  通過代碼的展現:

 //  建構函式的原型     Person.prototype = {       say: function () {         console.log(‘I am saying‘);       }     }    //  建構函式     function Person () {      //  隱士操作      // var this = Object.create(Person.prototype);            //返回對象屬性的設定        this.name = ‘lyl‘;        this.age = 18;      //  隱士操作        // return this;     }     var person1 = new Person();     console.log(person1.name); // lyl     person1.say(); //I am saying

 

  上述兩步理論的驗證:

  第一步:現在函數內的this是一個原型為Person.prototype, 建構函式為Person的對象

 //  建構函式的原型     Person.prototype = {       say: function () {         console.log(‘I am saying‘);       }     }    //  建構函式     function Person () {        this.name = ‘lyl‘;        this.age = 18;        // 列印this對象的原型        console.log(this.__proto__); //         // 驗證this是否是Person建構函式的執行個體        console.log(this instanceof Person); //true     }     new Person();//列印結果如下    //  Object say: ()__proto__: Object    // true     Person();//列印結果如下    //  Window    // false

 

   第二步:隱士的將建立的對象this通過return返回

由以上一些代碼,我們已經可以看出返回的是滿足條件的對象,現在我們建立對象時不用new,並顯示的類比這兩步隱士操作來驗證(我們不用new則兩步隱士操作就不會產生)

 //  建構函式的原型     Person.prototype = {       say: function () {         console.log(‘I am saying‘);       }     }    //  建構函式     function Person () {       var that = Object.create(Person.prototype);        that.name = ‘lyl‘;        that.age = 18;                return that;    //提前返回that導致return this無法執行而失效     }     var person = new Person();
//此處不用new也是可以成功返回一個滿足條件的對象,因為顯示的返回了that console.log(person.name); //lyl person.say(); //I am saying

p.s. 關於顯示返回that的問題,當我們用new產生對象,若我們顯示return的是一個對象 / 引用值,則會導致return this失效,若返回的是原始值,則return this不會失效

 

  3、Object.create(原型); 建立一個繼承該原型的執行個體對象

  關於此方法的一些注意事項:

  (1)、若傳參為Object.prototype,則建立的原型為Object.prototype,和 new Object()建立的對象是一樣的   Object.create(Object.prototype) <==>new Object(); 

  (2)、若傳參為空白 或者 null,則建立的對象是沒有原型的, 導致該對象是無法用document.write()列印會報錯,因為document.write()列印的原理是調用Object.prototype.toString()方法,該對象沒有原型,也就沒有該方法,所以document.write()無法列印

  由此延伸的知識點: 引用值都也是算作是對象,所以都可以用document.write()列印;原始值numebr, boolean, string都有自己對象的封裝類,藉助此機制也是可以用document.write()列印出的;

但undefined 和 null既不是引用值,也沒有對應的封裝類,所以應該無法列印的,但大家會發現這兩個值也是可是用document.write()列印的,因為這兩個值被設定為特殊值,document.write()列印其是不用調用任何方法的,而是之直接列印其值

 

三、對象的增、刪、改、查

  1、增: 所謂增添一個對象的屬性,就是直接對該屬性進行賦值操作即可,這就相當於為該對象添加了一個新屬性,而列印未添加的屬性,瀏覽器不會報錯,而是會列印出undefined

  

 var obj = {};    console.log(obj.name); //undefined (不會報錯)    obj.name = ‘lyl‘;    console.log(obj.name); // lyl

  

  2、刪:我們通過delete操作符來刪除一個對象的屬性

  

 var obj = {       name : ‘lyl‘     };     console.log(obj.name); //lyl     delete obj.name;      console.log(obj.name); //undefined

  3、改: 修改一個對象的屬性是最簡單的了,直接通過賦值操作賦予其其他的值即可

  

var obj = {      name: ‘lyl‘    };    console.log(obj.name); // lyl    obj.name = ‘obj‘;    console.log(obj.name); // obj

  

  4、查:查詢一個對象的屬性值有兩種方法

  

var obj = {      name: ‘lyl‘    };    // 第一種方法   console.log(obj[‘name‘]); //lyl  //  第二種方法    console.log(obj.name); // lyl  //p.s.最本質的是第一種方法,因為在使用第二種方法時,後台自動將其轉換為第一種字串的形式來查詢 

    

p.s.以上的增、刪、改三種操作都只是針對當前對象的屬性進行操作,而不會影響到當前對象的原型的屬性。

  而查詢是先看看當前對象本身是否設定了該屬性,如果當前對象未設定該屬性,則再看該對象的原型中是否設定了該屬性,若兩者都沒有,則返回undefined

 

四、封裝類:

  1、五個原始值:number, string , boolean, undefined, null

其中number, string, boolean是分別擁有自己的封裝類,而undefined和null是沒有自己的封裝類的

  2.原始值不是對象,無法擁有自己的屬性,但因為的封裝類的存在,原始值就好似可以擁有自己的屬性了,但其擁有的屬性又有點特殊之處,如下用string來舉例:

先看一段code

//  str是string類型的,非對象,不能擁有屬性,為什麼能列印出str.length?     var str = ‘abcd‘;     console.log(str.length); //4

 

 

上邊code中問題的解釋:

    // 因為每次執行完一條完整js語句後該類型對象的封裝類就會將該語句封裝,所以也就不會導致報錯了,這些都是後台自己寫的     var str = ‘abcd‘;    //  var str1 = new String(‘abcd‘);     console.log(str.length); //4    //  var str1 = new String(‘abcd‘);    // console.log(str1.length);

注意:每次封裝類封裝完一次完整語句後就會被銷毀。(即解釋一條語句,用封裝類封裝一次,然後銷毀),這回導致其和正常對象的一些不同之處,如下例子

  

    var str = ‘abcd‘;    str.len = 4;    console.log(str.len); // undefiend
  //???

 關鍵在於‘銷毀’一詞上,解釋如下:

  

    var str = ‘abcd‘;    // var str1 = new String(‘abcd‘);    // 銷毀    str.len = 4;    // var str1 = new String(‘abcd‘);    // str1.len = 4;    // 銷毀    console.log(str.len); // undefiend    // var str1 = new String(‘abcd‘);    // console.log(str.len);   str1為剛建立的對象,其len屬性自然為undefiend    // 銷毀

   一個易錯的坑:

// 總之記得‘原始值封裝類‘‘銷毀‘這兩句話就行   var str = ‘abcd‘;   str.lenth = 2;   console.log(str);  // ab or abcd ? answer is abcd   console.log(str.length); // 2 or 4 ? answer is 4

 

  

五、原型:

  1、原型的定義: 原型是function對象的一個屬性,它定義了建構函式製造出的對象的公用祖先。通過改建構函式產生的對象,可以繼承該原型的屬性和方法。原型也是對象。

  2、利用原型特點和概念,可以提取共有屬性。將一類對象的共有屬性提取出來,放到該類對象的原型中,從而不需要每次用new操作符時都重新定義一遍該共有屬性。

  如下,定義一個Person建構函式,而屬於Person多構造對象共有的屬性方法,則定義到Person的原型中

  

Person.prototype = {        eat: function (food) {           console.log(‘I have eated ‘ + food);        },        sleep: function () {          console.log("I am sleeping");        }      }      // 人的建構函式      function Person (name, age) {        this.name = name;        this.age = age;      }      var person1 = new Person(‘lyl‘, 18);      console.log(person1.name); //lyl      person1.eat(‘apple‘); //I have eated apple

  3、如何查看原型:

   之前是不允許我們查看建構函式的原型的,但後來提供了一個可查看建構函式原型的介面:隱士屬性__proto__(其實我們能夠訪問原型的屬性,或者說繼承原型,靠的就是__proto__屬性串連著建構函式和原型,可以說沒有__proto__屬性的存在,就無法實現原型的繼承)

    (1)、首先我們先說明一下__proto__這個介面是存放到哪裡的

      看過以上對象建立過程的都應該知道在用new建立一個對象時,內部會隱士自動建立一個this的對象,進過一系列處理後再隱士將this對象返回。而__proto__就對於隱士建立的this對象中,如下代碼:

  

 // 原型    Person.prototype = {      say: function () {        console.log("I am saying ");      },      play: function () {        console.log("I am playing");      }    }    // 建構函式    function Person (name) {            // var this = Object.create(Person.prototype);      // p.s.在隱士建立的this對象中存在一個屬性,即__proto__,該屬性儲存區了Person.prototype      this.name = name;      // return this;    }    // 對象的建立    var person1 = new Person(‘lyl‘);    // 列印原型    console.log(person1.__proto__);

 

    (2)、如何查看原型:直接通過new操作符建立的對象訪問__proto__屬性即可,如上代碼示範

  4、如何查看對象的建構函式,我們通過屬性constructor來查看:

    contructor屬性位於建構函式的原型中,其中儲存的是建構函式資訊,所以在不知道原型的情況下,由原型繼承原理,我們可以用執行個體對象來直接存取constructor,即擷取建立該執行個體的建構函式

 function Person () {           this.name = ‘myName‘;           this.age = 18;       }       var person = new Person();       console.log(person.constructor); // function Perso(){...}

六、原型鏈:

  1、定義:顧名思義,原型鏈就是將一個個原型串聯起來,形成一條原型繼承的鏈子。

  2、原型鏈的構成:

      如下代碼例子, Child繼承Parent, Parent繼承GrandParent, 而GrandParent沒有自訂原型,所以預設為原型鏈的最頂端new Object();

       (為什麼Object為最頂端,因為Object.prototype為null,為null是沒有原型的)

 //    原型鏈: Child -> new Parent() -> new GrandParent() -> new Object();       function GrandParent() {           this.name = ‘GrandParent‘;           this.a = 3;       }       Parent.prototype = new GrandParent();       function Parent() {           this.name = ‘parent‘;           this.b = 2;       }       Child.prototype = new Parent();       function Child() {           this.name = ‘child‘;           this.c = 1;       }       var child = new Child();       console.log(child); // Child {name: "child", c: 1}       console.log(child.a); // 3       console.log(child.b); //2       console.log(child.c); //1

  3、原型鏈的增刪改查:

    使用如上的原型鏈說明, Child -> new Parent() -> new GrandParent() -> new Object(), 執行個體對象為child

    (1)、增:

      為child執行個體對象添加屬性,總是添加為其自己本身的屬性,為對原型和原型鏈是沒有影響的。(再具體說即對和其相同建構函式構造的執行個體對象無法造成影響,以下說法同此)

    (2)、刪:

      使用delete操作符只能刪除child執行個體對象自己本身的屬性,而無法刪除由原型繼承而來的屬性

    (3)、改:

      分兩種情況:

        若修改的屬性為繼承自原型的,且實值型別為原始值,則僅僅修改的是該執行個體對象的屬性,對原型無法造成影響。

        若修改的屬性為繼承自原型的,屬性值類型為引用值,則對引用值的修改又分兩種情況:

            第一種是直接對該修改的屬性賦值 => 此情況僅僅修改的是執行個體對象的該屬性,無法對原型造成影響。

            第二種是對該修改的屬性新增內容或去除內容,而不是對其重新賦值 => 此情況會對原型造成影響。如下例子:

            

 Person.prototype = {            has: [1, 2, 3]        }        function Person () {            this.name = ‘lyl‘;        }        var person1 = new Person();        var person2 = new Person();        person1.has.push(4);        // person1 和 person2都改變了,因為person1的修改影響到了原型,進而影響到了另一個執行個體對象        console.log(person1.has); //[1, 2, 3, 4]        console.log(person2.has); // [1, 2, 3, 4]

  

 

    (4)、查:

  查詢過程如下,首先看建構函式中是否有要查詢的屬性,若有,則直接返回,若沒有,則看其原型有沒有要查詢的屬性,若沒有,則再看原型的原型上是否有要查詢的屬性,以此順序在原型鏈上查詢,若一直到原型鏈頂端後仍沒有要查詢的屬性,則返回undefined

  p.s.理解了在原型鏈上的增刪改查後,自然就能理解在原型上的增刪改查了,只要把在原型上的增刪改查當成只有一個原型的很短的原型鏈即可。

 

   4、絕大多數對象最終都會繼承自Object.prototype

    為什麼事絕大多數呢?因為null,和undefined是沒有原型的,上文有詳細提到

 

七、對象繼承史

  由於之前已經總結過這些,所以此處直接就粘連結了: http://www.cnblogs.com/Walker-lyl/p/5592048.html

八、命名空間:

  我們可以利用對象建立命名空間來管理變數,防止汙染全域,適用於模組開發,如下簡單的小demo:

 var workSpace = {           person1: {               name: ‘one‘,               age: 18           },           person2: {               name: ‘two‘,               age: 20           }       }    // 這樣兩個人雖然有同名變數,但不會相互影響,因為位於不同命名空間     //    訪問第一個人的姓名    console.log(workSpace.person1.name); // one    console.log(workSpace.person2.name); //two

 

九、實作類別似jquery中的鏈式調用: return this;

    如下小demo,其中的this不懂的沒關係,上面會說,你只要把次demo中的this當成person對象就行

  

 var person = {          foodCount: 10,          eat: function () {             this.foodCount--;              return this;          },          buy: function () {              this.foodCount++;              return this;          },          print: function () {              console.log(this.foodCount);          }      }    //   foodCount初始值為10, 在連續吃了三次後,變為7      person.eat().eat().eat();      person.print(); //7

 

 

十、對象的枚舉:

  1.obj.hasOwnProperty(‘prop‘);

      該方法的作用是來判斷對象obj的自身屬性中是否含有屬性prop,

    自身屬性是在建構函式中產生的或者執行個體對象後來自己添加的,而繼承屬性則是從原型上繼承的屬性,

    所以該方法就是判斷該屬性是從原型繼承來的還是自身的。

      作用: 遍曆一個對象的所有自身屬性,因為es5中的對象遍曆是預設列印包括繼承自原型的屬性的,demo如下

  

 Person.prototype.age = 18;     function Person () {         this.name = ‘lyl‘;     }     var person = new Person();    //  未用hasOwnProperty    // print:    //  lyl     for(var prop in person) {         console.log(person[prop]);     }     //  使用hasOwnProperty    // print:     // 18 lyl     for(var prop in person) {         if(person.hasOwnProperty(prop)) {             console.log(person[prop]);         }     }

 

   2、prop in obj; 

  in操作符用來判斷該對象obj上是否有該屬性prop,prop既可以是自身屬性,也可以是繼承屬性,如下demo

Person.prototype.age = 18;     function Person () {         this.name = ‘lyl‘;     }     var person = new Person();    console.log(‘age‘ in person); // true    console.log(‘name‘ in person); //true    delete person.name;    console.log(‘name‘ in person); //false

  3、object instanceof Object;

  instanceof操作符用來判斷object執行個體對象是否為Object建構函式建立的,如下demo

 Person.prototype.age = 18;     function Person () {         this.name = ‘lyl‘;     }     var person = new Person();     console.log(person instanceof Person); // true     console.log(new Object() instanceof Person); //false        

 

 

十一、this基本介紹:

  1、函數先行編譯過程 this —> window
  2、全域範圍裡 this —> window

  3、obj.func();   func()裡面的this指向obj), 可以這樣理解,誰調用func,則this就指向誰

  4、call/apply 可以改變函數運行時this指向,

    (1)、call用法:

      func.call(要改變後的this, arg1, arg2, ... );

    (2)、apply用法:

      func.apply(要改變後的this, [arg1, arg2, arg2]);

    (3)、apply和call共同點:都是改變this指向

        apply和call不同點:傳參形式不同,call是將參數一個個傳進來,而apply是將所有參數存進一個數組中,然後將該數組傳

 

如下demo:

  

// demo1    function demo1() {        console.log(this);    }    // demo1() <==> this.demo1(); <==> window.demo1()    demo1(); // window// demo2    var demo2 = {        retThis: function () {            console.log(this);        }    }    demo2.retThis(); // demo2 = {...}// call / apply改變this    demo1.call(demo2);  // demo2 = {}    demo2.retThis.call(window); // window

 

  

  

十二、對象的複製:

  你可能回想,直接用等號賦值不久完成複製了嗎?這還用說,但你忽略了對象是一個引用值,賦值操作賦的是該對象的引用,然後會產生很壞的影響。

  舉例來說,將obj1賦值給obj2,然後我們向obj2中添加了一個屬性,然後我們會驚喜的發現obj1中也有了此剛剛向obj2中添加的屬性,但我obj1並不需要該屬性啊,則造成obj1不開心了,為了讓obj1開心起來,我們向下看,嘿嘿:

  對象複製,分為淺複製和深複製,而上邊的直接賦值的複製操作為淺複製,為什麼稱為淺複製呢?因為,複製的和被複製的對象在複製操作完成後,指向同一個地址引用,改變其中一個(注意:此處的改變為增加或刪除對象的屬性,而不是為該對象重新賦值一個對象),另一個也會改變,而深複製則不會產生此現象。

  深複製/深拷貝code如下,這是我從網上搜到的一個目前我見到的最完整最簡短的code,直接貼上:

// 對象的深度複製(Array / obj /...)        function  deepClone(obj) {            var str, retObj = Object.prototype.toString.call(obj) === ‘[object Array]‘ ? [] : {};            if(typeof obj !== ‘object‘) {                return;            }else if(window.JSON) {                str = JSON.stringify(obj);                retObj = JSON.parse(str);            }            else {                for(var prop in obj) {                    retObj[prop] = typeof obj[prop] === ‘object‘ ? deepClone(obj[prop]) : obj[prop];                }            }            return retObj;        }

 

 

 ------------------------------------------end 至此基本包含了對象的i基本知識點

js之對象(經典)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.