淺解析js中的對象


淺解析js中的對象

原文網址:http://www.cnblogs.com/foodoir/p/5971686.html,轉載請注明出處。

前面的話:

    說到對象,我首先想到的是每到過年過節見長輩的時候長輩們老是開玩笑的問我“你找了對象沒?”。不說大家都知道,這里的“對象”指的是“女朋友”,但是今天我想要說的js中的“對象”和我們生活中談到的“對象”不是同一回事,但是其中也有着很多相似之處。

    在講js中的對象之前,我想先拋出幾個疑問:

    什么是對象?
    對象有哪些?
    對象能做什么?
    如何創建對象?
    如何對對象進行操作?
    對象有特性么?有的話有哪些特性?
    對象有屬性么?有的話有哪些?對屬性如何操作?
    ……  

什么是javascript中的對象?

    在ECMA-262中把對象定義為:“無序屬性的集合,其屬性可以包含基本值、對象、或者函數。”嚴格來說,這就是相當於說對象是一組沒有特定序列的值。對象的每一個屬性或方法都有一個名字,而每一個名字都映射到一個值。(如:“女朋友”[對象]喜歡運動[對象的屬性或方法]--打籃球[屬性對應的值],我門可以試着這樣去理解,但實際可能有所區別)
    在經典的面向對象語言中,對象是指數據和在這些數據上進行的操作的集合。與 C++ 和 Java 不同,JavaScript 是一種基於原型的編程語言,並沒有 class 語句,而是把函數用作類。
    我理解的javascript對象:

    第一:Javascript對象是基本數據類型之一,是復合類型;

    第二:Javascript中幾乎所有事物都是做對象

    第三:Javascript的對象是擁有屬性和方法數據

    第四:JavaScript 中的對象可以簡單理解成"名稱:值"對(name:value)。名稱(name):"名稱"部分是一個 JavaScript 字符串

        在javascript語句中這三種形式是一樣的

            var obj={prop:1}
            var obj={"prop":1}
            var obj={'prop':1}

        在這里看起來屬性名既可以加引號也可以不加引號,但是在實際中有這么幾種情況你必須將屬性名放到引號中間
            如果屬性名是Javascript的保留字之一
            如果屬性名種包含特殊字符(除字母、數字、下划線以外的字符)
            如果屬性名以數字開頭
            在ECMAScript5中,保留字可以用作不帶引號的屬性名,但對於ECMAScript3中必須用引號括起來
            在ECMAScript5中對象直接量中的最后一個屬性后的逗號將被忽略,在ECMAScript 3的大部分實現中也可以忽略這個逗號,但在IE中報錯
    值(value):"值"部分可以是任何 JavaScript 的數據類型——包括對象     

對象有哪些呢?

    學過java的都知道,在java中有一句很經典的描述對象的話“萬物皆對象”,是的,在javascript中也是一樣。在javascript中我們可以把對象分為兩大類,一類是內建對象(數據封裝對象,工具類對象,錯誤對象,后面會有專門針對內建對象的博客),一類是自定義對象(這篇文章里面講的主要就是自定義對象)

對象能做什么?

    在這里我要先賣個關子,到后面的時候告訴大家。

如何創建對象?

    既然了解了對象和基本的對象分類,我們是否應該要知道如何去創建一個自定義的對象呢?

    1、通過對象字面量的方式創建對象(對象字面量是一個表達式,這個表達式的每次運算都創建並初始化一個新對象。每次計算對象字面量的時候,也都會計算他的每個屬性的值。也就是說,如果在一個重復調用的函數中的循環體內使用了對象直接量,它將創建很多新對象,並且每次創建的對象的屬性值也有可能不同。)

        語法:var obj = {};
        實例:
        首先,我們創建一個空的對象,里面沒有任何屬性,我們來獲取它的類型   

var obj1 = {};//沒有任何屬性的對象
console.log(typeof obj);        //object

 

        它返回的結果是object,說明我們創建的是一個object對象,當然,我們還可以用下面的方法來創建

                var obj2={x:1,y:2,z:3};
                var obj3={
                'x':1,
                "y":2,
                username:'king',
                'for':'javascript關鍵字必須放到引號中間',    //for是javascript關鍵字,必須放到引號中間
                'first-name':'foodoir',                //-是特殊字符,也需要放在引號中間
                married:true,
                test:null,
                test1:undefined,
                salary:12.3,
                person:{                            //在對象中"值"部分可以是任何 JavaScript的數據類型——包括對象
                    username:'king',
                    age:21,
                    addr:'北京',
                },                                    //最后一個對象屬性的后面的逗號可以寫可以不寫,在ECMAScript 5中自動被忽略,在ECMAScript 3的大部分實現中也可以忽略這個逗號,但在IE中報錯
            }

    2、通過new object創建對象
        語法:var obj = new Object();
        實例:

            var obj4 = new Object();//創建一個空對象
            var arr = new Array();//創建一個空數組對象
            var date = new Date();//創建一個空時間對象   

    3、通過構造函數的形式創建對象
        語法:function Person(){};或者var Person=function(){};
        實例:

            var obj5=new Test1();
            function Test1(num1,num2){
                this.n1=num1;
                this.n2=num2;
            }
            var obj6=new Test1(5,6);
            console.log(typeof obj6);                //object
            console.log(obj6 instanceof Object);    //true

        在使用通過構造函數的形式創建對象時應注意:

            a.使用的時候通過new操作符得到對象var person1=new Person()
            b.用構造器創建對象的時候可以接收參數
            c.構造器函數的首字母最好大寫,區別其他的一般函數

        注意:構造器屬性(constructor property),當我們創建對象的時候,實際上同時也賦予了該對象一種特殊的屬性,就是構造器屬性,這個構造器屬性實際上是一個指向用於創建該對象的構造器函數的引用

補充:
typeof 和 instanceof 常用來判斷一個變量是否為空,或者是什么類型的。但它們之間還是有區別的:
    typeof 是一個一元運算,放在一個運算數之前,運算數可以是任意類型。
        它返回值是一個字符串,該字符串說明運算數的類型。typeof 一般只能返回這幾個結果:number,boolean,string,function,object,undefined。
        我們可以使用 typeof 來獲取一個變量是否存在,如 if(typeof a!="undefined"){alert("ok")},而不要去使用 if(a) 因為如果 a 不存在(未聲明)則會出錯,對於 Array,Null 等特殊對象使用 typeof 一律返回 object,這正是 typeof 的局限性。
    instanceof 用於判斷一個變量是否某個對象的實例
        在上面的例子中,obj6 instanceof Object判斷的為true,則說明創建的obj6也是Object類型            
        
    4、通過Object.create()創建對象   

        var obj7 = Object.create({x:1});
        //創建一個普通的空對象
        var obj8 = Object.create(null);
        //創建一個對象的原型屬性
        var obj9 = Object.create(Object.prototype);    //prototype對象的原型屬性
        console.log(typeof obj7);                //object
        console.log(typeof obj8);                //object
        console.log(typeof obj9);                //object
        //通過instanceof 操作符檢測對象是否由某個指定的構造器函數創建的
        console.log(obj7 instanceof Object);    //true
        console.log(obj8 instanceof Object);    //false,注意:空對象用instanceof判斷時,結果為false
        console.log(obj9 instanceof Object);    //true

如何對對象進行操作?

    學過數據庫的我們都知道,數據庫最基本的四個操作分別是“增、刪、查、改”,那么在對對象的操作中是否也存在類似的操做呢?是的,在對對象的操作中也有查詢,添加,修改,刪除操作,都是基於對象的屬性的操作。下面我們來介紹對象的幾個基本操作。

    首先,我們先要通過查詢方法,來獲取對象中的屬性,訪問屬性有三種方法:對象名.屬性名;對象名[屬性名];當處於某個對象方法內部的時候,可以通過this來訪問同一對象的屬性

var person = {
    username: "foodoir",
    age: 21,
    sex: "",
    addr: "湖南",
    salary: 123456,
};
console.log("姓名:" + person.username + "\n" + "性別:" + person.sex); //姓名:foodoir    性別:男
console.log("年齡:" + person['age'] + "\n" + "薪水:" + person["salary"]); //年齡:21    薪水:123456
//如果屬性不確定,需要使用[]
var key = 'username';
console.log(person.key); //undefined   此種方法不能得到key的值,通過[]方法可以得到
console.log(person[key]); //foodoir     當屬性不確定時,用該方法
console.log(person['key']); //undefined

            看到這里,你肯定會有有疑問,為什么第9行代碼中person['age']中的age和person["salary"]中的salary都加了引號,且都可以顯示出結果,而在第13行代碼中person['key']中的key加了引號,反而返回值是undefined 原因究竟是什么呢?

            age為person對象中的屬性名,而該對象只對person進行了定義,沒對person里面的屬性進行定義,故在調用的時候,在[]中間應該加上引號,也就是說,person['age']和person.age是等價的

            而key是在對象外定義的(var key = 'username';),在對象里面它是不存在key這個屬性的(即屬性不確定),此時直接用person[key]方法就好了,person.key和person['key']返回的必須是person對象里面確定的屬性,key沒在person屬性里面,故這兩種方法返回的值都是undefined

   學會了如何獲取對象的屬性,然后我們該繼續學習對象中的屬性進行添加,修改和查詢操作

function personInfo(name, age, sex) {
    this.name = name;
    this.age = age;
    this.sex = sex;
}
var person1 = new personInfo('foodoir', 20, '');
console.log(person1.name + person1.sex); //foodoir男

    屬性的添加有兩種方式:對象名.屬性名=值;對象名[屬性名]=值       

//添加屬性
var obj = {};
obj.username = 'foodoir';
obj.age = 21;
obj.sex = '';
obj.addr = '湖南';
obj['test'] = 'hello world';
console.log(obj.username + " " + obj.age + " " + obj.sex + " " + obj.addr + " " + obj['test']); //foodoir 21 男 湖南 hello world

    屬性的修改也有兩種方式:對象名.屬性名=值;對象名[屬性名]=值       

//修改指定屬性
obj.username = 'chenwu';
console.log("修改之后的名字:" + obj.username); //修改之后的名字:chenwu
obj['test'] = 'hello javascript';
console.log("修改之后的test:" + obj['test']); //修改之后的test:hello javascript

    屬性的刪除也有兩種方式:delete 對象名.屬性名;delete 對象名[屬性名]    。在delete刪除指定的屬性時應該注意:(后面講對象的結構的時候會詳細介紹)
        delete只能刪除自身屬性,不能刪除繼承屬性
        要刪除繼承屬性,只能從定義它屬性的原型對象上刪除它,而且這會影響到所有繼承自這個原型的對象
        delete只是斷開屬性和宿主對象的聯系,而不會去操作屬性的屬性
        delete不能刪除哪些可配制性為false的屬性  

//通過delete刪除指定的屬性
delete obj.sex;
console.log(obj.sex); //undefined
delete obj['test'];
console.log(obj['test']); //undefined

         除了“增、刪、改、查”我們還要學會對象中的遍歷,對象中的遍歷有兩種,一種是for/in遍歷一種是通過Object.keys(obj)函數進行遍歷

var obj = {
    x: 1,
    y: 2,
    test: "helloworld",
    edu: "javascript",
};
//通過for/in遍歷屬性
for(var i in obj) {
    console.log(i); //x y test edu(換行輸出)
    console.log(obj[i]); //輸出的是屬性名對應的屬性值
}
//通過Object.keys(obj)遍歷屬性
console.log(Object.keys(obj)); //["x", "y", "test", "edu"]

    雖然兩種方法都可以對對象的屬性進行遍歷,但是二者之間還是有區別的。Object.keys() 方法會返回一個由給定對象的所有可枚舉自身屬性的屬性名組成的數組,數組中屬性名的排列順序和使用for-in循環遍歷該對象時返回的順序一致(兩者的主要區別是 for-in 還會遍歷出一個對象從其原型鏈上繼承到的可枚舉屬性)。

    學了前面的覺得難度不是很大,我們試着做一個對象中有方法的例子

var obj = {
    username: 'foodoir',
    age: 21,
    sex: '',
    sayHi: function() {
        return 'say hi';
    },
    info: function() {
        return obj.username + obj['sex'];
    }
}
console.log(obj.sayHi()); //say hi
console.log(obj.info); //在info的后面要加(),不然結果是:function(){ return obj.username+obj['sex']; }
console.log(obj.info()); //foodoir男

    注意:在方法的后面要加(),不然,返回的將是function(){ return obj.username+obj['sex']; }

function Person(username, age, sex) {
    this.username = username;
    this.age = age;
    this.sex = sex;
    this.info = function() {
        return this.name + this.age;
    }
}
var person1 = new Person('foodoir', 21, '');
console.log(person1.info()); //NaN    String+Number兩種類型相加的結果為NaN        
console.log(Person.person1); //undefined person1不是Person里面的屬性,故返回undefined
console.log(person1.username); //foodoir    
//向對象中添加新屬性        
person1.test = 'this is a test'; //this is a test
console.log(person1.test);
//向對象中添加新的方法
person1.fun = function() {
    return 'hello world';
}
console.log(person1.fun()); //hello world

 

到此刻,你可能有新的疑問,究竟對象的結構是怎樣?下面圖片是我自己對於對象的理解畫的一幅圖

  首先從局部來看我在途中舉的兩個例子,我們通過面向字面量的方法var obj = {x:1,y:2};可以創建出我們想要的對象(如圖:最大的橢圓),在obj對象中x,y分別是obj的屬性,它們對應的屬性值分別是1和2,當然除了屬性對應的屬性值之外,屬性還有右側的幾個屬性特性(writable:是否可寫、 enumerable:是否可枚舉、 configurable:是否可配置、 getter:獲取屬性的值 、setter:設置屬性的值),這些屬性的特性是可以設置和更改的(后面會介紹),只要是對象中的屬性,都有這些特性,但是這些特性的值要柑橘具體情況來分析。屬性有自己的特性,我們的obj是否也有自己的特性呢?從圖中我們可以知道,obj對象也有自己的特性([[proto]]對象原型、 [[class]]對象的類、 [[extensible]]對象是否可擴展),這些特性我們和面都會介紹。然后,我們通過構造函數的方法創建對象function foo(){}; foo.prototype.z = 3; var obj = foo();可以創建出我們想要的對象(如圖:第二大的橢圓),它依然具有我們之前說的那些特性,但是具體特性要根據具體情況來分析,屬性的特性也是一樣。

  接下來為了理解的方便,我們只看四個橢圓,每上一個橢圓代表着下一個橢圓中的對象的原型,比如說foo.prototype是obj對象的原型,foo.prototype的原型是object.prototype(值得注意的是object.prototype是javascript中所有對象的父級對象,我們創建的所有對象都繼承於此),object.prototype往上,它的原型對象是null。這些話說起來有點拗口,我們還是通過具體的例子來了解它。

  首先我們來創建一個對象foo,並且設置它在原型中的值為3,設置完之后,再在obj中添加兩個屬性x,y

            function foo(){};
            foo.prototype.z = 3;
            var obj = new foo();
            obj.x = 1;
            obj.y = 2;

    此時我們來獲取對象中屬性對應的屬性值

            console.log(obj.x);        //1
            console.log(obj.y);        //2
            console.log(obj.z);        //3

    本來obj上是沒有z(屬性)的,但是它可以返回它的原型上去尋找z,從而得到z屬性的屬性值為3,下面我們用toString()方法來得到對象的返回值             

console.log(obj.toString());    //[object Object]

    返回值是Object,說明是Object對象,下面我們在obj上新增屬性z=12

            obj.z = 12;
            console.log(obj.z);        //12

    此時得到的值是12不是3,因為在obj中有了該屬性,不用到原型上去尋找,我們再刪除obj上的屬性z

            delete obj.z;
            console.log(obj.z);        //3

    此時得到的值是3不是12,因為在obj中原有的該屬性被刪除了,繼續到原型上去尋找z,再刪除掉原型上的z

            delete foo.prototype.z;
            console.log(obj.z);        //undefined 

原型上的z被刪除了,在原型上都找不到故返回undefined,下面我們直接看代碼,在代碼里面作了注釋,

            //創建新的對象檢測對象的屬性
            function foo1(){};
            foo1.prototype.z = 3;
            var obj1 = new foo1();
            obj1.x = 1;
            obj1.y = 2;
            //通過in檢測對象是否有某個屬性(可以檢測繼承下來的屬性)
            console.log('x' in obj1);                //true
            console.log('y' in obj1);                //true
            console.log('z' in obj1);                //true,對象上沒有該屬性,就繼續往上在原型上找
            console.log('toString' in obj1);         //true,對象上沒有該屬性,就繼續往上在原型上找
            console.log('notExists' in obj1);        //false,對象上沒有該屬性,就繼續往上在原型上找,原型上也不存在該屬性
            //通過hasOwnProperty檢測對象是否有某個屬性,只檢測對象自身的屬性
            console.log(obj1.hasOwnProperty('x'));    //true
            console.log(obj1.hasOwnProperty('y'));    //true
            console.log(obj1.hasOwnProperty('z'));    //false
            console.log(obj1.hasOwnProperty('toString'));    //false            
            //刪除掉原型上的z
            delete foo1.prototype.z;
            console.log('z' in obj1);                 //false,對象上沒有該屬性,就繼續往上在原型上找,原型上的也被刪除了
            console.log(obj1.hasOwnProperty('z'));    //false
            //通過create創建新的對象
            var obj2 = Object.create({x:1});
            obj2.y = 2;
            console.log('x' in obj2);                //true
            console.log(obj2.hasOwnProperty('x'));   //false,該屬性x是繼承下來的,不是對象自己的屬性
            console.log('y' in obj2);                //true
            console.log(obj2.hasOwnProperty('y'));   //true,該屬性x是后來添加的,是對象自己的屬性

 現在,我們對於對象的了解又多了一點點,下面我們探究我們之前提到的對象的特性和屬性的特性。先說說屬性的特性吧。

   屬性包括數據屬性和存取器屬性
   數據屬性包含一個數據值的位置,在這個位置可以讀取和寫入值,總共有4個描述行為的特性

             [[writable]]表示能否修改屬性的值。默認值為true
        [[Enumerable]]表示能否通過for in循環返回屬性。代表屬性是否可以枚舉。直接在對象上定義的屬性默認值為true
        [[configurable]]表示是否能通過delete刪除屬性從而重新定義屬性,能否修改屬性的特性,或者能否把屬性修改為訪問器屬性。直接在對象上定義的屬性,他們的默認值為true
        [[value]]包含這個屬性的數據值。讀取屬性值的時候,從這個位置讀取。寫入屬性值的時候,把新值保存在這個位置。這個特性的默認值為undefined

    存取器屬性包括get獲取屬性的值;set設置屬性的值。

來看下面的例子:

var obj = {}
obj.x = 1;
//和下面的是一樣的
Object.defineProperty(obj,'x',{
    value:1,
    writable:true,//代表是否可寫
    enumerable:true,//是否可枚舉
    configurable:true//是否可配置
});

defineProperty定義的對象,其屬性對象是可以設置的,這里我們又多了一種創建對象的方法。

但是下面的例子也應該注意,在使用defineProperty當特性值省略時,默認為false

Object.defineProperty(obj,'x',{value:1});
//和下面的是一樣的
Object.defineProperty(obj,'x',{
    value:1,
    writable:false,
    enumerable:false,
    configurable:false
});

補充:下面這種情況是沒有繼承而來的屬性

    var obj1={
        __proto__:null,//沒有繼承而來的屬性
        value:1
    };

關於其中configurable和writable的值發生變化時,會產生不一樣的情況,下面是我總結的一個表(僅作參考,具體是實踐的結果為准)

 

configurable:true

writable:true

configurable:true

writable:false

configurable:false

writable:true

configurable:false

writable:false

修改屬性的值

Y Y*(重設value標簽修改) Y N

通過屬性賦值修改屬性賦值

Y N Y N

delete,該屬性返回true

Y Y N N

修改getter/setter方法

Y Y N N

修改屬性標簽

Y Y N N

下面我們看看圖中標記*號的具體例子,相信大家看了這個例子很快就能明白。

Object.defineProperty(obj1, 'x', {
    value: 1,
    writable: false,
    enumerable: false,
    configurable: true
});
obj1.x = 2;
console.log(obj1.x);//1
Object.defineProperty(obj1, 'x', {
    value: 3,
    writable: true
});
console.log(obj1.x);//3
obj1.x = 5;
console.log(obj1.x);//5
//如果屬性不可配置,但是可以把writable的true變成false,但不能將false變為true

Object.defineProperty(obj1, 'y', {
    value: 1,
    writable: true,
    enumerable: false,
    configurable: false
});
obj1.y = 6;
console.log(obj1.y);//6
Object.defineProperty(obj1, 'y', {
    writable: false
});
obj1.y = 10;
console.log(obj1.y);//6

關於set和get:

直接看代碼:

var person = {
    username: 'foodoir',
    sex: '',
    get age() {
        return 21;
    },
    set age(val) {
        console.log('不能設置' + val);
    }
};
console.log(person.username); //foodoir
console.log(person.age); //21
person.age = 13; //不能設置13
var obj = {
    x: 1,
    y: 2,
    z: 3,
    get zhouchang() {
        return this.x + this.y + this.z;
    },
    set fbzc(val) {
        this.x *= val;
        this.y *= val;
        this.z *= val;
    }
};
console.log(obj.zhouchang); //6
obj.fbzc = 2;
console.log(obj.zhouchang); //12

var obj1 = {};
Object.defineProperty(obj1, 'x', {
    get: function() {
        return 123;
    }
});
console.log(obj1.x); //123

問題:在這里我們有多個屬性值需要設定怎么辦呢?

var person = {};
Object.defineProperties(person,{
    'name':{
        value:'foodoir',
        writable:true,
        enumerable:true,
        configurable:true
    },
    age:{
        value:21,
        writable:false
    }
});
person.addr = '湖南';
console.log(person.name);//foodoir
console.log(person.age);//21

//得到對象屬性的描述
console.log(Object.getOwnPropertyDescriptor(person,'name'));
//Object configurable : true enumerable : true value: "foodoir" writable: true__proto__: Object
console.log(Object.getOwnPropertyDescriptor(person,'age'));
//Object configurable : false enumerable : false value : 21 writable : false __proto__ : Object
console.log(Object.getOwnPropertyDescriptor(person,'addr'));
//Object configurable : true enumerable : true value : "湖南" writable : true __proto__ : Object

與對象相關的特性有哪些?

與對象相關的特性有三個:對象的原型(prototype)、對象的類(class)、對象的擴展標記(extensible flag),具體如下:

對象的原型(prototype)指向另外一個對象,本對象的屬性繼承自它的原型對象

        通過對象字面量創建的對象使用Object.prototype作為它們的原型
        通過new創建的對象使用構造函數的prototype屬性作為他們的原型
        通過Object.create()創建的對象使用第一個參數(也可以是null)作為它們的原型

  關於對象的原型,前面已經介紹過了,這里不再另外介紹。

對象的類(class)是一個標識對象類型的字符串

        ECMAScript3和ECMAScript5都未提供這個屬性的方法,可以通過對象的toString()方法間接查詢。下面來看例子:

var obj = {};
console.log(obj.toString());//【object Object】
var arr = new Array();
console.log(arr.toString());//這里的結果還是【object Object】,這里有個問題需要注意,內置對象重寫了toString方法
//我們用回調的方法
console.log(Object.prototype.toString.call(arr));//[object Array]
var d = new Date();
console.log(Object.prototype.toString.call(d));//[object Date]
//測試函數
function classof(obj) {
    if(obj === null) {
        return 'Null';
    }
    if(obj === undefined) {
        return 'Undefined';
    }
    return Object.prototype.toString.call(obj).slice(8, -1);
}
var x = null;//Null
x = undefined;//Undefined
x = 123;//Number
x = 12.3;//Number
x = 'foodoir';//String
x = true;//Boolean
x = [];//Array
x = window;//glable
x = function() {};//Function

function f() {};
x = new f();//Object
console.log(classof(x));

對象的擴展標記(extensible flag)指明了(在ECMAScript5中)是否可以向該對象添加新屬性

        所有內置對象和自定義對象都是顯示可擴展的,宿主對象的可擴展性由JavaScript引擎定義的。
        可以通過Object.preventExtensions()將對象設置為不可擴展的,而且不能再轉換成可擴展的了,可以通過Object.isExtensible()檢測對象是否是可擴展的。示例代碼如下:

        preventExtensions()只影響到對象本身的可擴展性,如果給一個不可擴展的對象的原型添加屬性,這個不可擴展的對象同樣會繼承這些新屬性
        可擴展性的目的是將對象鎖定,防止外接干擾,通常和對象的屬性的可配置行與可寫性配合使用

var obj = {};
//檢測對象是否可擴展
console.log(Object.isExtensible(obj));//true
var d = new Date();
console.log(Object.isExtensible(d));//true
obj.x = 1;
console.log(obj.x);//1
//通過preventExtensions()將對象變為不可擴展的
obj1 = Object.preventExtensions(obj);
console.log(obj === obj1);//true
console.log(Object.isExtensible(obj1));//false
obj1.y = 2;
console.log(obj1.y);//undefined

//通過下面的這種方法會報錯
Object.defineProperty(obj1, 'z', {
    value: 1
});

 問題:如何把對象變為不可擴展的,且保持對象自身的屬性變為不可修改的?

       Object.seal()和Object.preventExtensions()類似,除了能夠將對象設置為不可擴展的,還可以將對象的所有自身屬性都設置為不可配置的。也就是說不能給這個對象添加新屬性,而且它已有的屬性也不能刪除或配置,不過它已有的可寫屬性依然可以設置。可以通過Object.isSealed()檢測對象是否封閉

var obj = {
    x: 1,
    y: 2,
    username: 'foodoir'
};
obj.age = 12;
delete obj.x;
var o = Object.seal(obj);
console.log(obj === o);//true
console.log(Object.isExtensible(o));//false,不可擴展的
obj.y = 55;//嘗試修改
console.log(obj.y);//此時仍然可以修改

/*訪問器屬性是不可以修改的,運行下面代碼會報錯
    Object.defineProperty(obj,'username',{
        get :function(){
            return 'this is a test';
        }
    });
*/

o.z = 77;
console.log(o.z);
console.log(o.username);//foodoir
delete o.username;
console.log(o.username);//foodoir
//    Object.defineProperties(obj,'username',{value:'hello'});//同樣會報錯
console.log(Object.isSealed(o));//true,被封閉了
console.log(o.username);//foodoir
console.log(Object.getOwnPropertyDescriptor(obj, 'username'));
//Object configurable: falseenumerable: true value: "king" writable: true __proto__: Object

        Object.freeze()將更嚴格地鎖定對象--凍結(frozen).除了對象設置為不可擴展的和將其屬性設置為不可配置的之外,還可以將它自身的所有數據屬性設置為只讀(如果對象的存儲器屬性具有setter方法,存取器屬性將不受影響,仍可以通過給屬性賦值調用它們)。可以使用Object.isFroze()來檢測對象是否凍結。

 var obj = {
    prop: function() {},
    foo: 'foodoir'
};
obj.test = 'this is a test';
delete obj.prop;
var o = Object.freeze(obj);
console.log(obj === o);//true
console.log(Object.isFrozen(o));//true,已經被凍結
//凍結后所有的操作都會失敗
o.x = 1;
console.log(o.x);//undefined
console.log(o.foo);//foodoir
o.foo = 'hello';
console.log(o.foo);//foodoir,在嚴格模式下會報出異常

//淺凍結
var obj1 = {
    internal: {}
};
//凍結obj1
Object.freeze(obj1);
//在屬性中添加值1
obj1.internal.x = 1;
console.log(obj1.internal.x);//1

//遞歸的凍結(深度凍結)
function deepFreeze(obj) {
    var prop, propKey;
    Object.freeze(obj);
    for(propKey in obj) {
        //如果還是一個對象的話,將obj[propKey]賦值給prop
        prop = obj[propKey];
        //如果沒有自己的屬性或者不是一個Object或者凍結這個函數
        if(!obj.hasOwnProperty(propKey) || !(typeof prop === 'object') || Object.isFrozen(prop)) {
            continue;
        }
        deepFreeze(prop);
    }
}

var obj2 = {
    internal: {}
};
deepFreeze(obj2);
obj2.internal.x = 1;
console.log(obj2.internal.x);//undefined

下面是關於可擴展性的一些小結,直接看代碼

 
        
//默認對象是可擴展的,也就是非凍結的
    console.log(Object.isFrozen({}));//false
    
//一個不可擴展的對象同時也是一個凍結的對象
    var obj=Object.preventExtensions({});
    console.log(Object.isFrozen(obj));//true
    
//一個非空對象默認也是非凍結
var obj1={x:1};
console.log(Object.isFrozen(obj1));//false
Object.preventExtensions(obj1);//將對象變成不可擴展的
console.log(Object.isFrozen(obj1));//false
delete obj1.x;//刪掉屬性之后,變成可凍結的了
console.log(Object.isFrozen(obj1));//true

//一個不可擴展的對象,但是擁有一個可寫但不可配置的屬性,仍然是非凍結的
var obj2={x:1};
Object.preventExtensions(obj2);
Object.defineProperty(obj2,'x',{writable:false});
console.log(Object.isFrozen(obj2));//false,非凍結
//將x變為不可配置
Object.defineProperty(obj2,'x',{configurable:false});
console.log(Object.isFrozen(obj2));//true,凍結了

//如果一個不可擴展的對象,擁有一個不可配置但可寫的屬性,是非凍結的
var obj3={x:1};
Object.preventExtensions(obj3);
Object.defineProperty(obj3,'x',{configurable:false});
console.log(Object.isFrozen(obj3));//false,非凍結
//將x變為不可寫
Object.defineProperty(obj3,'x',{writable:false});
console.log(Object.isFrozen(obj3));//true,凍結了

//如果一個不可擴展的對象擁有一個訪問器屬性,它也是非凍結的
var obj4={
    get test(){
        return 1;
    }
};
Object.preventExtensions(obj4);
console.log(Object.isFrozen(obj4));//false,非凍結的
Object.defineProperty(obj4,'test',{configurable:false});
console.log(Object.isFrozen(obj4));//true,凍結的

//直接凍結一個對象
var obj5={x:1};
Object.freeze(obj5);
console.log(Object.isFrozen(obj5));//true,被凍結了
console.log(Object.isSealed(obj5));//true,密封的
console.log(Object.isExtensible(obj5));//true,不可擴展的

 

到這里,我們再來小結一下Object.prototype和Object對象有哪些屬性和方法。

Object對象的屬性

Object.prototype: 可以為所有Object類型的對象添加屬性

Object對象的方法

Object.create(): 指定原型對象和屬性創建一個對象。
語法
Object.create(proto, [propertiesObject])
參數
proto: 一個對象, 作為新創建對象的原型
propertiesObject: 一個對象值, 可以包含若干個屬性, 屬性名稱為新建對象的屬性名, 屬性值為那個屬性的屬性描述對象。

Object.defineProperty(): 給對象添加 / 修改一個屬性並指定該屬性的配置
語法
Object.defineProperty(obj, prop, descriptor)
參數
obj: 需要定義的對象
prop: 需要定義或修改的屬性名
descriptor: 屬性定義或修改的屬性的描述
描述
該方法允許精確添加或修改對象的屬性。 正常的屬性添加通過賦值來創建並顯示在屬性枚舉中(
for...in 循環 或 Object.keys 方法), 這種方式添加的屬性值可能被改變, 也可能會被 刪除。 該方法允許改變這些額外細節的默認設置。
對象里目前存在的屬性描述符有兩種主要形式: 數據描述符和存取描述符。 數據描述符是一個擁有可寫或不可寫值的屬性。 存取描述符是由一對 getter - setter 函數功能來描述的屬性。 描述符必須是兩種形式之一;
數據描述符和存取描述符均具有以下可選鍵值
configureable: 當且僅當這個屬性描述符值為 true 時, 該屬性可能會改變, 也可能會被從相應的對象刪除。 默認為 false。
enumerable: true 當且僅當該屬性出現在相應的對象枚舉屬性中。 默認為 false。
value: 與屬性有關的值。 可以是任何有效的Javascript值。 默認為undefined
writable: true當且僅當可能用賦值運算符改變與屬性相關的值。 默認為false
存取描述同時具有以下可選鍵值
get: 一個給屬性提供getter的方法, 如果沒有getter則為undefined。 方法將返回作用屬性的值, 默認為undefined
set: 一個給屬性提供setter的方法, 如果沒有setter則為undefined。 該方法將受到作為唯一參數的新值分配給屬性。 默認為undefined
注意
這些選項不一定是自身屬性, 如果是繼承來的也要考慮。 為了確認保留這些默認值, 你可能要在這之前凍結 Object.prototype, 明確指定所有的選項, 或者將 __proto__ 屬性指向空。

Object.defineProperties(): 在一個對象上添加或修改一個或者多個自有屬性, 並返回該對象。
語法
Object.defineProperities(obj, props)
參數
obj: 將要被添加屬性或修改屬性的對象
props: 該對象的一個或多個鍵值對定義了將要為對象添加或修改的屬性的具體配置

Object.keys(): 方法會返回一個由給定對象的所有可枚舉自身屬性的屬性名組成的數組, 數組中屬性名的排列順序和使用for - in循環遍歷該對象時返回的順序一致(兩者的主要區別是for - in還會遍歷除一個對象從其原型鏈上繼承到得可枚舉的屬性)
語法
Object.keys(obj)
參數
返回該對象的所有可枚舉自身屬性的屬性名
描述
Object.keys 返回一個所有元素為字符串的數組, 其元素來自於從給定的對象上面可直接枚舉的屬性。 這些屬性的順序與手動遍歷該對象屬性時的一致。
如果你想獲取一個對象的所有屬性, ,甚至包括不可枚舉的, 可以通過Object.getOwnPropertyNames() 實現

Object.getOwnPropertyNames(): 返回一個由指定對象的所有自身屬性的屬性名( 包括不可枚舉屬性) 組成的數組
語法
Object.getOwnPropertyNames(obj)
參數
obj: 要查看的對象
描述
Object.getOwnPropertyNames 返回一個數組, 該數組對元素是 obj 自身擁有的枚舉或不可枚舉屬性名稱字符串
數組中枚舉屬性的順序與通過
for...in loop( 或 Object.keys)) 迭代該對象屬性時一致。 數組中不可枚舉屬性的順序未定義。

Object.getOwnPropertyDescriptor(): 返回指定對象上一個自有屬性對應的屬性描述符。(自有屬性指的是直接賦予該對象的屬性, 不需要從原型鏈上進行查找的屬性))
語法
Object.getOwnPropertyDescriptor(obj, prop)
參數
obj: 在該對象上查看屬性
prop: 一個屬性名稱, 該屬性的屬性描述被返回
返回值
如果指定的屬性存在於對象上, 則返回其屬性描述符( property descriptor), 否則返回 undefined
描述
該方法允許對一個屬性的描述進行檢索。 在 Javascript 中, 屬性 由一個字符串類型的“ 名字”( name) 和一個“ 屬性描述符”( property descriptor) 對象構成一個屬性描述符是一個記錄

Object.getPrototypeOf(): 返回指定對象的原型(也就是該對象內部屬性[[Prototype]] 的值)
語法
Object.getPrototypeOf(obj)
參數
要返回的對象
描述
如果參數不是一個對象類型, 將跑出TypeError異常
Object.freeze(): 凍結一個對象。 凍結對象是指那些不能添加新的屬性, 不能修改已有屬性的值, 不能刪除已有屬性, 以及不能修改已有屬性的可枚舉性、 可配置性、 可寫性的對象。 也就是說這個對象永遠不能改變的。
語法
Object.freeze(obj)
參數
obj: 要被凍結的對象
描述
凍結對象的所有自身屬性都不可能以任何方式被修改。 任何嘗試修改該對象的操作都會失敗, 可能是靜默失敗, 也可能會拋出異常( 嚴格模式中)
數據屬性的值不可更改, 訪問器屬性( 有getter和setter) 也同樣( 但由於是函數調用, 給人的錯覺是還是可以修改這個屬性)。 如果一個屬性的值是個對象, 則這個對象中的屬性是可以修改的, 除非它也是個凍結對象。

Object.isFrozen(): 判斷對象是否已經被凍結
語法
Object.isFrozen(obj)
參數
obj: 被檢測的對象
描述
一個對象是凍結的( frozen) 是指它不可擴展, 所有屬性都是不可配置的( non - configurable), 且所有數據屬性( data properties) 都是不可寫的( non - writable)數據屬性是值那些沒有取值器( getter) 或賦值器( setter) 的屬性。

Object.preventExtensions(): 阻止對象擴展
語法
Object.preventExtensions(obj)
參數
obj: 將要變得不可擴展的對象
描述
如果一個對象可以添加新的屬性, 則這個對象是可擴展的。 preventExtensions 可以讓這個對象變的不可擴展, 也就是不能再有新的屬性。
需要注意的是不可擴展的對象的屬性通常仍然可以被刪除
嘗試給一個不可擴展對象添加新屬性的操作將會失敗, 不過可能是靜默失敗, 也可能會拋出 TypeError 異常( 嚴格模式)。

Object.isExtensible(): 檢測一個對象是否可擴展(是否可以在它上面添加新的屬性)
語法
Object.isExtensible(obj)
參數
obj: 需要檢測的對象
描述
默認情況下, 對象是可擴展的: 即可以為他們添加新的屬性。 以及它們的 __proto__ 屬性可以被更改。
Object.preventExtensions, Object.seal 或 Object.freeze 方法都可以標記一個對象為不可擴展( non - extensible)。

Object.seal(): 可以讓一個對象密封, 並返回被密封之后的對象。 密封對象是指那些不能添加新的屬性、 不能刪除已有屬性, 以及不能修改已有屬性的可枚舉性、 可配置性、 可寫性, 但可能可以修改已有屬性的值的對象
語法
Object.seal(obj)
參數
obj: 要被密封的對象
描述
通常情況下, 一個對象是可擴展的( 可以添加新的屬性)
密封一個對象會讓這個對象變的不能添加新屬性, 且所有已有屬性會變的不可配置。
屬性不可配置的效果就是屬性變的不可刪除, 以及一個數據屬性不能被重新定義成為訪問器屬性, 或者反之。
但屬性的值仍然可以修改。 嘗試刪除一個密封對象的屬性或者將某個密封對象的屬性從數據屬性轉換成訪問器屬性, 結果會靜默失敗或拋出TypeError 異常( 嚴格模式)。
不會影響從原型鏈上繼承的屬性。 但 __proto__() 屬性的值也會不能修改。

Object.isSealed(): 檢測一個對象是否被密封sealed
語法
Object.isSealed(obj)
參數
obj: 要被檢測的對象
描述
如果這個對象是密封的, 則返回 true, 否則返回 false。
密封對象是指那些不可 擴展 的, 且所有自身屬性都不可配置的( non - configurable) 對象。

 
        

Object.prototype的屬性

Object.prototype.constructor: 返回一個指向創建了該對象原型的函數引用
  注意:該屬性的值是那個函數本身, 而不是一個包含函數名稱的字符串。 對於原始值( 如1, true 或 "test"),該屬性為只讀。所有對象都會從它的原型上繼承一個 constructor 屬性

Object.prototype的方法

Object.prototype.hasOwnProperty(): 檢測某個對象是否含有指定的自身屬性
語法
obj.hasOwnProperty(prop)
參數
要檢測的屬性名稱
描述
所有繼承了 Object.prototype 的對象都會從原型鏈上繼承到 hasOwnProperty 方法
這個方法可以用來檢測一個對象是否含有特定的自身屬性, 和 in 運算符不同, 該方法會忽略掉那些從原型鏈上繼承到的屬性

Object.prototype.isPrototypeOf(): 檢測一個對象是否存在於另一個對象的原型鏈上
語法
prototype.isPrototypeOf(object)
參數
prototype: 檢測該對象是否在參數object的原型鏈上
object: 在該對象的原型鏈上搜尋
描述
isPrototypeOf方法允許你檢測一個對象是否存在於另一個對象的原型鏈上

Object.prototype.propertyIsEnumerable(): 檢測指定的屬性名是否是當前對象可枚舉的自身屬性
語法
obj.propertyIsEnumerable(prop)
參數
prop: 需要檢測的屬性名
描述
每個對象都有 propertyIsEnumerable 方法。 該方法可以判斷出指定的屬性是否是自身的可枚舉屬性, 也就是說該屬性是否可以通過for...in 循環等遍歷到
有些屬性雖然可以通過for...in 循環遍歷到, 但因為它們不是自身屬性, 而是從原型鏈上繼承的屬性, 所以該方法也會返回false。 如果對象沒有指定的屬性, 該方法返回 false。

Object.prototype.toString(): 返回一個代表該對象的字符串
語法
object.toString()
描述
當對象需要轉換為字符串時, 會調用它的toString() 方法.
默認情況下, 每個對象都會從Object上繼承到toString() 方法, 如果這個方法沒有被這個對象自身或者更接近的上層原型上的同名方法覆蓋(遮蔽), 則調用該對象的toString() 方法時會返回 "[object type]",
這里的字符串type表示了一個對象類型

Object.prototype.valueOf(): 返回的是this值, 即對象本身
語法
object.valueOf()
返回值
在其他類型的對象中, valueOf有可能返回一個不同的值

后面的話:

  今天終於把一個月前該總結完的東西總結完了。再次看的時候,發現對對象的了解又多了一點。不能說一下子就把對象掌握了,但是每次學習后都有一種新的認識,這種感覺很是奇妙。

  到這里,如果你還想想問我對象能干啥,那么我建議你去看看jquery(或者其他流行框架的)源碼,看看大神們都在用對象在干啥,當然,那個時候僅僅知道對象的知識是遠遠不夠的。

  轉載請注明出處!原文網址:http://www.cnblogs.com/foodoir/p/5971686.html

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM