JS对象
简单来说,对象是JavaScript的基本数据类型,它是一种复合值,名:值(原始值或者其他对象),可通过名访问这些值,可以看做是映射。
一、 创建对象
创建对象有3种方式:对象直接量、关键字new、object.create()函数。
1.1 通过对象直接量创建
属性名可以是JS标识符也可以是字符串直接量,属性的值可以是任意类型的JS表达式。
//创建没有任何属性的对象
let o1 = {};
//创建有两个属性的对象
let o2 = {
attribute1:0,
attribute2:0
};
//创建属性的值是一个对象的对象
let o3 = {
author: {
attribute1:0,
attribute2:0
},
}
1.2 通过new创建
new运算符创建并初始化一个新对象。关键字new后跟随一个函数调用。这里的函数称做构造函数,构造函数用以初始化一个新创建的对象。
如下为内置构造函数:
//创建一个空对象,和{}一样
let o = new Object();
//创建一个空数组,和[]一样
let a = new Array();
//创造一个表示当前时间的Date对象
let d = new Date();
//创建一个可以进行模式匹配的EegExp对象
let r = new RegExp("js");
1.3 通过Object.create()创建
Object.create()的方法,它创建一个新对象,其中第一个参数是这个对象的原型。
//参数为所要的原型对象,o1继承了属性x和y
let o1 = Object.create({x:1,y:2});
创建一个不继承任何东西的新对象,甚至不包括基础方法,如toString():
//o2不继承任何属性和方法
let o2 = Object.create(null);
如果想创建一个普通的空对象:
//需要传人object.prototype原型
let o3 = Object.create(Object.prototype);
1.4 原型
每一个JavaScript对象(null除外)都和另一个对象相关联。“另一个”对象就是我们熟知的原型,每一个对象都从原型继承属性。
所有通过对象直接量创建的对象都具有同一个原型对象,并可以通过Javascript代码object.prototypeѣs对原型对象的引用。
//直接量创建对象
let o = {};
//获取直接量创建对象的原型
Object.prototype
//每个对象都有一个__proto__属性,它指向自己的原型对象,所以
//o.__proto__ == Object.prototype;
通过关键字new和构造函数调用创建的对象,的原型就是构造函数的prototype属性的值。
//创建String()对象
let s = new String();
// s的原型就是String.prototype
//创建Date()对象
let d = new Date();
// s的原型就是Date.prototype
没有原型的对象为数不多,object.prototype就是其中之一,它不继承任何属性。其他原型对象都是普通对象,普通对象都具有原型。所有的内置构造函数(以及大部分自定义的构造函数)都具有一个继承自object.prototype的原型。例如,,Date.prototype的属性继承自obiect.prototype,因此由new Date()创建的Date对象的属性同时继承自Date.prototype和object.prototype。这一系列链接的原型对象就是所谓的“原型链”
二、属性的查询和设置
2.1 查询
查询对象属性有两种方式:通过点(.)或([])运算符来获取属性的值。
//对象
let book = {
name:"三国",
author:"阿三"
};
//通过点(.)获取属性的值
console.log(book.name);
//通过([])获取属性的值
console.log(book["author"]);
2.2 设置
通过点和方括号也可以创建属性或给属性赋值,但需要将它们放在赋值表达式的左侧:
//赋值
let sname = book.name;
//修改
book.author = "小牛";
2.3 删除属性
delete运算符可以删除对象的属性。delete运算符只能删除自有属性,不能删除继承属性(要删除继承属性必须从定义这个属性的原型对象上删除它,而且这会影响到所有继承自这个原型的对象)。
//删除--成功,返回true
delete book.author;
//什么都没做(author已经不存在了),返回true
delete book.author;
//什么都没做(toString是继承来的),返回true
delete book.toString;
//无意义,返回true
delete 1;
//不能删除,属性是不可配置的
delete Object.prototype;
//声明一个全局变量
let x = 1;
//不能删除
delete this.x;
//声明一个全局函数
function f() {}
//也不能删除全局函数
delete this.f;
2.4 检测属性
判断某个属性是否存在于某个对象中,可以通过in运算符来完成这个工作。
//对象
let book = {
name:"三国",
author:"阿三"
};
//true: "name"是book的属性
"name" in book;
//false: "y"不是book的属性
"y" in book;
2.5 枚举属性
遍历对象的属性,使用for/in循环遍历。
//三个可枚举的自有属性
let o = {x:1,y:2,z:3};
//遍历属性 输出x、y和z,不会输出tostring
for(p in o) console.log(p);
//for等语句如操作代码体只有一行可以简写为上述样子
三、属性getter和setter
由getter和sette定义的属性称做“存取器属性”,当程序查询存取器属性的值时,JavaScript调用getter方法(无参数) 。当程序设置一个存取器属性的值时, JavaScript调用setter方法,将赋值表达式右侧的值当做参数传入setter.从某种意义上讲,这个方法负责“设置”属性值。可以忽略setter方法的返回值。
定义存取器属性最简单的方法是使用对象直接量语法的一种扩展写法:
//定义具有get方法和set方法的对象
let o = {
//定义普通的可读写的数据属性
data_prop1 : 1,
//定义存取器属性 都是成对定义的函数
get data_prop2() {
return this.data_prop1;
},
set data_prop2(prop) {
this.data_prop1 = prop * 2;
}
}
//调用存取器属性的set方法,更改普通数据属性data_prop1的值
o.data_prop2 = 2;
//这时data_prop1的值为4,调用存取器属性的get方法,o.data_prop2将返回4
o.data_prop2;
o.data_prop2是一个读/写属性。如果它只有getter方法,那么它是一个只读属性。如果它只有setter方法,那么它是一个只写属性(数据属性中有一些例外) ,读取只写属性总是返回undefined。
下面我将会写一个只有getter(只读)的对象属性:
//这个对象有一个可以返回随机数的存取器属性
//例如,表达式"random.octet"产生一个随机数
//每次产生的随机数都在0-255之间
let random = {
get octet() { return Math.floor(Math.random()*256); },
get uint15() { return Math.floor(Math.random()*65536); },
get int16() { return Math.floor(Math.random()*65536)-32768; }
}
//调用属性
random.octet(); //0-255之间随机数
四、属性的特性
除了包含名字和值之外,属性还包含一些标识它们可写、可枚举和可配置的特性。
一个属性包含一个名字和4个特性:
- 数据属性的4个特性分别是它的值(value) 、可写性 (writable) 、可枚举性(enumerable)和可配置性(configurable) 。
- 存取器属性不具有值(value)特性和可写性,它们的可写性是由setter方法存在与否决定的,因此存取器属性的4个特性是读取(get)、写人(set) 、可枚举性和可配置性。
通过调用object.getownPropertyDescriptor()可以获得某个对象特定属性的属性描求符:
//属性描述符
//返回{value: 1, writable:true, enumerable:true, configurable:true)
object.getownPropertyDescriptor({x:1}, "x");
//查询上文中定义的randam对象的octet属性
//返回(get: /*func*/, set:undefined, enumerable:true,configurable:truel
object.getownPropertyDescriptor(random, "octet");
要想设置属性的特性,或者想让新建属性具有某种特性,则需要调用object.definePeoperty(),传入要修改的对象、要创建或修改的属性的名称以及属性描述符对象:
//创建一个空对象
var o ={};
//添加一个不可枚举的数据属性x,并赋值为1
object.definePeoperty(o, "x", { value : 1,
writable: true,
enumerable: false,
configurable: true);
//属性是存在的,但不可枚举
o.x; //1
object.keys(o); //[]
//现在对属性x做修改,让它变为只读
object.definePeoperty(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
五、对象方法
5.1 toString()
它并不会输出很多有用的信息,因此很多类都带有自定义的tostring(),例如,当数组转换为字符串的时候,结果是一个数组元素列表,只是每个元素都转换成了字符串,再比如,当函数转换为字符申的时候,如Array.tostring()、 Date. tostring()以及Function.tostring()。
下面这行代码的计算结果为字符串"[object Object]",
var s = {x:1, y:1 }.toString();
5.2 toLocaleString()
这个方法返回一个表示这个对象的本地化字符串。
Object中默认的toLocalestring()方法并不做任何本地化自身的操作,它仅调用tostring()方法并返回对应值。Date和Number类对toLocalestring()方法做了定制,可以用它对数字、日期和时间做本地化的转换。Array类的toLocalestring()方法和tostring()方法很像,唯一的不同是每个数组元素会调用tolocalestring()方法转换为字符串,而不是调用各自的tostring()方法。
5.3 valueof()方法
valueof()方法和tostring()方法非常类似,但往往当Javascript需要将对象转换为某种原始值而非字符串的时候才会调用它,尤其是转换为数字的时候。如果在需要使用原始值的上下文中使用了对象, JavaScript就会自动调用这个方法。默认的valueof()方法不足为奇,但有些内置类自定义了valueof()方法(比如Date.valueof()) , 9.6.3节讨论如何给自定义对象类型定义valueof()方法。
注意点
- JS中除了字符串、数字、true、false、null、undefined之外,JS中的值都是对象。
- 所有通过对象直接量创建的对象都具有同一个原型对象。