欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

JavaScript对象

程序员文章站 2022-04-24 23:06:57
...

对象

  • 对象是JavaScript的基本数据类型。

  • 对象是一种复合值:它将很多值(原始值或者其他对象)聚合在一起,可以通过名字访问这些值。

  • 对象也可看做是属性的无序集合,每个属性都是一个名/值对。

  • 属性名是字符串,因此我们可以把对象看成从字符串到值的映射。

  • “散列”(hash),”散列表”(hashtable),”字典”(dictionary),”关联数组”(accociative array).

  • JavaScript对象还可以从一个称为原型的对象继承属性。

  • 对象的方法通常是继承的属性。这种”原型式继承”(prototypal inheritance)是JavaScript的核心特征。

  • JavaScript对象是动态的——可以新增属性也可以删除属性——但它们常用来模拟静态对象以及静态类型语言中的”结构体”(struct).

  • 除了字符串,数字,true,false,null和undefined之外,JavaScript中的值都是对象。

  • 对象是可变的,我们通过引用而非值来操作对象。

  • 对象最常见的用法是创建(create),设置(set),查找(query),删除(delete),检测(test)和枚举(enumerate)

  • 属性包括名字和值。

属性名可以是包含空字符串在内的任意字符串,但对象中不能存在两个同名的属性。

值可以是任意JavaScript值,或者可以是一个getter或setter函数

创建对象

可以通过对象直接量,关键字new和Object.create()函数来创建对象。

1.对象直接量

对象直接量是一个表达式,这个表达式的每次运算都创建并初始化一个新的对象。

var empty = {};           //没有任何属性的对象
var point = {x:0,y:0};       //两个属性
var point2 = {x:point.x,y:ponit.y+1};     //更复杂的值
var book = {
  "main title":"JavaScript",         //属性名字里有空格,必须使用字符串表示
  "sub-title":"The Definitive Guide",       //属性名字里有连字符,必须使用字符串表示
  "for":"all audiences",          //"for"是保留字,因此必须使用引号
  author:{
    firstname:"David",       //这个属性的值是一个对象
    surname:"Flanagan"         //注意,这里的属性名都没有引号
  }
}

2.通过new创建对象

  • new运算符创建并初始化一个新对象。

  • 关键字new后跟随一个函数调用。

  • 这里的函数称做构造函数(constructor),构造函数用以初始化一个新创建的对象。

内置构造函数

var o = new Object();    //创建一个空对象,和{}一样
var d = new Date();      //创建一个表示当前时间的Date函数
var r = new RegExp("js");      //创建一个可以进行模式匹配的RegExp对象

3.原型

  • 每一个JavaScript对象(null除外)都和另一个对象相关联。

  • “另一个”对象就是我们熟知的原型,每一个对象都从原型继承属性。

  • 所有通过对象直接量创建的对象都具有同一个原型对象,并可以通过JavaScript代码Object.prototype获得对原型对象的引用。

  • 没有原型的对象为数不多,Object.prototype就是其中之一。它不继承任何属性。其他原型对象都是普通对象,普通对象都具有原型。

  • 所有的内置构造函数(以及大部分自定义的构造函数)都具有一个继承自Object.prototype的原型。例如,Date.prototype的属性继承自Object.prototype,因此由new Date()创建的Date对象的属性同时继承自Date.prototype和Object.prototype.这一系列链接的原型对象就是所谓的”原型链”(prototype chain),

4.Object.create()

  • 它创建一个新对象,其中第一个参数是这个对象的原型。

  • Object.create()是一个静态函数,而不是提供给某个对象调用的方法。

  • 使用它的方法很简单,只须传人所需的原型对象

eg:

var o1 = Object.create({x:1,y:2});     //o1继承了属性x和y
  • 可以通过传人参数null来创建一个没有原型的新对象

eg:

var o2 = Object.create(null);         //o2不继承任何属性和方法
  • 如果想创建一个普通的空对象

eg:

var o3 = Object.create(Object.prototype);    //o3和{}和new Object{}一样
  • 可以通过任意原型创建新对象

eg:通过原型继承创建一个新对象

//inherit()返回一个继承自原型对象p的属性的新对象
//这里使用ECMAScript5中的Object.create()函数(如果存在的话)
//如果不存在Object.create(),则退化使用其他方法
function inherit(p){
  if(p == null){
    throw TypeError();      //p是一个对象,但不能是null
  }
  if(Object.create){         //如果Object.create()存在
    return Object.create(p);          //直接使用它
  }
  var t = typeof p;        //否则进行进一步检测
  if(t !== "object" && t !== "function"){
      throw TypeError();   
  }
  function f(){};      //定义一个空构造函数
  f.prototype = p;         //将其原型属性设置为p
  return new f();         //使用f()创建p的继承对象
}     

当函数读取继承对象的属性时,实际上读取的是继承来的值。如果给继承对象的属性赋值,则这些属性只会影响这个继承对象自身,而不是原始对象

var o = {x:'don't change this value'};
library_function(inherit(o));   //防止对o的意外修改
属性的查询和设置
  • 可以通过点(.)或方括号([])运算符来获取属性的值。

eg:

var author = book.author;  //得到book的"author"属性
var name = author.surname;    //得到author的"surname"属性
var title = book["main title"];  //得到book的"main title"属性
  • 通过点和方括号也可以创建属性或给属性赋值,但需要将它们放在赋值表达式的左侧

eg:

book.edition = 6;   //给book创建一个名为"edition"的属性
book["main title"] = "ECMAScript";   //给"main title"属性赋值

1.作为关联数组的对象

object.property   //使用点运算符和一个标识符
object["property"]       
//第二种语法使用方括号和一个字符串,看起来更像数组,只是这个数组元素是通过字符串索引而不是数字索引。这种数组就是我们所说的关联数组(associative array),也称做散列、映射或字典(dictionary)
  • 当通过[]来访问对象的属性时,属性名通过字符串来表示。字符串是JavaScript的数据类型,在程序运行时可以修改和创建它们。

eg:

var addr = "";
for(var i = 0;i < 4;i++){
  addr += customer["address" + i] + '\n';
}

使用数组写法和用字符串表达式来访问对象属性的灵活性。

function addstock(portfolio,stockname,shares){
  portfolio[stockname] = shares;
}
  • 当使用for/in循环遍历关联数组时,就可以清晰地体会到for/in的强大之处。

eg:

function getvalue(portfolio){
  var total = 0.0;
  for(stock in portfolio){           //遍历portfolio中的每只股票
    var shares = portfolio[stock];  //得到每只股票的份额;
    var price = getquote(stock);        //查找股票价格
    total += shares * price;       //将结果累加至total中
  }
  return total;      //返回total的值
}

2.继承

  • JavaScript对象具有”自有属性”(own property),也有一些属性是从原型对象继承而来的。

eg:

var o = {}  //o 从Object.prototype继承对象的方法
o.x = 1;   //给o定义一个属性x
var p = inherit(o);   //p继承o和Object.prototype
p.y = 2;  //给p定义一个属性y
var q = inherit(p);   //q继承p,o和Object.prototype
var s = q.toString();   //toString继承自Object.prototype
q.x + q.y  //=>3:x和y分部继承自o和p

现在假设给对象o的属性x赋值,如果o中已经有属性x(这个属性不是继承来的),那么这个赋值操作只改变这个已有属性x的值。如果o中不存在属性x,那么赋值操作给o添加一个新属性x。如果之前o继承自属性x,那么这个继承的属性就被新创建的同名属性覆盖了。

  • 属性赋值操作首先检查原型链,以此判断是否允许赋值操作。

  • 在JavaScript中,只有在查询属性时才会体会到继承的存在,而设置属性则和继承无关

eg:

var unitcircle = {r:1};   //一个用来继承的对象
var c = inherit(unitcircle);    //c继承属性r
c.x = 1; c.y =1;    //c定义两个属性
c.r = 2;   //c覆盖继承来的属性
unitcircle.r;   //=>1,原型对象没有修改

属性赋值要么失败,要么创建一个属性,要么在原始对象中设置属性

3.属性访问错误

  • 属性访问并不总是返回或设置一个值。

  • 查询一个不存在的属性并不会报错,返回undefined

eg:

book.subtitle;   //=>undefined:属性不存在
  • 如果对象不存在,那么试图查询这个不存在的对象的属性就会报错。

eg:

//抛出一个类型错误异常,undefined没有length属性
var len = book.subtitle.length;

eg:避免出错的方法

//一种冗余但很易懂的方法
var len = undefined;
if(book){
  if(boo.subtitle){
    len = book.subtitle.length;
  }
}   

//一种更简练的常用方法,获取subtitle的length属性或undefined
var len = book && book.subtitle && book.subtitle.length
删除属性
  • delete运算符可以删除对象的属性。

eg:

delete book.author;   //book不再有属性author
delete book["main title"];  //book也不再有属性"main title"
  • delete运算符只能删除自有属性,不能删除继承属性(要删除继承属性必须从定义这个属性的原型对象上删除它,而且这会影响到所有继承自这个原型的对象)。

eg:

var a = {p:{x:1}};
b = a.p;
delete a.p;   //=>b.x=1
  • 当delete表达式删除成功或没有任何副作用(比如删除不存在的属性)时,他返回true。如果delete后不是一个属性访问表达式,delete同样返回true

eg:

var o = {x:1};  //o有一个属性x,并继承属性toString
delete o.x;    //删除x,返回true
delete o.x;   //什么都没做(x已经不存在了),返回true
delete o.toString;   //什么也没做(toString是继承来的),返回true
delete 1;  //无意义,返回true
  • delete不能删除那些可配置性为false的属性

eg:

delete Object.prototype;        //不能删除,属性是不可配置的
var x = 1;  //声明一个全局变量
delete this.x;  //不能删除这个属性
function f(){};   //声明一个全局函数
delete this.f;     //也不能删除全局函数
  • 当在非严格模式中删除全局对象的可配置属性时

eg:

this.x = 1; //创建一个可配置的全局属性(没有用var)
delete x;   //将它删除
检测属性

检测集合中成员的所属关系——判断某个属性是否存在于某个对象中。可以通过in运算符,hasOwnPreperty()和propertyIsEnumerable()方法来完成这个工作,甚至仅通过属性查询也可以做到这一点。

  • in运算符的左侧是属性名(字符串),右侧是对象。如果对象的自有属性或继承属性中包含这个属性则返回true

eg:

var o ={x:1};
"x" in o;   //true:"x"是o的属性
"toString" in o;  //true:o继承toString属性
"y" in o;  //false:'y'不是o的属性
  • 对象的hasOwnProperty()方法用来检测给定的名字是否是对象的自有属性。对于继承属性它将返回false

eg:

var o = {x:1}
o.hasOwnProperty("x");   //true:o有一个自有属性x
o.hasOwnProperty("y");   //false:o中不存在属性y
o.hasOwnProperty("toString");  //false:toString是继承属性
  • propertyIsEnumerable()是hasOwnproperty()的增强版,只有检测到是自有属性且这个属性的可枚举性(enumerable attribute)为true时它才返回true.

eg:

var o = inherit({y:2});
o.x = 1;
o.propertyIsEnumerable("x");   //true:o有一个可枚举的自有属性x
o.propertyIsEnumerable("y");   //false:y是继承来的
Object.prototype.propertyIsEnumerable("toString");   //false:不可枚举
  • 除了使用in运算符之外,另一种更简便的方法是使用”!==”判断一个属性是否是undefined

eg:

var o = {x:1}
o.x !== undefined;   //true:o中有属性x
o.y !== undefined;   //false:o中没有属性y
o.toString !== undefined;  //true:o继承了toString属性
  • in可以区分不存在的属性和存在但值为undefined的属性

eg:

var o ={x:undefined}  //属性被显示赋值为undefined
o.x !== undefined;   //false:属性存在,但值为undefined
o.y !== undefined;   //false:属性不存在
"x" in o;  //true:属性存在
"y" in o;  //false:属性不存在
delete o.x;   //删除了属性x
"x" in o;  //false:属性不存在
枚举属性

除了检测对象的属性是否存在,我们还会经常遍历对象的属性。通常使用for/in循环遍历

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

过滤for/in循环返回的属性。

for(p in o){
  if(!o.hasOwnProperty(p)){
      continue;      //跳过继承属性
  }
  for(p in o){
    if(typeof o[p] === "function"){
      continue;      //跳过方法
    }
  }
}

用来枚举属性的对象工具函数

<!-- 把p中的可枚举属性复制到o中,并返回o
如果o和p中含有同名属性,则覆盖o中的属性
这个函数并不处理getter和setter以及复制属性 -->

function extend(o,p){
  for(prop in p){   //遍历p中的所有属性
    o[prop] = p[prop];    //将属性添加至o中
  }
  return o;
}

<!-- 把p中的可枚举属性复制到o中,并返回o
如果o和p中含有同名属性,o中的属性将不受影响
这个函数并不处理getter和setter以及复制属性
 -->
function merge(o,p){
  for(prop in p){       //遍历p中是所有属性
    if(o.hasOwnProperty[prop]){   //过滤掉已经在o中存在的属性
      continue;
    }
    o[prop] = p[prop];      //将属性添加至o中
  }
  return o;
}


<!-- 如果o中的属性在p中没有同名属性,则从o中删除这个属性
返回o -->

function restrict(o,p){
  for(prop in o){  //遍历o中的所有属性
    if(!(prop in p)){        //如果在p中不存在,则删除之
      delete o[prop];     
    }
  }
  return o;
}

<!-- 如果o中的属性在p中存在同名属性,则从o中删除这个属性
返回o -->

function subtract(o,p){
  for(prop in p){      //循环遍历p中的所有属性
    delete o[prop];       //从o中删除(删除一个不存在的属性不会报错)
  }
  return o;
}

<!-- 返回一个新对象,这个对象同时拥有o的属性和p的属性
如果o和p中有重名属性,使用p中的属性值 -->

function union(o,p){
  return extend(extend(extend({},o),p))
}

————-此处很难理解———————-

属性getter和setter
  • 对象属性是由名字、值和一组特性(attribute)构成的。

  • 在ECMAScript5中,属性值可以用一个或两个方法替代,这两个方法就是getter和setter.

  • 由getter和setter定义的属性称做”存取器属性”(accessor property),它不同于”数据属性”(data property),数据属性只有一个简单的值。

  • 和数据属性不同,存取器属性不具有可写性(writable attribute).如果属性同时具有getter和setter方法,那么它是一个读/写属性。

eg:

var o = {
  //普通的数据属性
  data_prop:value,

  //存取器属性都是成对定义的函数
  get accessor_prop(){
    //这里是函数体
  }
  set accessor_prop(value){
    //这里是函数体
  }
}
  • 在函数体内的this指向表示这个点的对象

eg:

var p ={
  //x和y是普通的可读写的数据属性
  x:1.0,
  y:1.0,

  //r是可读写的存取器属性,它有getter和setter
  //函数体结束后不要忘记带上逗号
  get r(){
    return Math.sqrt(this.x*this.x + this.y*this.y);
  },
  set r(newvalue){
    var oldvalue = Math.sqrt(this.x*this.x + this.y*this.y);
    var ratio = newvalue/oldvalue;
    this.x *= ratio;
    this.y *= ratio;
  },
  //theta是只读存取器属性,它只有getter方法
  get theta(){
    return Math.atan2(this.y,this.x);
  }
}
  • 和数据属性一样,存取器属性是可以继承的

eg:

var q  inherit(p);     //创建一个继承getter和setter的新对象
q.x = 1,q.y = 1;        //给q添加属性
console.log(q.r);      //可以使用继承的存取器属性
console.log(q.theta);

//这个对象产生严格自增的***
var serialnum = {
  //这个数据属性包含下一个***
  //$符号暗示这个属性是一个私有属性
  $n:0,

  //返回当前值,然后自增
  get next(){
    return this.$n++;
  },

  //给n设置新的值,但只有当它比当前值大时才设置成功
  set next(n){
    if(n >= this.$n){
      this.$n = n;
    }else{
      throw "***的值不能比当前值小";
    }
  }
}


var random = {
  get octet(){
    return Math.floor(Math.random()*256);
  },
  get uint16(){
    return Math.floor(Math.random()*65536);
  },
  get int16(){
    return Math.floor(Math.random()*65536)-32768
  }
}
属性的特性
  • 数据属性的4个特性分别是它的值(value),可写性(writable),可枚举性(enumerable)和可配置性(configurable)

  • 存取器属性的4个特性是读取(get),写入(set),可枚举性和可配置性。

  • 通过调用Object.getOwnPropertyDescriptor()可以获得某个对象特定属性的属性描述符,只能得到自有属性的描述符:

eg:

返回Object {value: 1, writable: true, enumerable: true, configurable: true}
Object.getOwnPropertyDescriptor({x:1},"x");
  • 要想获得继承属性的特性,需要遍历原型链Object.getProtorypeOf()

  • 要想设置属性的特性,或者想让新建属性具有某种特性,则需要调用Object.definePeoperty(),传人要修改的对象,要创建或修改的属性的名称以及属性描述符对象

eg:

var o = {};  //创建一个空对象
//添加一个不可枚举的数据属性x,并赋值为1
Object.defineProperty(o,"x",{
  value:1,
  writable:true,
  enumerable:false,
  configurable:true
  });

//属性是存在的,但不可枚举
o.x;    //=>1
Object.keys(o)  //=>[]

//现在对属性x做修改,让它变为只读
Object.defineProperty(o,"x",{writable:false});

//试图更改这个属性的值
o.x = 2;     //操作失败但不报错,而在严格模式中抛出类型错误异常
o.x //=>1

//属性依然是可配置的,因此可以通过这种方式对它进行修改
Object.defineProperty(o,"x",{value:2});
o.x //=>2

//现在将x从数据属性修改为存取器属性
Object.defineProperty(o,"x",{get:function(){
  return o;
  }});
o.x //=>0
  • 如果要同时修改或创建多个属性,则需要使用Object.defineProperties().第一个参数是要修改的对象,第二个参数是一个映射表,它包含要新建或修改的属性的名称,以及它们的属性描述符

eg:

var p = Object.defineProperties({},{
  x:{value:1,writable:true,enumerable:true,configurable:true},
  y:{value:1,writable:true,enumerable:true,configurable:true},
  r:{
    get:function(){
      return Math.sqrt(this.x*this.x+this.y*this.y)
    },
    enumerable:true,
    configurable:true
  }
  });
  • 复制属性的特性

eg:

/**
 * 给Object.prototype添加一个不可枚举的extend()方法
 * 这个方法继承自调用它的对象,将作为参数传人的对象的属性一一复制
 * 除了值以外,也复制属性的所有特性,除非在目标对象中存在同名的属性
 * 参数对象的所有自有对象(包括不可枚举的属性)也会一一复制
 */

  Object.defineProperty(Object.prototype,"extend",{         //定义Object.prototype.extend
    wirtable:true,
    enumerable:false,    //将其定义为不可枚举
    configurable:true,
    value:function(o){       //值就是这个函数
      //得到所有的自有属性,包括不可枚举属性
      var names = Object.getOwnPropertyNames(o);
      //遍历它们
      for(var i = 0;i < names.length;i++){
        //如果属性已经存在,则跳过
        if(names[i] in this){
          continue;
        }
        //获得O中的属性的描述符
        var desc = Object.getOwnPropertyDescriptor(o,names[i]);
        //用它给this创建一个属性
        Object.defineProperty(this,names[i],desc);
      }
    }
    })
对象的三个属性

每个对象都有与之相关的原型(prototype),类(class)和可扩展性(wxtensible attribute)

1.原型属性

  • 对象的原型属性是用来继承属性的

  • 原型属性是在实例对象创建之初就设置好的,

A.通过对象直接量创建的对象使用Object.prototype作为它们的原型。

B.通过new创建的对象使用构造函数的prototype属性作为它们的原型。

C.通过Object.create()创建的对象使用第一个参数(也可以是null)作为它们的原型。

  • 将对象作为参数传人Object.getPrototypeOf()可以查询它的原型

  • 表达式o.constructor.prototype来检测一个对象的原型

  • 通过new表达式创建的对象,通常继承一个constructor属性,这个属性指代创建这个对象的构造函数

  • 通过对象直接量Object.create()创建的对象包含一个名为constructor的属性,这个属性指代Object()构造函数

  • constructor.prototype是对象直接量的真正的原型

  • 检测一个对象是否是另一个对象的原型(或处于原型链中),isPrototypeOf()

eg:

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

2.类属性

对象的类属性是一个字符串,用以表示对象的类型信息。

  • 默认toString()方法

返回:

[object class]
  • 获得对象的类

classof()函数

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

JavaScript对象

3.可扩展性

对象的可扩展性用以表示是否可以给对象添加新属性。

  • 通过将对象传人Object.esExtensible(),来判断该对象是否是可扩展的。

  • 通过调用Object.preventExtensions()将对象转换为不可扩展的。一旦将对象转换为不可扩展的,就无法再将其转换为可扩展的了。

  • 可扩展性的目的是将对象”锁定”,以避免外界的干扰

  • Object.seal() 封闭

  • Object.freeze() 冻结

eg:

var o = Object.seal(Object.create(Object.freeze({x:1}),
                                  {y:{value:2,writable:true}}))
序列化对象

对象序列化(serialization)是指将对象的状态转换为字符串,也可将字符串还原为对象

  • JSON.stringify() 序列化对象

  • JSON.parse() 还原对象

  • JSON作为数据交换格式,JSON的全称是”JavaScript Object Notation”——JavaScript对象表示法

eg:

o = {
  x:1,
  y:{
    z:[false,null,""]
  }
};           //定义一个测试对象
s = JSON.stringify(o);        //=>s:'{"x":1,"y":{"z":[false,null,""]}}'
p = JSON.parse(s);            //p是o的深拷贝
  • JSON的语法是JvaScript语法的子集,它并不能表示JavaScript里的所有值。
对象方法

所有的JavaScript对象都从Object.prototype继承属性(除了那些不通过原型显式创建的对象)。

1.toString()方法

var s = {x:1,y:1}.toString();     //=>"[object Object]"

2.toLocaleString()方法

这个方法返回一个表示这个对象的本地化字符串

3.toJSON方法

JSON.stringify()方法会调用toJSON()方法

4.valueOf()方法

当JavaScript需要将对象转换为某种原始值而非字符串的时候才会调用它,尤其是转换为数字的时候。

|版权声明:本文为summer博主原创文章,未经博主允许不得转载。

相关标签: javascript