要了解JavaScript对象,我们可以从对象创建、属性操作、对象方法这几个方面入手。概括起来,包括以下几模块:

1.创建对象

1.1 对象直接量

对象直接量是创建对象最简单的方式,由若干名/值对组成映射表:

  1. var point = {x: 0, y: 0 };

属性名也没有什么限制,可以是js的关键字或者任意字符串,如果是这两种情况,属性需要用双引号引起来:

  1. var empty = {};
  2. va point = {x: 0, y: 0 };
  3. var book = {
  4. "main title": "Javascript",
  5. "sub-title": "The definitive Guide",
  6. "for": "all audience",
  7. author: {
  8. firstName: "Davide",
  9. lastName: "Flanagan"
  10. }
  11. };

对象直接量创建对象十分简单,但一般都不会这样使用。代码可复用性低,如果想要在其他地方使用该对象并且属性值不一样,那这么办?是不是又得重新创建一份代码?

1.2 通过new创建对象

通过new创建对象之前,先要创建一个函数,new把这个函数当做构造函数(constructor)。例如通过new创建一个Person对象:

  1. function Person(){
  2. //构造函数
  3. }
  4.  
  5. var person = new Person();

Javscript语言核心中的原始类型都包含内置构造函数:

  1. var a = new Array();
  2. var d = new Date();
  3. var r = new RegExp(“js”);

1.3 Object.create()

在了解Object的create方法之前,我们想看看什么是原型。每一个Javascript对象(null除外)都和另一个对象相关联。“另一个”对象就是我们所说的原型。每一个对象都从原型继承属性。

所有通过对象直接量创建的对象都具有同一个原型对象Object.prototype。关键字new和构造函数创建的对象原型就是构造函数的prototype属性的值。通过new Array()创建对象的原型为Array.prototype,通过new Date()创建的对象原型为Date.prototype。原型暂介绍到这里。

Object.create方法包含两个参数,第一个参数是对象的原型,第二个参数可选,用于描述对象属性。使用很简单,只需传入所需的原型对象即可:

  1. var o1 = Object.create({x: 1, y: 2 }); //原型为Object.prototype

如果想创建一个没有原型的对象,可通过传入null作为参数。这样创建的对象不会继承任何属性,也没有像toString这样的方法:

  1. var o2 = Object.create(null); //没有原型

如果想创建一个普通的空对象,直接传入Object.prototype:

  1. var o3 = Object.create(Object.prototype);

如果是自定义的对象,和创建空对象一样。直接传入对象名.prototype:

  1. function Person(){
  2. }
  3. var o4 = Object.create(Person.prototype);

2.属性管理

2.1 属性查询和设置

对象的属性可通过点(.)或方括号([])运算符获取。如果使用点获取属性,属性名必须是简单的表示符。不能是保留字,比如,o.for或者o.class。

  1. ar author = book.author; //正确
  2. var name = author.surname; //正确
  3. var title = book[“main title”]; //正确
  4. var className = book.class; //错误

object[“property”]这种语法看起来更像数组,只是这个数组的元素是通过字符串索引而不是数字索引。这种数组就是我们所说的关联数组,也称为散列、映射或字典。Javascript对象都是关联数组。

既然对象是关联数组,那么Javascript也为我们提供了属性的遍历方式for/in。下面的例子利用for/in计算portfolio的总计值:

  1. function getvalue(portfolio){
  2. var total = 0.0;
  3. for(stock in portolio){
  4. var shares = portolio[stock];
  5. var price = getquote(stock);
  6. total += shares * price;
  7. }
  8.  
  9. return total;
  10. }

继承:Javascript对象具有自有属性(own property),也有一些属性是从原型对象继承而来。我们先看看一个实现继承功能的函数inherit:

  1. function inherit(p){
  2. if (p == null) throw TypeError(); //p是一个对象,大不能是null
  3. if(Object.create){
  4. return Object.create(p); //直接使用Object.create方法
  5. }
  6. var t = typeof p;
  7. if(t !== "object" && t !== "function") throw TypeError();
  8. function f() {};
  9. f.prototype = p; //将其原型属性设置为p
  10. return new f();
  11. }

假设要查询对象o的属性x,如果o中不存在x,将会继续在o的原型对象中查询属性x。如果原型对象中也没有x,但这个原型对象也有原型,那么继续在这个原型对象的原型上执行查询,直到找到x或者查询到一个原型为null的对象为止。

  1. var o = {}; //o从Object.prototype继承对象属性
  2. o.x = 1; //给o定义x属性
  3. var p = inherit(o); //p继承o和Object.prototype
  4. p.y = 2; //p定义属性y
  5. var q = inherit(p); //q继承p、o和Object.prototype
  6. q.z = 3; //给q定义属性z
  7. var s = q.toString(); //toString继承自Object.prototype
  8. q.x + q.y // => 3:x和y分别继承自o和p

2.2 删除属性

delete运算符可以删除对象的属性:

  1. delete book.author;
  2. delete book[“main title”];

delete只能删除自有属性,不能删除继承属性。要删除继承属性,必须从定义这个属性的原型对象上删除它,而且这会影响到所有的继承自这个原型的对象。删除成功会返回true。

  1. ar o = {x: 1};
  2. delete o.x; //删除x,返回true
  3. delete o.x; //x已经不存在了,什么都没做,返回true。
  4. delete o.toString; //什么都没做,返回true。
  5. delete不能删除可配置型为false的属性。某些内置对象的属性是不可配置的,比如通过变量声明和函数声明创建的全局对象的属性:
  6. delete Object.prototype //不能删除,属性是不可配置的
  7. var x = 1;
  8. delete this.x; //不能删除这个属性
  9. function f() {}
  10. delete this.f; //不能删除全局函数

2.3 检测属性

判断某个属性是否存在于某个对象中,可通过in运算符、hasOwnProperty()和propetyIsEnumerable()方法来检测。
    in运算符:运算符左侧是属性名,右侧是对象。如果对象的自有属性或者继承属性包含属性则返回true:

  1. var o = {x: 1};
  2. "x" in o; //true:x是o的属性
  3. "y" in o; //false:y不是o的属性
  4. "toString" in o; //true:o继承toString属性

hasOwnProperty()方法:检测给定的名字是否是对象的自有属性。对于继承属性它将返回false:

  1. var o = {x: 1};
  2. o.hasOwnProperty("x"); //true:o有一个自由属性x
  3. o.hasOwnProperty("y"); //false:o中不存在属性y
  4. o.hasOenProperty("toString"); //false:toString是继承属性

propertyIsEnumerable()方法:是hasOwnProperty的增强版,只有检测到自有属性并且这个属性是可枚举行为true时才返回true:

  1. var o = inherit({y: 2});
  2. o.x = 1;
  3. o.propertyIsEnumerable("x"); //true: o有一个可枚举属的自有属性x
  4. o.propertyIsEnumerable("y"); //false:y是继承来的
  5. Object.prototype.propertyIsEnumerable("toString"); //false:不可枚举

2.4 枚举属性

通常使用for/in循环遍历对象属性,遍历的属性包括自有属性和继承属性。对象继承的内置方法是不可枚举的,但在代码中给对象添加的属性都是可枚举的。例如:

  1. var o = {x: 1, y: 2, z: 3}; //三个可枚举的自有属性
  2. o.propertyIsEnumeable("toString"); //false,不可枚举
  3. for (p in o) //遍历属性
  4. console.log(p); //输出x、y和z,不会输出toString

有时候我们只想遍历自有属性,并且属性不为函数:

  1. for(p in o){
  2. if(!o.hasOwnProperty(p)) continue;
  3. if(typeof o[p] === "function") continue;
  4. }

我们可通过枚举遍历功能实现可枚举属性的复制:

  1. /*
  2. * 把p中的可枚举属性复制到o中,并返回o
  3. * 如果o和p含同名属性,则覆盖o中的属性
  4. * 这个函数并不处理getter和setter以及复制属性
  5. */
  6. function extend(o, p){
  7. for(prop in p){ //遍历p中的所有属性
  8. o[prop] = p[prop]; //将属性添加到o中
  9. }
  10. return o;
  11. }

ES5定义了两个用以枚举属性名称的函数。第一个是Object.keys(),返回由对象中可枚举属自有属性名称组成的数组。第二个枚举函数是Object.getOwnPropertyNames(),和Object.keys()类似,它返回对象的所有自有属性,而不仅仅是可枚举属性。

3.属性封装

3.1 属性getter和setter

对象属性由名字、值和一组特性(attribute)构成的。在ES5中,属性值可以用一个或两个方法替代,这两个方法就是getter和setter。由getter和setter定义的属性称做“存取器属性”,它不同于“数据属性”,数据属性只有一个简单的值。
    和数据属性不同,存取器属性不具有可写性(writeable atribute)。如果属性同时具有getter和setter方法,那么它是一个读/写属性。如果它只有getter方法,那么它是一个只读属性,如果它只有setter方法,那么它是一个只写属性。读取只写属性总是返回undefined。
    存取器属性定义语法也比较简单,函数定义没有使用function关键字,而是使用get或set:

  1. var o = {
  2. //普通的数据属性
  3. data_prop: 1,
  4. //存取器属性都是成对定义的函数
  5. get accessor_prop(){/* 这里是函数体 */},
  6. set accessor_prop(value){}
  7. };

思考下面这个表示2D笛卡尔点坐标的对象。它有两个普通属性x和y分别表示x坐标和y坐标,它还有两个等价的存取器属性用来表示点的极坐标:

  1. var p = {
  2. //x和y是普通的可读写数据属性
  3. x: 1.0,
  4. y: 1.0,
  5. //r是可读写的存取器属性,它有getter和setter
  6. get r(){return Math.sqrt(this.x * this.x + this.y * this.y); },
  7. set r(newValue){
  8. var oldValue = Math.sqrt(this.x * this.x + this.y * this);
  9. var ratio = newValue / oldValue;
  10. this.x *= ratio;
  11. this.y *= ratio;
  12. },
  13. //theta是只读存取器属性,只有getter方法
  14. get theta() { return Math.atan2(this.y, this.x); }
  15. };

和数据属性一样,存取器属性是可以继承的,因此可以将上述代码中的p对象当做另一个“点”的原型。可以给性对象定义它的x和y属性,但r和theta属性继承而来:

  1. var q = inherit(p);
  2. q.x = 1, q.y = 1;
  3. console.log(q.r);
  4. cosole.log(q.theta);

3.2 属性特性

我们可以将存取器属性的getter和setter方法看成属性的特性。按照这个逻辑,我们也可把属性的值同样看着属性的特性。因此,可以认为一个属性包含一个名字和4个特性。
    数字属性的4个特性分别是它的值(value)、可写性(writeable)、可枚举性(enumerable)和可配置型(configurable)。
    存取器属性不具有值(value)特性和可写性,因此包含:读取(get)、写入(set)、可枚举性、可配置性。

ES5定义了一个名为“属性描述符”的对象,这个对象代表那4个特性。数据属性的描述符对象的属性有value、writable、enumerable和configurable。存取器属性的描述符对象则用get属性和set属性代替value和writable。其中writable、enumerable、configurable都是布尔值,get属性和set属性是函数值。
    通过调用Object.getOwnPropertyDescriptor()可以获取某个对象特定属性的属性描述符:

  1. //返回{value: 1, writable: true, enumerable: true, configurable: true}
  2. Object.getOwnProeprtyDescriptor({x: 1},"x");
  3. //查询上文中定义的random对象的octet属性
  4. //返回{get: /*func */, set: undefined, enumerable: true, configurable: true}
  5. Object.getOwnPropertyDesciptor(random, "octet");
  6.  
  7. //对于继承属性和不存在属性,返回undefined
  8. Object.getOwnPropertyDesciptor({}, "x");
  9. Object.getOwnPropertyDesciptor({}, "toString");

从函数名就可以看出,Object.getOwnPropertyDesciptor()只能得到自有属性的描述符。要想获得继承属性的特性,需要遍历原型链(Object.getPrototypeOf())。

想要设置属性的特性,或者让新建属性具有某些特性,则需要调用Object.defineProperty(),包含三个参数:对象、属性名、属性描述符对象:

  1. // 属性是存在的,但不可枚举
  2. o.x; //=> 1
  3. Object.keys(o) //=> []
  4. //现在对属性x做修改,让它变成只读
  5. Object.defineProperty(o, "x", {writable: true });
  6. //视图更改这个属性的值
  7. o.x = 2; //操作失败但不报错,而在严格模式中抛出类型错误异常
  8. //属性依然是可配置的,因此可通过这种方式对它进行修改:
  9. Object.defineProperty(o, "x", {value: 2 });
  10. o.x //=> 2
  11. //现在将x从数据属性修改为存取器属性
  12. Object.defineProperty(o, "x", { get: function() {return 0;} });
  13. o.x // => 0

如果要同时修改或创建多个属性,则需要使用Object.defineProperties()。第一个参数是要修改的对象,第二个参数㐊一个映射表。例如:

  1. var p = Object.defineProperties({}, {
  2. x: { value: 1, writable: true, enumerable: true, configurable: true},
  3. y: { value: 2, writable: true, enumerable: true, configurable: true},
  4. r: {
  5. get: function(){ return Math.sqrt(this.x * this.x + this.y * this.y); },
  6. enumerable: true,
  7. configurable: true
  8. }
  9. });

getter和setter的老式API: 在ES5采纳之前,大多数Javascript的实现已经可以支持对象直接量语法中get和set写法。这些实现提供了非标准的老式API用来查询和设置getter和setter。这些API由四个方法组成,所有对象都拥有这些方法。

__lookupGetter__()和__lookupSetter__()用以返回一个命名属性的getter和setter方法。
    __defineGetter__()和__defineSetter__()用以定义getter和setter,第一个参数是属性名字,第二个参数是getter和setter方法。

  1. var o = {};
  2. o.__defineGetter__("x", function(){return 0;});
  3. o.__defineSetter__("y", function(value){console.log("set value:" + value);});

4.对象的三个属性

每一个对象都有与之相关的原型(prototype)、类(class)、可扩展性(extensible attribute)。接下来讲述这些属性有什么作用。

4.1 原型属性

对象的原型属性是用来继承属性的,我们经常把“o的原型属性”直接叫做“o的原型”。在之前“创建对象”介绍了三种方式创建对象。通过对象直接量创建的对象使用Object.prototype作为它们的原型。通过new创建的对象使用构造函数的prototype属性作为它们的原型。通过Object.create()创建的对象使用第一个参数作为它们的原型。
    在ES5中,可通过Object.getPrototypeOf()查询对象原型。在ES3中,没有与之等价的函数,而是使用表达式o.constructor.prototype检查对象的原型。
    要想检测一个对象是否是另一个对象的原型(或处于原型链中),使用isPrototypeOf()方法。例如,可以通过p.isPrototypeOf(o)来检测p是否是o的原型:

  1. var p = {x: 1}; //定义一个原型对象
  2. var o = Object.create(p); //使用这个原型创建一个对象
  3. p.isPrototypeOf(o); //=> true,o继承自p
  4. Object.prototype.isPrototypeOf(o) //=> true, p继承自Object.prototype

Mozilla实现的Javascript对外暴露了一个专门命名为__proto__属性,用以直接查询/设置对象原型。但IE和Opera不支持__proto__属性,所以不建议直接使用__proto__属性。

4.2 类属性

对象的类属性是一个字符串,用以表示对象的类型信息。ES3和ES5都为提供设置这个属性的方法,只有一种间接方式查询它。默认的toString()方法返回这种格式的字符串:[object class]。
    可通过调用toString()方法,然后提取已返回字符串的第八个到倒数第二个位置之间的字符。但有个麻烦是,很多对象继承的toString()方法重写了,为了能够调用正确的toString()版本,必须间接调用Function.call()方法。下面例子的classof函数可返回任意对象的类:

  1. function classof(o){
  2. if(o === null) return "Null";
  3. if(o === undefined) return "Undefined";
  4. return Object.prototype.toString.call(o).slice(8, -1);
  5. }

4.3 可扩展性

对象的可扩展性用以表示是否可以给对象添加新属性。所有内置对象和自定义对象都是显式可扩展的。在ES5中,可将对象转换为不可扩展的。
    Object.seal()方法除了能够将对象设置为不可扩展的,还可以将对象的所有自有属性都设置为不可配置的。也就是说,不能给对象添加新属性,而且已有属性也不能删除和配置。
    Object.isSealed()方法用来检测对象是否封闭。
    Object.freeze()方法将更严格的锁定对象,除了拥有Object.seal()方法的功能外,还可以将自有的所有数据属性设置为只读(如果对象的存取器属性有setter方法,存取器属性不受影响, 仍可以通过给属性赋值调用它们)。
    Object.isFrozen()用来检测对象是否冻结。

5.序列化对象

对象序列化是指将对象的状态转换为字符串,也可以将字符串还原为对象。ES5提供了内置函数JSON.stringify()和JSON.parse()用来序列化和还原Javascript对象。这些方法都使用JSON作为数据交换格式。例如:

  1. o = {x: 1, y: {z: [false, null, ""]}}; //定义一个测试对象
  2. s = JSON.stringify(o); //{"x":1,"y":{"z":[false,null,""]}}
  3. p = JSON.parse(s); //p是o的深拷贝

JSON的语法是Javscript语法的子集,它并不能表示Javascript里的所有值。支持对象、数组、字符串、无穷大数字、true、false和null,并且它们可以序列化和还原。NaN、Inifinity和-Inifinity序列化结果都是null。函数、RegExp、Error对象和undefined值不能序列化和还原。
    这里在附加说一下对象的方法:
    toString()方法:它将返回一个表示调用这个方法的对象值的字符串。很多对象都重写了toString()方法,比如Array.toString()、Date.toString()以及Function.toStrring()。
    toJSON()方法:Object.prototype实际上没有定义toJSON()方法,但由于需要执行序列化的对象来说,JSON.stringify()方法会调用toJSON()方法。如果在带序列化的对象中存在这个方法,则调用它。
    valueOf()方法:valueOf()方法和toString()方法非常相似,但往往Javascript需要将对象转换为某种原始值而非字符串的时候才调用它,尤其是转换为数字的时候。有些内置类自定义了valueOf()方法,比如,Date.valueOf()。

如果本篇内容对大家有帮助,请点击页面右下角的关注。如果觉得不好,也欢迎拍砖。你们的评价就是博主的动力!下篇内容,敬请期待!

 
 

JavaScript对象进阶的更多相关文章

  1. JavaScript进阶系列03,通过硬编码、工厂模式、构造函数创建JavaScript对象

    本篇体验通过硬编码.工厂模式.构造函数来创建JavaScript对象. □ 通过硬编码创建JavaScript对象 当需要创建一个JavaScript对象时,我们可能这样写: var person = ...

  2. javascript入门进阶(一)

    javascript 入门进阶 这里主要讲解一下在入门阶段很难注意的一些知识点,不一定有用.但是会了总比不会强. 1.HTML为<script>标签准备的6个属性: -async:可选.表 ...

  3. json与JavaScript对象互换

    1,json字符串转化为JavaScript对象: 方法:JSON.parse(string) eg:var account = '{"name":"jaytan&quo ...

  4. javaScript对象-基本包装类型的详解

    本人按书上的内容大致地把javaScript对象划分成“引用类型”.“基本包装类型”和“内置对象”三块内容. 我们这篇先了解下基本包装类型的详细用法吧! 一.我们先解下相关概念: 1.引用类型的值(对 ...

  5. 如何理解javaScript对象?

    在我们生活中,常常会提到对象一词,如:你找到对象了吗?你的对象是谁呀?等等. 在我们家庭中,有男友的女青年都会说我有对象了,那么她的对象是XX(她的男友). 夫妻间呢?都会说我的爱人是谁谁谁,现在我们 ...

  6. 简述JavaScript对象、数组对象与类数组对象

    问题引出 在上图给出的文档中,用JavaScript获取那个a标签,要用什么办法呢?相信第一反应一定是使用document.getElementsByTagName('a')[0]来获取.同样的,在使 ...

  7. 深入理解javascript对象系列第二篇——属性操作

    × 目录 [1]查询 [2]设置 [3]删除[4]继承 前面的话 对于对象来说,属性操作是绕不开的话题.类似于“增删改查”的基本操作,属性操作分为属性查询.属性设置.属性删除,还包括属性继承.本文是对 ...

  8. Javascript对象的方法赋值

    Javascript对象编程学习中,一直不能很好的掌握对象的属性(property)和方法(method).今天在写代码过程中,又犯了一个低级错误. <!DOCTYPE html> < ...

  9. web前端学习(二) javascript对象和原型继承

    目录 1. JavaScrpt对象 2. 原型对象和继承 3. 对象的克隆 (1)javascript对象 在JS中,对象是属性的容器.对于单个对象来说,都由属性名和属性值构成:其中属性名需要是标识符 ...

随机推荐

  1. ASP.NET中@Page指令中的AutoEventWireup

    AutoEventWireup:指示控件的事件是否自动匹配 (Autowire).如果启用事件自动匹配,则为 true:否则为 false.默认值为 true.如果设为false,则事件不可用.有关更 ...

  2. jquery之val()和attr("value")

    1.attr("value")=原来的默认值 ,而val()=用户改变的值.

  3. 将raw里面的数据库文件写入到data中

    package com.city.list.db; import java.io.File; import java.io.FileNotFoundException; import java.io. ...

  4. 合并果子 (codevs 1063) 题解

    [问题描述] 在一个果园里,多多已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆.多多决定把所有的果子合成一堆. 每一次合并,多多可以把两堆果子合并到一起,消耗的体力等于两堆果子的重量之和 ...

  5. python的pip和virtualenv使用心得

    pip可以很方便的安装.卸载和管理Python的包.virtualenv则可以建立多个独立的虚拟环境,各个环境中拥有自己的python解释器和各自的package包,互不影响.pip和virtuale ...

  6. Python Ogre Blender(转载)

    http://www.cppblog.com/Charlib/archive/2010/05/31/python_ogre_blender_1.html PyOgre入门以及如何使用Blender制作 ...

  7. open/fopen read/fread write/fwrite区别

    fopen /open区别 UNIX环境下的C 对二进制流文件的读写有两套班子:1) fopen,fread,fwrite ; 2) open, read, write这里简单的介绍一下他们的区别.1 ...

  8. spring AOP advice 类型 和 通用的切点的配置方式

    spring aop advice的类型: 1.前置通知(before advice) 2.返回后通知(after returning advice) 3.抛出异常后通知(after throwing ...

  9. hdu 4715 Difference Between Primes

    题目连接 http://acm.hdu.edu.cn/showproblem.php?pid=4715 Difference Between Primes Description All you kn ...

  10. ajax 无刷新文件上传

    无废话,直接重点: 1:准备工作  需要4个js库 1.jquery 8以上版本 2.jquery.ui.widget.js 3.jquery.iframe-transport.js 4.jquery ...