什么是对象
对象是JavaScript的基本数据类型。是属性的无序集合,每个属性都是一个键值对。属性又分为两种:一种是数据属性,一种是方法属性。(另一种说法是:对象是键值对的集合,对象是由属性和方法构成的)
对象属性名是字符串,所以对象又可以看成是字符串到值的映射。字符串到值的映射这种结构,通常还有一些其它的叫法,比如:散列表、字典、关联数组。
JavaScript对象是动态的,我们可以对对象执行新增属性,删除属性的操作。在JS当中除了字符串、数字、true、false、null、undefined这些值之外,其它所有的值都是对象。
对象的创建方式
对象直接量
创建对象最简单的方式就是使用对象直接量。比如如下代码:
// 创建一个空对象
const o1 = {};
// 创建一个带有属性和方法的对象
const o2 = {
name: "thirteen",
class: "class",
age: 18,
say: function () {
console.log("console.log: say hello");
}
};
console.log(o2.class, "关键字当做属性名");
o2.say();
new 运算符(构造函数)
new 运算符创建并初始化一个新对象,new运算符后面紧跟一个函数调用。new 后跟随的函数被成为构造函数。
使用构造函数创建对象
let o = new Object(); // 创建一个空对象,和{}(对象直接量)一样
let a = new Array(); // 创建一个空数组对象,和[]一样
function papa(){};
let child = new papa(); // 通过实例化构造函数papa来创建一个对象。
Object.create
Object.create方法是ES5中新增创建对象的一个方法。该方法有两个参数,第一个参数必选是新创建对象的原型,第二个参数是对对象属性的描述。Object.create方法返回一个新的对象
语法
Object.create(proto,[propertiesObject])
// 创建没有原型的新对象
const o1 = Object.create(null);
// 从该方法的介绍中我们可以看到,第一个参数是新创建对象的原型,
// o1创建的时候传入null,则表示o1是没有原型的新对象,没有原型,也就没有继承任何的方法和属性。
// 创建一个具有原型的对象
const o2 = Object.create(Object.prototype); // 相当于{}和new Object()创建对象一样
// 创建一个以另一个空对象为原型,且拥有一个属性p的对象
const o3 = Object.create({}, { p: { value: 42 } })
const o4 = Object.create(Object.prototype, {
// foo会成为所创建对象的数据属性
foo: {
writable:true,
configurable:true,
value: "hello"
},
// bar会成为所创建对象的访问器属性
bar: {
configurable: false,
get: function() { return 10 },
set: function(value) {
console.log("Setting `o.bar` to", value);
}
}
});
// 创建一个继承现有对象的新对象
const person = {
name:"thirteen",
age:18,
say:function(){
console.log(`name:${this.name},age:${this.age} `);
}
}
const o3 = Object.create(person);
o3.name === "thirteen" // true
o3.age === 18 // true
o3.say(); // name:thirteen,age:18
用Object.create实现类式继承
// 父类
function parent(){
this.x = 0;
this.y = 0;
}
// 父类的方法
parent.prototype.move = function(x,y){
this.x += x;
this.y += y;
console.log('move 执行了');
}
// 子类
function child(){
parent.call(this);
}
// 子类继承父类
child.prototype = Object.create(parent.prototype);
child.prototype.constructor = child;
// 实例化
var o1 = new child();
console.log(o1 instanceof child);
console.log(o1 instanceof parent);
o1.move(2,2);
对象的属性
一个JavaScript对象有很多属性。一个对象的属性可以被解释成一个附加到对象上的变量。对象属性和普通的JavaScript变量基本没有什么区别,仅仅是属性属于某个对象。
属性查询和设置(修改)
属性的查询和设置有两种方式,第一种是使用(•)点运算符.,第二种是使用([])方括号运算符。在ES3,使用"."点运算符后面是不能跟JS当中的保留字的,例如:for,class,function等,不过在ES5之后就不存在这种问题了,所以下面的代码在现代浏览器里面是不会有问题的.obj.for = "for"
。如果使用方括号[],方括号中间在ES6之前必须是一个计算结果为字符串的表达式。在现代浏览器当中,方括号表达式中间还可以是Symbol类型。
点运算符
// 定义一个对象o1
const o1 = {
name:"thirteen",
age:18
}
// 读取对象o1的属性
o1.name // thirteen
o1.age // 18
// 修改对象o1的属性,如果属性名原来就有,就是修改,如果原来对象上没有该属性,就是新增
o1.sex = "男";
o1.say = function(){
console.log('i say');
}
console.log(o1.name);
//
const name = "thirteen";
const age = 18;
const o2 = {
name,
age
}
o2.name // thirteen
o2.age // 18
方括号运算符
当通过[]来访问(新增,修改)对象的属性时,属性名通过字符串来表示。字符串是JS的基本数据类型,在程序运行时我们可以修改和创建他们。因为有方括号这种访问对象的方式,有时候对象也被叫做关联数组,只不过数组的索引是数字,对象的索引是字符串。
const addr = "";
const obj = {
p0:"p0",
p1:"p1",
p2:"p2",
p3:"p3"
};
for(i=0;i<4;i++){
addr += obj["p"+i] + '\n';
}
// 当对象的属性名字无法确认(动态)的时候,只能通过方括号访问
function addStock(portfolio,name,shares){
portfolio[name] = shares;
}
//同时创建四个变量,用逗号分割
const myObj = new Object(),
str = "myString",
rand = Math.random(),
obj = new Object();
myObj.type = "点运算符";
myObj['data create'] = '有空格的字符串';
myObj[str] = "字符串值";
myObj[rand] = "随机数";
myObj[obj] = "Object 对象";
myObj[""] = "空字符串";
// PS:方括号中所有的键都将转换为字符串类型。
两种方式比较
点运算符
- 使用方便灵活
- 有一定的限制,当对象的属性是数字开头的字符串、空字符串、连字符就不能使用点运算符来访问
- 点运算符后面紧跟的标识符是静态的,必须写死在程序中
方括号运算符
- 使用全面,所有点运算符能访问的不能访问的属性,方括号都可以访问
- 当对象的属性是无法提前得知的时候,只能使用方括号运算符
属性的删除
delete 运算符可以删除对象的属性;不过只能删除对象自身的属性,不能删除对象继承的属性。另外,如果对象的属性的可配置值是false的时候,也是不能够被删除的。delete 表达式执行成功后,会返回一个true
const o1 = {
name:"thirteen"
}
Object.defineProperty(o1, 'age', {
value: 42,
configurable: false
});
// 可以删除
delete o1.name
console.log(o1.name) // 返回undefined
// 不可以删除,因为属性的可配置值configurable是false
delete o1.age
console.log(o1.age) // 返回42
// 不可以删除
delete o1.toString
属性的枚举
属性遍历的次序
- 先遍历数字键,按照谁小谁先的方式
- 再遍历字符串键,按照在对象中的位置排列
- 最后遍历Symbol,按照加入时间升序排列。
// 对象属性遍历的次序
Reflect.ownKeys({
[Symbol()]:0,
b:0,
10:0,
2:0,
a:0
})
// 返回结果
['2','10','a','b',Symbol()]
for...in循环
该方法依次访问一个对象以及其原型链中所有可枚举的属性
const o = {
x: 1,
y: 2,
z: 3,
};
// 对象 o1 继承 对象o
const o1 = Object.create(o, {
foo: {
writable: true,
configurable: true,
enumerable:true, // 可以被枚举
value: "hello",
},
// bar会成为所创建对象的访问器属性
bar: {
configurable: false,
get: function () {
return 10;
},
set: function (value) {
console.log("Setting `o.bar` to", value);
},
},
});
console.log("打印对象o1的属性名和属性值");
for (prop in o1) {
console.log(`${prop}:${o1[prop]}`);
}
// foo:hello x:1 y:2 z:3
// 给对象o1新增a属性,属性可枚举配置为true 能够遍历得到
Object.defineProperty(o1, "a", {
value: "4",
enumerable: true,
});
// 给对象o1新增b属性,属性可枚举配置为false 不能能够遍历得到
Object.defineProperty(o1, "b", {
value: "5",
enumerable: false,
});
console.log("打印对象o1可枚举的属性名和属性值");
for (prop in o1) {
console.log(`${prop}:${o1[prop]}`);
}
//foo:hello x:1 y:2 z:3 a:4
Object.keys(obj)
该方法返回对象obj自身包含(不包括原型中)的所有可枚举属性的名称数组
const o = {
x: 1,
y: 2,
z: 3,
};
// 对象o1继承o,
const o1 = Object.create(o);
o1.name = "thirteen";
o1.age = 18;
const resultArray = Object.keys(o1);
console.log(resultArray,'打印对象可枚举属性的名称数组');
// [name,age]
// 给对象o1新增a属性,属性可枚举配置为true 能够遍历得到
Object.defineProperty(o1, "a", {
value: "4",
enumerable: true,
});
// 给对象o1新增b属性,属性可枚举配置为false 不能能够遍历得到
Object.defineProperty(o1, "b", {
value: "5",
enumerable: false,
});
const resultArray1 = Object.keys(o1);
console.log(resultArray1,'打印对象可枚举属性的名称数组');
// [name,age,a]
Object.getOwnPropertyNames(obj)
该方法返回对象obj自身包含(不包括原型中)的所有属性(无论是否可枚举)的名称的数组。
const o = {
x: 1,
y: 2,
z: 3,
};
const o1 = Object.create(o, {
sex: {
enumerable: false,
value: "男",
},
height: {
enumerable: true,
value: 165,
},
});
o1.name = "thirteen";
o1.age = 18;
const resultArray = Object.getOwnPropertyNames(o1);
console.log(resultArray, "打印对象可枚举属性的名称数组");
// [sex,height,name,age]
// 给对象o1新增a属性,属性可枚举配置为true 能够遍历得到
Object.defineProperty(o1, "a", {
value: "4",
enumerable: true,
});
// 给对象o1新增b属性,属性可枚举配置为false 不能能够遍历得到
Object.defineProperty(o1, "b", {
value: "5",
enumerable: false,
});
const resultArray1 = Object.getOwnPropertyNames(o1);
console.log(resultArray1, "打印对象可枚举属性的名称数组");
// [sex,height,name,age,a,b]
Object.getOwnPropertySymbols(obj)
返回一个数组,包含对象obj自身所有Symbol属性的键名
const mySymbol = Symbol("mySymbol");
const mySymbol1 = Symbol("mySymbol1");
const mySymbol2 = Symbol("mySymbol2");
const mySymbol3 = Symbol("mySymbol3");
const o = {
[mySymbol]: 1,
y: 2,
z: 3,
};
const o1 = Object.create(o, {
sex: {
enumerable: false,
value: "男",
},
height: {
enumerable: true,
value: 165,
},
});
o1.name = "thirteen";
o1.age = 18;
o1[mySymbol1] = "mySymbol1";
const resultArray = Object.getOwnPropertySymbols(o1);
console.log(resultArray, "打印对象Symbol属性的键名数组");
// [symbol(mySymbol1)]
// 给对象o1新增a属性,属性可枚举配置为true 能够遍历得到
Object.defineProperty(o1, mySymbol2, {
value: "4",
enumerable: true,
});
// 给对象o1新增b属性,属性可枚举配置为false 不能能够遍历得到
Object.defineProperty(o1, mySymbol3, {
value: "5",
enumerable: false,
});
const resultArray1 = Object.getOwnPropertySymbols(o1);
console.log(resultArray1, "打印对象Symbol属性的键名数组");
// [symbol(mySymbol1),symbol(mySymbol2),symbol(mySymbol3)]
Reflect.ownKeys(obj)
返回一个数组,包含对象自身所有键名,不管键名是Symbol或字符串,也不管是否可枚举。
const mySymbol = Symbol("mySymbol");
const mySymbol1 = Symbol("mySymbol1");
const mySymbol2 = Symbol("mySymbol2");
const mySymbol3 = Symbol("mySymbol3");
const o = {
[mySymbol]: 1,
y: 2,
z: 3,
};
const o1 = Object.create(o, {
sex: {
enumerable: false,
value: "男",
},
height: {
enumerable: true,
value: 165,
},
});
o1.name = "thirteen";
o1.age = 18;
o1[mySymbol1] = "mySymbol1";
const resultArray = Reflect.ownKeys(o1);
console.log(resultArray, "打印对象自身所有属性的键名数组");
// [sex,height,name,age,Symbol(mySymbol1)]
// 给对象o1新增a属性,属性可枚举配置为true 能够遍历得到
Object.defineProperty(o1, mySymbol2, {
value: "4",
enumerable: true,
});
// 给对象o1新增b属性,属性可枚举配置为false 不能能够遍历得到
Object.defineProperty(o1, "b", {
value: "5",
enumerable: false,
});
const resultArray1 = Reflect.ownKeys(o1);;
console.log(resultArray1, "打印对象Symbol属性的键名数组");
// [ "sex", "height", "name", "age", "b", Symbol(mySymbol1), Symbol(mySymbol2) ]
属性的特性
除了包含名字和值之外,属性还包含一些标记他们可写,可枚举和可配置的特性。在ES3中无法设置这些内容,ES5之后就可以了。属性可以分为『存取器属性』(getter和setter定义的属性)和『数据属性』,不同的属性有不同的特性。
存取器属性
- get // 读取
- set // 写入
- enumerable // 是否可以被枚举(遍历读取)
- configurable // 当且仅当该属性的 configurable 键值为 true 时,该属性的描述符才能够被改变,同时该属性也能从对应的对象上被删除。
数据属性
- value // 属性值
- writable // 属性是否可写(是否可以修改)
- enumerable // 是否可以被枚举(遍历读取)
- configurable
获取属性的属性描述
通过调用Object.getOwnPropertyDescriptor方法可以获得某个对象特定属性的属性描述,例如:
const o = {
x: "我是X",
y: 2,
z: 3,
};
const o1 = Object.create(o);
o1.age = 18;
Object.defineProperty(o1, "name", {
value: "thirteen",
writable: true,
configurable: true,
enumerable: true,
});
const ageDesc = Object.getOwnPropertyDescriptor(o1, "age");
const nameDesc = Object.getOwnPropertyDescriptor(o1, "name");
const xDesc = Object.getOwnPropertyDescriptor(o1, "x");
//
console.log(ageDesc, "获取自有属性age默认的属性描述");
console.log(nameDesc, "获取自有属性name配置的属性描述");
console.log(xDesc, "获取继承属性X的属性描述");
// 如果想要获取继承的属性的属性描述,需要通过Object.getPrototypeOf方法先遍历原型链
const xdesc2 = Object.getOwnPropertyDescriptor(
Object.getPrototypeOf(o1),
"x"
);
console.log(xdesc2, "获取继承属性X的属性描述");
属性getter和setter
对象的属性是由名字、值和一组特性构成的。在ES5之后属性的值可以用一个或两个方法替代,这两个方法就是getter和setter。由getter和setter定义的属性称作『存取器属性』,不同于『数据属性』,数据属性只有一个简单的值。
当我们使用getter和setter定义属性的时候,我们就可以监听属性的读取和写入动作,比如VUE框架当中就使用了Object.defineProperty方法重写了data对象当中的所有属性,这样当data的值发生改变的时候,就可以更新vue当中的模板了;
存取器属性还可以智能检查属性的写入值,以及在属性每次读取的时候返回不同的值;
// 如何定义,第一种
const o1 = {
name: "thirteen",
$age: 18,
get age() {
return this.$age;
},
set age(v) {
this.$age = v;
},
};
console.log("第一种返回");
console.log(o1.age); // 18
o1.age = 20;
console.log(o1.age); // 20
// 第二种
const o2 = {
$age: 18,
};
let ageValue = 18;
Object.defineProperty(o2, "age", {
get: function () {
return ageValue;
},
set: function (value) {
ageValue = value;
},
});
console.log("第二种返回");
console.log(o2.age); // 18
o2.age = 20;
console.log(o2.age); // 20
// 第三种:当使用Object.create 方法创建对象的时候
const o3 = Object.create(Object.prototype, {
// foo会成为所创建对象的数据属性
foo: {
writable: true,
configurable: true,
value: "hello",
},
// bar会成为所创建对象的访问器属性
bar: {
configurable: false,
get: function () {
return 10;
},
set: function (value) {
console.log("Setting `o.bar` to", value);
},
},
});
console.log("第三种返回");
console.log(o3.bar); // 10
o3.bar = 20; // 打印 『Setting o.bar to 20』
// 读取对象属性时,每次返回不同的值
const serialnum = {
$n: 0,
get next() {
return this.$n++;
},
set next(n) {
if (n >= this.$n) this.$n = n;
else throw "序列号的值不能比当前值小";
},
};
console.log("序列号属性返回")
console.log(serialnum.next); // 0
console.log(serialnum.next); // 1
console.log(serialnum.next); // 2
console.log(serialnum.next); // 3
console.log(serialnum.next); // 4
console.log(serialnum.next); // 5
// 每次读取属性都返回一个随机数
const random = {
get octet() {
// 返回0~255之间的数字
return Math.floor(Math.random() * 256);
},
get uint16() {
// 返回0~65535之间的数字
return Math.floor(Math.random() * 65536);
},
get int16() {
// 返回-32768~(65535-32768)之间的数字
return Math.floor(Math.random() * 65536) - 32768;
},
};
console.log("随机数打印结果")
console.log(random.octet);
console.log(random.octet);
console.log(random.octet);
console.log(random.uint16);
console.log(random.uint16);
console.log(random.uint16);
console.log(random.int16);
console.log(random.int16);
console.log(random.int16);
对象的方法
当我们通过对象直接量、new Object()和Object.create()不传入null的时候创建的对象,都会和另一个对象关联,『另一个』对象就是我们熟知的原型,每一个对象都从原型继承属性;
所以当我们新创建一个对象的同时,对象就有一些方法可以使用,这些方法就是从原型继承的。
toString
返回一个表示调用这个方法的对象值的字符串。
toLocaleString
同toString类似,不同的是针对不同类型的对象做了定制处理,比如Data和Number对象
valueOf
和toString类似,常常在JavaScript需要将对象转换为某种原始值而非字符串的时候才会调用它。
其它
new Object()
通过构造函数的形式创建对象,new Object()方法可接受一个参数;下面看一些例子:
const o = new Object();
console.log(o,'不传入任何参数');
const o1 = new Object(null);
console.log(o1,'传入null');
const o2 = new Object(undefined);
console.log(o2,'传入undefined');
// 相当于 const o3 = new Boolean(true);
const o3 = new Object(true);
console.log(o3,'传入true');
// 相当于 const o4 = new String("3");
const o4 = new Object("3");
console.log(o4,'传入字符串');
const o5 = new Object(function(){});
console.log(o5,'传入函数');
const o6 = new Object(function(){
return [1,2,3]
});
console.log(o6,'传入函数');
// 相当于 const o4 = new Boolean(false);
const o7 = new Object(Boolean());
console.log(o7,'传入对象');
打印结果:
super关键字
对象当中的this指向当前所在的对象,ES6中新增了一个super关键字,super关键字指向对象的原型对象;
const parent = {
name: 'papa'
};
const child = {
name: 'child',
say() {
return super.name;
}
};
// 通过setProtoTypeOf 设置对象child的原型是parent
Object.setPrototypeOf(child, parent);
child.say() // "papa"
PS:注意,super关键字表示原型对象时,只能用在对象的方法之中,用在其他地方都会报错。
详情参考:ES6——对象的扩展
更多JavaScript对象的扩展内容,参考阮一峰的ES6对象的扩展一节
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。