zoukankan      html  css  js  c++  java
  • 浅解析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 + "
    " + "性别:" + person.sex); //姓名:foodoir    性别:男
    console.log("年龄:" + person['age'] + "
    " + "薪水:" + 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

  • 相关阅读:
    通过线程池,从hbase中拿数据
    phoenix如何压缩表,以及如何映射表
    spring boot改造现有jms activeMQ配置
    windows安装redis
    mysql免安装版配置
    转发和重定向的区别
    object.equals(null)和object==null区别
    用IDEA学习getRealPath遇到的问题
    总误按win+Enter键弹出讲述人
    戴尔电脑插耳机后声音变化问题
  • 原文地址:https://www.cnblogs.com/foodoir/p/5971686.html
Copyright © 2011-2022 走看看