JavaScript || 对象

对象


对象是JavaScript的基本数据类型:属性的无序集合。每一个属性key: value和属性描述符descripter组成。数组

  • 属性名key:字符串或合法的变量标识符;ide

  • 属性值value:能够是任意JavaScript值(numberstringbooleannullundefined和数组、对象).
    value能够是gettersetter函数

  • 属性描述符descripter:每一个属性的三个相关特性writableenumerableconfigurable,其值都是布尔类型,默认都为true工具


1 建立对象

建立对象有三种方法:对象字面量{a: 1}new Constructor()Object.create()测试

1.1 对象字面量

var book = {
    "main title": "JavaScript",                   //有空格或其余非标识符、关键字,必须使用双引号
    "sub-title": "The Definitive Guide",
    "for": "all audiences",
    author: {
        firstname: "David",
        lastname: "Flanagan"
    }
}

1.2 new操做符调用构造函数

可使用new操做符调用一个构造器函数Constructor()建立一个对象。ui

var o = new String();
var d = new Date();

1.3 Object.create()方法

原型:每一个JS对象都有一个与之相关联的原型对象prototype,对象其原型对象上继承属性。(当前对象中的__proto__属性是其原型对象的连接)this

全部经过字面量建立的对象的原型都是Object.prototype,一个字面量对象{}至关于调用new Object()prototype

Object.prototype的原型是null,因此null是原型的出口。rest

Object.create()是一个静态函数(不是提供给对象调用的方法),用于建立对象:code

  • 第一个参数:原型对象,建立的新对象以该参数为原型;

  • 第二个参数:可选,用于对对象属性的描述

  • 建立一个没有原型的对象:Object.create(null);

  • 建立一个普通空对象({}new Object()):Object.create(Object.prototype);


2 对象三个特殊属性

每一个对象都有与之相关的3个属性:原型prototype、类属性calss attribute、可扩展性extensible attribute

2.1 原型

每一个JS对象都与另外一个原型对象(prototype),利用原型能够实现继承。

  • 字面量的原型是:Object.prototype

  • new操做符建立的对象其原型:构造器函数的原型(prototype属性)

  • Object.create()建立的对象,其原型:第一个参数

查询对象的原型

  • ES5中定义了Object.getPrototypeOf()函数,来查询对象的原型

  • 利用new建立的对象会继承constructor属性,指向建立该对的构造器函数,全部该对象的原型是:obj.constructor.prototype

  • 字面量{}constructor属性指向Object()

  • 使用isPrototypeOf()方法查看一个对象是否为另外一个对象的原型(或处于原型链中)

    Array.prototype.isPrototypeOf([]);   //  ==> true

2.2 类属性

对象的类属性class attribute是一个字符串,用来表示对象的信息。ES3与ES5均为提供设置类属性的API,只能经过继承自Object.prototypetoString()方法来简洁查询。

{}.toString();    //  ==> "[object Object]"

经过对返回字符串切片:第8位到倒数第2位便可得到对象的类型。可是许多对象重写了toString()方法,须要间接调用Functioncall()方法

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

classof()方法能够返回传递给它的任意对象的类型,参数为numberstringboolean时,使用包装类型将其转化为对象,再进行操做。

classof(null);  // ==> "Null"
    classof(2);  // ==> "Number"
    classof(false);  // ==> "Boolean"
    classof(new Date());  // ==> "Date"
    classof("");  // ==> "String"
    classof({});  // ==> "Object"
    classof([]);  // ==> "Array"
    classof(/./);  // ==> "RegExp"
    classof(window);  // ==> "Window",宿主对象
    function F() {};    //自定义一个构造器函数
    classof(new F());  // ==> "Object"

2.3 可扩展性extensible attribute

对象的课扩展性用来描述对象是否能够添加新的属性。全部的内置对象和自定义对象都是可扩展的,除非将其转化为不可扩展

  • Object.esExtensible()函数判断传入对象是否能够扩展;

  • Object.preventExensible()函数将传入的对象设置为不可扩展,而且过程不可逆;只影响对象自己的可扩展性

  • Object.seal()函数将对象设置为不可扩展,属性描述符configurable设置为false(不能添加新属性,已有的属性不能删除或配置,可是对于writable: true时,能够修改属性的值);过程不可逆

  • Object.isSealed()函数判断一个对象是否封闭

  • Object.freeze()函数将对象设置为不可扩展,属性描述符configurable: false;writable: false;只读。valueaccessor property含有setter函数,不受影响


3 组成对象的属性

每一个对象是无需的属性集合,属性名能够是合法的变量标识符或字符串;变量值能够是任意JavaScript值;属性由描述符descripter来控制特性;

3.1 属性的查询与设置

属性能够经过.[]来访问:

  • 使用.访问时,其右侧必须是属性名称命名的简单标识符

  • 使用[]访问时,括号内必须是计算结果为字符串的表达式,字符串是属性的名字。变量名为关键字、含有空格或属性名是变量时,必须使用[]

    var author = book.author;
    var name = author.surname;
    var title = book["main title"];

属性的设置:与访问类似,将其放在赋值表达式的左侧

book.edtion = 6;
    book["main title"] = "ECMAScript";

继承与错误

在查询一个对象是否存在时,先查看自身属性,若是没有;经过原型链逐层向上查找,直到原型链顶端null为止。

  • 若是自身属性与原型链中均为找到,属性访问返回undefined,不报错

  • 若是查询不存在对象的属性,会报错

  • nullundefined设置属性会报错

3.2 属性删除

使用delete操做符,能够删除对象的属性(其描述符中configurabletrue),而且只能删除自有属性,不能删除继承属性

3.3 属性检测

判断某个属性是否在某个对象中,JS有三种方法:inhasOwnProperty()propertyIsEnumerable()

  • in:若是对象自身属性或继承属性中包含该属性,返回true;

  • hasOwnProperty():只有对象自身属性包含该属性时,才返回true

  • propertyIsEnumerable():只有对象自身属性包含该属性,而且该属性是能够枚举(描述符中enumerable: true;

    var o = {a : 1};
    "a" in o;  //  ==> true
    o.hasOwnProperty("a");  //  ==> true
    o.propertyIsEnumerable("a");  //  ==> true

3.4 枚举属性

使用for-in循环能够遍历对象中全部可枚举的属性(包括自身属性与继承属性),把属性名赋值给循环变量。ES5定义了Object.keys()Object.getOwnPropertyNames()两个函数用来枚举属性名称。

  • Object.keys():返回对象中可枚举的自有属性名组成的数组

  • Object.getOwnPropertyNames():返回对象全部的自有属性名组成的数组,包括不可枚举的属性

对象继承的内置方法都是不可枚举的,给对象中增长的属性均可以枚举(除非设置其enumerable: false

有许多使用工具库为Object.prototype增长新的属性,这些属性能够被全部对象继承使用,可是在ES5以前,不能将其设置为不可枚举,因此在for-in循环时会被枚举出来。

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

操做对象属性的工具函数:

/**
 * 枚举属性的工具函数
 * 将对象p中可枚举的属性复制到对象o中,返回对象o;
 * 若是p和o含有同名的属性,则覆盖o中的属性
 * 不处理getter和setter以及复制属性
 */
function extend(o, p) {
  for(let prop in p) {
    o[prop] = p[prop];
  }
  return o;
}

/**
 * 将对象p中可枚举的属性复制到对象o中,返回对象o;
 * 若是o和p有同名属性,不影响o中的属性
 * 不处理getter和setter以及复制属性
 */
function merge(o, p) {
  for(let prop in p) {
    if(o.hasOwnProperty(prop)) {
      continue;
    }
    o[prop] = p[prop];
  }
  return o;
}

/**
 * 若是o中的属性在p中没有同名属性,从o中删除这个属性,并返回o
 */
function restrict(o, p) {
  for(let prop in o) {
    if(!(prop in p)) {
      delete o[prop];
    }
  }
  return o;
}

/**
 * 若是o中的属性在p中存在同名属性,从o中删除这个属性,并返回o
 */
function substrict(o, p) {
  for(let prop in o) {
    if(prop in p) {
      delete o[prop];
    }
  }
  return o;
}

/**
 * 返回一个数组,包含o中可枚举的自有属性的名字
 */
function keys(o) {
  //o必须是对象
  if(typeof o !== 'object') {
    throw TypeError();
  }
  var result = [];
  for(let prop in o) {
    if(o.hasOwnProperty(prop)) {
      result.push(prop);
    }
  }
  return result;
}

3.5 属性settergetter

属性的组成:keyvaluedescripter。在ES5中,value能够用一个或两个方法替代(gettersetter方法)

  • 全部JavaScript的value叫作数据属性data property

  • gettersetter定义的属性叫作存取器属性:accessor property

不一样于data propertyaccessor property的读写属性由gettersetter决定。

  • 若是属性同时有gettersetter,它是一个可读写的属性;

  • 若是属性只有getter,它是一个只读的属性;

  • 若是属性只有setter,它是一个只写的属性;读取只写属性老是返回undefined

gettersetter定义

var o = {
    data_prop: value;        //普通数据属性
    
    //存取器属性是成对定义的函数,函数名是该属性名,没有冒号分隔函数体以属性名
    get accessor_prop() {...},
    set accessor_prop(value) {...}
}
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)}
};
  • 函数体内的this指向表示这个点的对象

3.6 属性的描述符descripter

属性除keyvalue,还有一组用于描述其特性的descripter,其中有writableenumerableconfigurable三个属性,其值都为布尔类型,默认为true

假设将settergetter看作descripter,同理将属性的value也看作descripter

  • data property的四个特性:valuewritableenumerableconfigurable

  • accessory property的四个特性:gettersetterenumerableconfigurable

ES5提供操做descripter的API:

  • 调用Object.getOwnPropertyDescripter()函数,获取某个对象中特定自有属性的descirpter

  • 调用Object.getPrototypeOf()函数能够获取继承属性的descirpter

  • 调用Object.defineProperty()函数能够设置属性的特性,或者新建属性,使其具备某种特性

    //返回{ value: 1, writable: true, enumerable: true, configurable: true }
    Object.getOwnPropertyDescriptor({a: 1}, "a");
    
    //继承属性和不存在的属性,返回undefined
    Object.getOwnPropertyDescriptor({});
    Object.getOwnPropertyDescriptor({}, "toString");
    var o = {};   //建立空对象
    //添加一个不可枚举的属性,其值为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 0;}});
    o.x;   //==> 0

4 序列化对象

序列化对象指将对象的状态转化为字符串,同时能够将JSON字符串转化为对象。ES5内置JSON对象的JSON.stringgify()JSON.parse()能够完成序列化和解析。

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

JSON是JavaScript的子集,不能表示JavaScript中全部值。

  • 能够表示:objectArraystringnumberbooleannull,能够序列化与还原。infinityNaN序列化结果是nullDate对象序列化结果是日期字符串

  • 不能表示:函数、RegExpError对象和undefined

  • JSON.stringify()只能序列化对象自有的可枚举属性。


5 Object.prototype对象的方法

  • hasOwnProperty():检测该属性是否为对象自有属性

  • propertyIsEnumerable():检测该属性是不是对象自有,而且可枚举的属性

  • isPrototypeOf:检测某对象是否为另外一个对象的原型

  • toString():无参数,返回调用该方法对象值的字符串,因为功能有限,某些对象重写了该方法:Array.toString()Date.toString()Function.toString()

  • toLoaclString()Object中的toLoaclString()只是调用toString(),只有DateNumber类对toLoaclString()定制,能够对数字、日期和时间进行本地化处理


6 总结

  1. JS中对象是属性的无序集合,每一个对象有三个相关特性:原型prototype、类属性class property、可扩展性extensible

  2. 对象的建立三种方法:对象字面量、new调用构造函数和Object.create()

  3. 每一个属性由三个部分组成:keyvaluedescripter

    • key只能是字符串或者值为字符串的变量标识符

    • value能够是任意JavaScript值(数据属性data property);能够是一个或两个方法,gettersetter(控制区属性accessor property

    • descripterwritableenumerableconfigurable,其值都是布尔类型,默认都为true。可使用Object.defineProperty()操做描述符特性。

  4. writable指该属性是否能够写入数据;enumerable指该属性是否能够枚举,(使用for-in循环能够列出,ES5提供Object.keys()Object.getOwnPropertyNames());configurable指该属性是否能够用delete操做符删除和配置。

相关文章
相关标签/搜索