JavaScript之对象
对象是JavaScript中最基本的数据类型。
对象是一个属性(property)的无序集合,每个属性是一对名字(name)和值(value),属性名通常是字符串(ES6后可为symbol)。
创建对象
对象字面量,new关键字和Object.create()
对象字面量
这是最简单的语法来创建对象。
const days = {}; // 创建一个无属性的对象
let point = {x: 1,y: 2};
let p2 = {x: point.x,y: point.y};
let obj = {
str: "string",
cat: {
"name": "orange",
"skill": "sleep",
"fav food": "fish" //属性名包含空格和连字符,因此使用字符串字面量
}
};
注意的是,对象字面量语法属于表达式,每次求值都会创建并初始化一个新的、不一样的(地址值)对象;因此每个属性的值也会被求值,在重复调用时,可以创建很多新的对象,且这些对象的属性的值可能不同。
new关键字
熟悉面向对象类语言的应该不会陌生这个关键字,这种其实就是调用了构造函数(constructor),以初始化对象。
let emptyObj = new Object(); // {}
let arrObj = new Array(); // []
let dateObj = new Date(); // 日期对象let
let mapObj = new Map(); //创建Map映射对象,用于存储键值映射
Object.create()
关于这个函数,首先要说的是JavaScript中的原型(prototype),上面的通过对象字面量创建的对象都有相同的原型对象,Object.prototype引用了这个原型对象。而使用了new关键字和构造函数调用创建的对象,使用了构造函数prototype属性的值作为它们的原型,即继承自Object.prototype。
类似的,通过new Array()创建的对象以Array.prototype为原型,new Date()则为Date.prototype。
记住,几乎所有对象都有原型(Object.prototype无原型),但只有小数对象有prototype属性(Object、多数内置构造函数)。
let obj = Object.create({x: 1,y: 2}); // obj继承属性x和y
console.log(obj.x+obj.y); // => 3
//传入null可以创建一个没有原型的对象,这个新对象不会继承任何东西,toString()也不会有
let obj2 = Object.create(null); // 不继承任何属性或方法
let obj3 = Object.create(Object.prototype); // 与{} 或 new Object() 类似
//保护对象不被第三方库修改
let o = {dontModValue: "dont change this!!"};
//pass obj which extends o
library.function(Object.create(o));
查询(访问)和设置属性
作为关联数组的对象
访问属性:
object.property
object.["proprety"]
对于第二种访问方式,和普通数组索引访问相像,只不过索引变成了字符串。而这种数据被称为关联数组
,JavaScript对象是关联数组。
JavaScript程序可以为任意对象创建任意数量的属性。
let addr = "";
for(let i=0;i<4;i++){
addr += customer[`address${i}`+"\n"]; // address0 address1 address2 address3
}
字符串是动态的,可以在运行时修改;而标识符是静态的,必须硬编码到程序中。
function addAuth(user, name, auth){// unit是一个存储用户的对象,该方法用于添加权限
// unit.name = auth; //addAuth(user,"zhangsan",auth) => {name: auth} name是字符串
//对于unit.name 由于name为字符串,所以先进行求值得到标识符name,添加新属性到unit对象
unit[name] = auth; //addAuth(user,"zhangsan",auth) => {zhangsan: auth }
}
继承
JavaScript对象有一组“自有属性”,同时也从它们的原型对象中继承一组属性。对于继承的属性查找,对象中没有某属性,就会沿上的原型查找该属性,这个过程会一直进行直至查询到一个原型为null的对象。
let o = {};//继承自Object.prototype\
o.x = 1; // 添加自有属性
let p = Object.create(o); // p从o和Object.prototype继承属性
p.y = 2; //p自有属性let
let q = Object.create(p); //q从p、o和Object.prototype继承属性
q.z = 3;
let f = q.toString();//toString方法继承
console.log(q.x+q.y); // 3
q.x = "new value"; //只会修改原始对象的属性,原型不受影响
删除属性
使用delete操作符可以从对象中移除属性。同样的只删除对象自有属性,不会删除继承属性。
delete 属性访问表达式
let base = {x: 1};
let sub = Object.create(base);
sub.y = 2;
console.log(sub.x+sub.y); //3
delete sub.x; //虽然true
delete sub.y; //true
console.log(sub.x); //1 不会被删除
console.log(sub.y); //undefined
delete不会删除configurable特性为false的属性。
测试属性
in, hasOwnProperty()和propertyIsEnmuerable()。
in操作符
in操作符要求左边是一个属性名,右边是一个对象。
let o = {x: 1};
"x" in o; //true
"y" in o; //false
"toString" in o;// true
对象属性不是undefined时可利用 !==
let o = {x: 1};
o.x !== undefined; // true o有属性x
o.y !== undefined; //flase 无
o.toString !== undefined; // true
hasOwnProperty()
对象的hasOwnProperty()方法用于测试对象是否有给定名字的自有属性。对于继承的属性,返回false。
let o = {x: 1};
o.hasOwnProperty("x"); //true
o.hasOwnProperty("y"); //false
o.hasOwnProperty("toString"); //false
propertyIsEnmuerable()
propertyIsEnmuerable()方法细化了hasOwnProperty()测试。如果传入的命名属性是自有属性且这个属性的enumerate特性为true,这个方法就返回true.
枚举属性
let o = {x: 1,y: 2,z: 3};//3个可枚举属性
o.propertyIsEnmuerable("toString");// false 不可枚举属性
for(let p in o){
console.log(p); //1 2 3
}
//跳过属性或方法
for(let p in o){
if(!o.hasOwnProperty(p)) continue; //跳过继承属性
}
for(let p in o){
if(typeof o[p] === "function") continue; // 跳过所有方法
}
扩展对象
常见的对象复制操作:
let target = {x: 1}, source = {y: 2,z: 3};
for(let key of Object.keys(source)){
target[key] = source[key];
}
target // => {x: 1,y: 2,z: 3}