js对象
在前面,我们有介绍过,在JavaScript中,数据类型整体上来讲可以分为两大类:简单数据类型 和复杂数据类型。
简单数据类型一共有6种,分别是我们前面介绍过的
string , number , boolean , null , undefined 以及本章将会介绍的 symbol
而复杂数据类型就只有1种
object
也就是我们本章将会向大家所介绍的对象。事实上,我们前面已经遇到过一些对象了。例如之前 所介绍过的数组就是一种对象,不过它是JavaScript语言中内置的对象。在本章中我们将学习到 如何自定义对象,以及一些其他的内置对象。
本章将学习如下内容:
・创建对象的方式
・访问对象属性的方式
・对象常用的属性和方法
・对象的嵌套与解构
- this关键字
・JSON对象
- Math对象
- Date对象
•正则表达式
6-1对象基础介绍
6-1-1 JS对象概述
JavaScript里面的对象就是一组键值对的集合。这些键一般由字符串构成,而值可以是任意数据 类型。比如字符串,数字,布尔,数组或者函数。一般来讲,如果一个键映射的是一个非函数的 值,我们将这个值称之为该对象的属性,而如果一个键映射的是一个函数的值,那么我们将其称 之为方法。
6-1-2创建对象
要创建一个对象,我们只需要输入一对大括号即可。这样我们就可以创建一个空的对象,如下:
let objName = {};
创建好对象以后,我们就可以给该对象添加相应的属性,例如这里我们给xiejie这个对象添加 相应的属性
let xiejie = {};
xiejie.name = "xiejie";
xiejie.age = 18;
xiejie.gender = "male";
xiejie.score = 100;
当然,和前面为大家介绍过的数组一样,我们可以在创建对象时就给对象添加好属性信息,如 下:
let xiejie = {
name : "xiejie",
age : 18,
gender : "male",
score : 100
};
可以看到,当我们创建包含属性的对象的时候,属性与属性之间是以逗号隔开的。这里我们可以 将属性名称之为键,属性对应的称之为值。所以,正如开头我们所介绍的那样,对象是由一个一 个键值对组成的。
6-1-3访问对象属性
访问对象的属性的方法有3种:点访问法,中括号访问法,symbol访问法
- 点访问法
我们可以通过一个点.来访问到对象的属性,如下:
let xiejie = {
name : "xiejie",
age : 18,
gender : "male",
score : 100
};
console.log(xiejie.name);//xiejie
console.log(xiejie.age);//18 console.log(xiejie.gender);//rnale
console.log(xiejie.score);//100
- 中括号访问法
第二种方法,是使用中括号法来访问对象的属性,如下:
let xiejie = {
name : "xiejie",
age : 18,
gender : "male",
score : 100
};
console.log(xiejie["name"]);//xiejie console.log(xiejie["age"]);//18
console.log(xiejie["gender"]);//male console.log(xiejie["score"]);//100
—般来讲,访问对象属性的时候使用点访问法的情况要多一些,那什么时候使用中括号访问方法 呢?当我们的属性名来自于变量的时候,这个时候中括号就要比点要灵活许多。来看下面的例 子:
let xiejie = {
name : "xiejie",
age : 18,
gender : "male",
score : 100
};
let str = "name"; console.log(xiejie[str]);//xiejie
既然讲到了对象属性的中括号访问法,那我们就顺带介绍一下伪数组对象的原理。前面给大家介 绍过的argument s就是一个伪数组对象。伪数组对象的原理就在于对象的键都是数字。如果属性 名是数字的话,通过中括号法来访问时可以不用添加引号,如下:
let obj = {
1 : "Bill",
2 : "Lucy",
3 : "David"
}
console.log(obj[1]);//Bill
console.log(obj[2]);//Lucy
console.log(obj[3]);//David
这样,就形成了给人感觉像是数组,但是并不是数组的伪数组对象。
- symbol访问法
在ES6之前,对象的属性名都只能是字符串。但是这样很容易造成属性名的冲突。比如我们使用 了一个别人提供的对象,然后我们想在这个对象的基础上进行一定的扩展,添加新的属性,这个 时候由于并不知道原来的对象里面包含哪些属性名,所以很容易就把别人的对象所具有的属性给 覆盖掉了。示例如下:
//假设person对象是从外部库引入的一个对象
let person = {
name : "xiejie"
}
console.log(person.name);//xiejie
person.name = "yajing"; console.log(person.name);//yajing
可以看到,这里两个name就产生了冲突,下面的name就把上面的name给覆盖掉了。
从ES6开始,新增了symbol这种数据类型,专门来解决这样的问题。创建symbol,需要使 用Symbol。函数,其语法如下:
let sym = Symbol(描述信息);
示例:
let name = Symbol("这是一个名字");
console.log(name);//Symbol(这是一个名字) console.log(typeof name);//symbol
这里的描述信息是可选的,是对我们自己创建的symb。l的一个描述。接下来我们来用symb。l作 为对象的属性,示例如下:
let person = {
name : "xiejie"
}
let name = Symbol("这是一个名字");
person[name] = "yajing";
console.log(person.name);//xiejie console.log(person[name]);//yajing
可以看到,使用symbol来作为对象的属性,避免了同名的属性名发生冲突。
有些时候我们希望在不同的代码中共享一个symbol,那么这个时候可以使用Symbol.for()方法 来创建一个共享的symbol。ES6提供了一个可以随时访问的全局symb。l注册表。当我们使 用Symbol.for()方法注册一个symbo l的时候,系统会首先在全局表里面查找对应的参数的 symbol是否存在,如果存在,直接返回已经有的symbol,如果不存在,则在全局表里面创建一 个新的symbol
let obj = {};
let name = Symbol.for("test");
obj[name] = "xiejie";
let name2 = Symbol.for("test"); console.log(obj[name2]);//xiejie
如果使用Symbol.for()方法创建symbo l的时候没有传递任何参数,那么也会将undefined作为 全局表里面的键来进行注册,证明如下:
let obj = {};
let name = Symbol.for();
obj[name] = "xiejie";
let name2 = Symbol.for(undefined); console.log(obj[name2]);//xiejie
ES6里面还提供了 Symbol.keyFor()方法来查找一个symbo l的键是什么。但是需要注意的是, 该方法只能找到注册到全局表里面的symbol的键。如果是通过Symbol。方法创建的symbol,是 无法找到的。这其实也很好理解,通过Symbol()方法创建的symbo l都不存在有键。
let obj = {};
let name1 = Symbol("test1");
let name2 = Symbol.for("test2");
let i = Symbol.keyFor(namel);
let j = Symbol.keyFor(name2);
console.log(i);//udnefined console.log(j);//test2
前面有提到,如果一个对象的属性对应的是一个函数,那么这个函数被称之为对象的方法。访问 对象方法的方式和上面介绍的访问对象属性的方式是一样的,可以通过点访问法,中括号访问法 以及symbo l访问法来进行对象方法的调用。
let walk = Symbol("this is a test"); let person = {
name : "xiejie", walk : function(){ console.log("I'm walking");
},
[walk] : function(){ console.log("I'm walking,too");
}
}
person.walk();//I'rn walking person["walk"]();//I'm walking person[walk]();//I'm walking,too
6-1-4删除对象属性
对象的任何属性都可以通过delete运算符来从对象中删除。示例如下:
let person = {
name : "xiejie",
age : 18,
walk : function(){
console.log("I'm walking");
}
}
console.log(person.age);//』8
delete person.age;//删除age 这个属性
console.log(person.age);//undefined
person.walk();//I'm walking
delete person.walk; //删除walk方法 person.walk();//指错
//TypeError: person.walk is not a function
如果是删除的是属性,那么再次访问值为变为undefined,而如果删除的是方法,那么调用时会 直接报错
6-1-5对象常用属性和方法
1. in操作符
该操作符用于判断一个对象是否含有某一个属性,如果有返回true,没有返回false。需要注意的 是目前为止还无法判断对象的symb。l属性的包含情况,如果属性是symbol,那么会直接报错
let person = {
name : "xiejie",
age : 18,
walk : function(){
console.log("I'm walking");
}
}
let gender = Symbol("person's gender");
person[gender] = "male";
console.log("name" in person);//true console.log("age" in person);//true console.log([gender] in person);//灰错
//TypeError: Cannot convert a Symbol value to a string
但是需要注意,虽然上面给出了无法转换的错误,不过如果我们按照下面的方式来查看的话,是
会返回true的
let person = {
name : "xiejie",
age : 18,
walk : function(){
console.log("I'm walking");
}
}
let gender = Symbol("person's gender");
person[gender] = "male"; console.log("name" in person); // true console.log("age" in person); // true console.log(gender in person); // true
2. for..in
这个for..in我们在前面讲解遍历数组的时候已经见到过了。可以使用for..in来取出数组的键。除此 之外,我们还可以使用for..in来循环遍历一个对象的所有属性,示例如下:
let person = {
name : "xiejie",
age : 18,
walk : function(){
console.log("I'm walking");
}
}
for(let i in person){
console.log(i);
}
// name
// age
// walk
需要注意的是,使用for..in虽然说可以遍历出一个对象的所有的属性和方法(包括继承的,关于继 承后面会介绍),但是无法遍历出用symbol来定义的属性,证明如下:
let person = {
name : "xiejie",
age : 18,
walk : function(){
console.log("I'm walking");
}
}
let gender = Symbol("person's gender");
person[gender] = "male";
for(let i in person){
console.log(i);
}
// name
// age
// walk
那么,这个时候可能有人会问了,那我如何才能遍历出一个对象的s ymbo l属性呢?这里介绍两种 方式。
第一种是使用Object.getOwnPropertySymbols()来返回一^对象所有的symbo l属性,如下:
let person = {
name : "xiejie",
age : 18,
walk : function(){
console.log("I'm walking");
}
}
let gender = Symbol("person's gender");
person[gender] = "male";
console.log(Object.getOwnPropertySymbols(person));
//[ Symbol(person's gender)]
除了上面的方法以外,ES6中还提供了一个叫做Reflect.ownkeys()方法。该方法可以遍历出 —个对象的所有类型的键名,包括字符串的键名以及symbo l键名
let person = { name : "xiejie",
age : 18,
walk : function(){
console.log("I'm walking");
}
}
let gender = Symbol("person's gender"); person[gender] = "male";
console.log(Reflect.ownKeys(person));
//[ 'name', 'age', 'walk', Symbol(person's gender)]
3. keys。,values。,entries。
前面在介绍遍历数组,集合以及映射的时候,有介绍过这3个方法,分别用于找出可迭代对象的 键,值,以及键和值。实际上,我们的对象也是属于可迭代对象的一种,所以也可以使用这3个 方法来找出对象的键和值
Object.keyO
let person = {
name : "xiejie",
age : 18,
walk : function(){
console.log("I'm walking");
}
}
let gender = Symbol("person's gender"); person[gender] = "male";
for(let i of Object.keys(person)){
console.log(i);
}
// name
// age
// walk
Object.valuesO
let person = { name : "xiejie", age : 18,
walk : function(){
console.log("I'm walking");
}
let gender = Symbol("person's gender"); person[gender] = "male";
for(let i of Object.values(person)){ console.log(i);
}
// xiejie
// 18
// [Function: walk]
Object.entriesO
let person = { name : "xiejie", age : 18,
walk : function(){
console.log("I'm walking");
}
}
let gender = Symbol("person's gender"); person[gender] = "male";
for(let i of Object.entries(person)){ console.log(i);
}
// [ 'name', 'xiejie' ]
// [ 'age', 18 ]
// [ 'walk', [Function: walk] ]
6-1-6嵌套对象
—个对象里面可以包含其他的对象,这个我们称之为对象的嵌套。示例如下:
let family = { xiejie : {
age : 18,
gender : "male"
},
song : {
age : 20,
gender : "female"
}
};
当我们访问嵌套对象里面的值的时候,和访问单个对象的方式是一样的。
let family = {
xiejie : {
age : 18, gender : "male"
},
song : {
age : 20, gender : "female"
}
};
console.log(family.xiejie.gender);//male 点访问法 console.log(family["song"]["age"]);//20 中括号访问法
对象的解构
在前面介绍解构的时候,我们有介绍过数组的解构。实际上对象我们也是可以将其解构的。解构 的方式也是和解构数组是类似的,示例如下:
let a = {name:"xiejie",age:18};
let b = {name:"song",age:20};
let {name:aName,age:aAge} = a; let {name:bName,age:bAge} = b; console.log(aName);//xiejie console.log(aAge);//18 console.log(bName);//song console.log(bAge);//20
当属性名和变量名一致的时候,可以进行简写,示例如下:
let a = {name:"xiejie",age:18};
let {name,age} = a;
console.log(name);//xiejie console.log(age);//18
和数组一样,同样可以解构嵌套的对象,如下:
let family = { xiejie : {
age : 18, gender : "male"
},
song : {
age : 20,
gender : "female"
}
};
let {xiejie,song} = family;
console.log(xiejie);//f age: 18, gender: 'male' } console.log(song);//f age: 20, gender: 'female' }
顺便一提的是,解构我们也是可以像函数一样设置一个默认值的,这里一起来看一下,如下:
let {name="xiejie",age} = {}; console.log(name);//xiejie console.log(age);//undefined
这里我们为name变量设置了一个默认值为xiejie,当我们解构一个空对象的时候,name变量的值 就使用了默认的xiejie这个值,而age这个变量由于没有设置默认值,所以值为undefined。
当然,既然叫做默认值,和函数一样,如果有解构的值传过来的话,肯定就是使用解构传过来的 值,如下:
let {name="xiejie",age} = {name:"song",age:10}; console.log(name);//song console.log(age);//10
6-1-7对象作为函数参数
对象字面量也可以作为函数的参数进行传递。这在有很多形式参数的时候非常有用,因为它允许 我们在调用函数时不用记住参数的顺序。
我们先来看一下我们一般函数调用的例子,如下:
let test = function(name,age){
console.log('我叫${name},我今年${age}岁');
}
test("xiejie", 18);//我叫xiejie,我今年 18岁 test(18,"xiejie");//我叫 18,我今年xiejie岁
可以看到,以前我们调用函数传递参数时,参数的顺序是必须要和形式参数的顺序一致的。否则 就会出现上面第2次调用函数的情况,输出不符合预期的结果。
当我们使用对象字面量来最为形式参数时,就可以不用按照定义函数时形式参数的顺序,只要名 字相同即可,如下:
let test = function({name,age}){
console.log('我叫${name},我今年${age}岁');
}
test({name:"xiejie",age:18});我今年 18 岁 test({age:18,name:"xiejie"});//^^AXiejie,我今年 18 岁
可以看到,只要参数名能对上号,位置是可以随意放置的。
和前面介绍函数的参数默认值一样,当对象字面量作为参数的时候,我们也可以为字面量的每一 项设置一个默认值,甚至我们还可以给整个对象字面量一个默认值,示例如下:
//给整个对象字面量一个默认值{ name:"BilT,age:20}
let test = function({name = "xiejie",age = 18} = {name:"Bill",age:20}){ console.log('我叫${name},我今年${age}岁');
}
test();//我叫Bill,我今年20岁
test({});//我叫xiejie,我今年 18岁 test({name:"yajing"});//我〃V/ajing,我今年 18 岁 test({age:1,name:"xizhi"});//我〃AXizhi,我今年 1 岁
这种技术被称之为命名参数,经常被用在函数有很多可选参数的时候。
6-1-8 this关键字
既然学习到了对象,那么有必要介绍一下this关键字。this ,翻译成中文就是这个的意思。 当我们在一个对象中使用this关键字时,该关键字就代表的是当前对象。来看下面的例子:
let person = {
name : 'xiejie',
age : 18,
intro : function(){
console.log(this);
// { name: 'xiejie', age: 18, intro: [Function: intro] } console.log('My name is ${this.name},I'm ${this.age} years old'); // My name is xiejie,I'm 18 years old
}
}
person.intro();
这里我们调用了 person对象的intro方法,里面涉及到了 this关键字。由于是在对象里面,所 以this指向当前对象,也就是person这个对象。所以this.name等价
于 person.name , this.age 等价于 person.age。
6-1-9命名空间(扩展)
当相同的变量和函数名被共享在同一作用域的时候,就会发生命名冲突。这看起来不太可能,但 是我们可以想象一下,随着时间的推移,我们已经写了很多的代码,可能不知不觉就重用了一个 变量名。如果是使用的其他开发者的代码库,这种问题就变得更加有可能。
解决命名冲突的方式,就是使用对象字面量来为一组相关函数创建一个命名空间。这样在调用这 些函数的时候需要先写上对象名,这里的对象名就充当了命名空间的角色。示例如下:
let myMaths = {
//求平方函数
square : function(x){
return x * x;
},
//传入数组求平均值函数
avg : function(arr){
let total = arr.reduce((a,b) => a + b);
return total / arr.length;
}
}
let arr = [1,2,3,4,5];
console.log(myMaths.avg(arr));//3
console.log(myMaths.square(5));//25
这里我们的myMaths就是我们的命名空间,这样就不用担心和其他人的变量或者函数名发生命 名冲突。