对象是 JavaScript 数据类型的一种,之前已经学习了数值类型、字符串类型、布尔类型、undefined。对象数据类型可以被理解成是一种数据集合。
对象数据类型,有点类似于数组,它是多个数据的集合,不同的是,对象内部的数据,都是对这个对象的描述,先为大家展示一个对象:
let person = {
name: "张三",
age: 18,
dance: function(){
document.write("张三会跳舞");
},
cook: function(){
document.write("张三会做饭");
}
}
这是一个名为person的对象,它内部有四个属性,分别是name,age,以及cook函数,dance函数。这四个属性都是对这个person对象的描述,代表其名字为张三,十八岁,会做饭,会跳舞。
其中,后两个函数,一般不称为属性,而称为方法。
对象由其内部的属性和方法组成,属性就是一些基础数据,而方法本质就是一些函数,当我们使用这个对象的时候,就可以执行它的某一个函数,比如我们在刚刚的对象中,可以调用它的函数dance,这样屏幕就会输出一句”张三会跳舞“。可以把输出这句话的功能,称之为这个对象的方法。
那么我们要如何声明一个对象?我们有两种方式:直接声明和利用函数声明。
语法:let 对象名 = new Object()
由于对象也是一个数据类型,和其它数据类型一样,需要使用let,var,const这种关键字。
这种函数方法其实不常用,以上语法会得到一个空对象,也就是其内部没有任何属性,不过后续有增加属性的方法,不必担心,在此不对此方法过多赘述了。
语法:
let 对象名 = {
属性1: 属性值1,
属性2: 属性值2
}
这种直接声明的方式,和数组十分类似,我们只需用一个大括号把对象的属性括起来,每两个属性之间用逗号隔开即可。这样我们就可以得到一个初始化过的对象,当然,你也可以一个属性值都不写,直接得到一个空对象。
既然我们得到了一个对象,我们就需要访问这个对象的值,就好比得到了一个数组,我们也需要去访问数组的元素。
访问对象有两种方法:对象名.属性
和对象名['属性']
。
对象名.属性
:
示例:
我们在此访问person这个对象,获取它的name属性,通过对象名.属性
,即person.name
就可以得到张三
这个值。
对象名['属性']
:
我们再利用另一个方法访问一下age这个属性:
可以发现我们一样访问到了属性的值,这里要十分注意[]内部的属性要加上引号,单引号或双引号都可以。
那么为什么需要两种方法呢?
我们为person这个对象在加一个属性:previous-name:"张四"
来代表曾用名:
报错了,说明这个语法是有问题的,当我们的属性名字太长,需要使用特殊符号来区分的时候,需要为其加上引号:
加上引号后,这个报错就消失了,此时的previous-name:"张四"
是合法的。
尝试以不同方式访问这个previous-name
:
这里一共用了四种方式访问,主要问题在于,访问时要不要加引号?用哪种方式访问?
首先person.'previous-name'
已经报错,毫无疑问是不行的,删除后再执行一次:
运行后,程序报错了:第25行的previous
没有定义,这里不加引号就已经违背基础语法了,报错是毫无疑问的。把它注释掉,再执行一次:
用一个点访问的方式全军覆没了,不论加不加引号,解析都会有问题。
最后得出结论,这种带有特殊字符,需要用引号引起的属性名,必须使用中括号的形式来访问。
两种方法都有自己的应用面,都非常重要。
我们刚刚的访问,都是基于对象的属性,没有访问对象的后两个方法,因为方法的本质是函数,函数调用又是额外的语法:对象名.方法名(函数参数)
或对象名['方法名'](函数参数)
。
分析一下,其实不难发现,相比于访问对象的属性,调用对象的方法只是在访问完毕后,加上一个调用函数的小括号而已,而函数的传参是在小括号里面也不难理解。
示例:
最后函数都正常执行了,也就是方法成功调用了。
增加或改变对象的属性,语法是完全一致的,只需要直接赋值即可,当被赋值的属性在对象中存在,那么可以直接修改;如果被赋值的属性在对象中不存在,那么就会增加这个属性。
示例:
原先age是存在的,于是person.age = 20;
就直接修改了age属性的值;
原先weight不存在,于是person.weight = "100kg";
增加了该属性。
删除对象的属性,需要用到关键字delete
,语法:delete 对象名.属性名
或者delete 对象名['属性名']
,这样就能删除对应的对象。
示例:
最后,age属性和previous-name属性都被删掉了。
对象和数组很像,我们在遍历数组的时候,往往是利用下标来一个个访问,但是对象没有下标,该如何访问?
首先介绍一个for in
语句,它可以遍历数组:
我们利用for in
语句,可以在每一次循环时,为k一次赋值一个下标,那么我们遍历数组只需要arr[k]
就可以了:
我们成功输出了这个数组的所有值。
这个for in
语句对对象同样生效:
我们利用for in
语句,让k每次循环,都得到一个属性名,但是,我们检测k的类型后,发现它是string类型。这说明,k并不直接等于属性名,而是属性名的字符串形式,也就是 k = “属性名” 。这十分重要,因为这意味着我们无法通过属性名.k
的形式来获得对应的属性,因为自带一对引号。所以我们要使用属性名[k]
的形式,由于k自带引号,无需在对k加引号。
示例:
这样我们就通过for in
语句遍历了整个对象的所有属性。
对象不仅可以我们自己创建,JavaScript还提供了丰富的内置对象,我们需要时可以直接调用。
回想一下我们曾经使用过的 console.log
,console
其实就是 JavaScript 中内置的对象,该对象中存在一个方法叫 log
,然后调用 log
这个方法,即 console.log()
。
除了 console
对象外,JavaScritp 还有其它的内置的对象
在此我介绍一个Math
对象。
Math
是 JavaScript 中内置的对象,称为数学对象,这个对象下即包含了属性,也包含了许多的方法。
Math.PI
,获取圆周率console.log(Math.PI);
调用得到:3.141592653589793
Math.random
,生成 0 到 1 间的随机数Math.random()
0 ~ 1 之间的随机数, 包含 0 不包含 1
Math.ceil
,数字向上取整Math.ceil(3.4)
舍弃小数部分,整数部分加1
Math.floor
,数字向下取整Math.floor(4.68)
舍弃小数部分,整数部分不变
Math.round
,四舍五入取整Math.round(5.46539)
Math.round(4.849)
取整,四舍五入原则
Math.max
,在一组数中找出最大的Math.max(10, 21, 7, 24, 13)
Math.min
,在一组数中找出最小的Math.min(24, 18, 6, 19, 21)
Math.pow
,幂方法Math.pow(4, 2) // 求 4 的 2 次方
Math.pow(2, 3) // 求 2 的 3 次方
求某个数的多少次方
Math.sqrt
,平方根Math.sqrt(16)
求某数的平方根
数学对象提供了比较多的方法,这里不要求强记,通过演示数学对象的使用,加深对对象的理解。
在此我们特地讲解一下生成指定随机数的方法:
随机数生成方法:
在调用random方法的时候,会得到一个[0,1)的随机数,如果我们对其乘以N,那么就能得到[0,N)的随机数,也就是Math.random() * N
。
但是这样的随机数是包括小数的,此时我们就需要对这个结果取整,得到[0,N)的随机整数,即Math.floor(Math.random() * N)
。
如果我们想得到[M,N]的整数,就是Math.floor(Math.random() * (N - M)) + N
,在此不过多推算了。
JavaScript中,主要有两个内存区域:栈区和堆区。而不同的数据类型,对内存的使用是不同的,主要区别为简单类型和复杂类型。
对于简单类型的数据,比如Number,Boolean,String,Undefined。
它们数据存储于栈区中,我们使用的变量,本身指向这个数据。
对于这样一串代码:
let a = 5;
let b = false;
let c = undefined;
let d = "1234";
对于复杂数据类型,比如对象,它会由两部分组成,分别是栈区的变量索引,以及堆区的数据实体。
比如对于下面的代码:
let person = {
name: "张三",
age: 18,
}
那么这两种内存分配有啥区别?
首先明确一点,我们操作的变量,其数据是栈区的数据。
当我们进行变量赋值的时候,就会发生区别:
let a = 5;
let b = a;
这个过程中,a在栈区中的数据是5,于是b就得到5这个值:
而对于复杂数据类型:
let person = {
name: "张三",
age: 18,
}
let person2 = person;
person变量在栈区中的数据是指向堆区的索引,于是person2就得到了这个索引:
由于索引与person一致,所以最终person2和person指向同一个对象。
执行以下代码
let person = {
name: "张三",
age: 18,
}
let person2 = person;
delete person2.name;
console.log(person);
输出结果如何呢?
由于person2与person指向同一个对象,最后对person2改变,person也会改变:
最后person的name属性也没有了。