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}

Object.assign()