目录
一、对象的初识
(一)对象的定义
(二)对象的意义
(三)对象的分类
二、创建对象:利用 new Object 创建对象
(一)对象的基本操作
(二)属性名与属性值
(三)基本和引用数据类型
三、创建对象方式二
(一)利用字面量创建对象
(二)对象的方法
(三)枚举(遍历)对象属性
(四)区分变量和属性,函数和方法
(五)this 的初印象
四、创建对象方式三
五、创建对象方式四
(一)利用构造函数创建对象
(二)构造函数(new 关键字)的执行流程
(三)对象的实例化
(四)instanceof
(五)构造函数优化
在 JavaScript 的编程世界里,对象是极为关键的概念。让我们一同深入探索对象的奥秘。
一、对象的初识
(一)对象的定义
在现实生活中,世间万物皆可看作对象。它是具体的事物,是那些我们能亲眼看到、亲手触摸到的实体。像一本书、一辆汽车、一个人,无疑都是对象;而在数字化领域,一个数据库、一张网页,甚至是与远程服务器的连接,同样也属于对象的范畴 。
比如,明星、女朋友、班主任、苹果、手机这些都是对象的示例,进一步具体化,周星驰、小明的女朋友、这个班的班主任、这个被咬了一口的苹果、小王的手机,分别对应着不同的特定对象。
在 JavaScript 中,对象被定义为一组无序的相关属性和方法的集合。值得注意的是,几乎所有的事物在 JavaScript 里都可被视为对象,像常见的字符串、数值、数组、函数等。
属性,用于描述事物的特征,在对象中以属性的形式呈现,通常用名词来表示。以手机对象为例,其属性包括大小、颜色、重量、屏幕尺寸、厚度等。
方法,则体现了事物的行为,在对象中用方法来表示,多为动词。例如手机具有打电话、发短信、聊微信、玩游戏等方法 。
再比如桌子,其属性有长宽、材质、颜色;方法包括放东西、写字、吃饭。电脑的属性有 cpu、显卡、固态、颜色、尺寸;方法有写代码、看视频、开会议、聊天 。
(二)对象的意义
当我们仅需保存一个值时,使用变量就足够了。若要保存多个值(即一组值),数组是不错的选择。然而,数组存在一定的局限性,数组元素之间的信息缺乏连贯性和紧密连接,表达不够清晰。
设想一下,若要完整保存一个人的信息,数组可能无法清晰地展现各信息之间的关系。而 JavaScript 中的对象,其表达结构更加清晰,功能也更为强大,能够很好地解决这个问题。
(三)对象的分类
对象属于一种复合的数据类型,在对象中可以保存多个不同数据类型的属性,这使得信息与信息之间建立起联系,关系明确,操作起来也更加便捷。
内建对象:由 ES 标准定义的对象,只要是符合 ES 标准的实现,都能使用这些对象。例如 Math、String、Boolean、function、object 等。宿主对象:由 JavaScript 的运行环境提供的对象。在当前的环境下,主要指浏览器提供的对象,如 BOM(浏览器对象模型)和 DOM (文档对象模型) 。这两组对象中各自包含了众多的具体对象。自定义对象:由开发人员根据实际需求自行创建的对象。
二、创建对象:利用 new Object 创建对象
(一)对象的基本操作
创建对象:使用 new 关键字调用的函数,被称为构建函数 constructor。构建函数的使命就是专门用来创建对象。当我们使用 typeof 检查一个对象时,它会返回 object。
例如:
var obj = new Object();
向对象添加属性:采用对象.属性名=属性值的方式向对象添加属性。
obj.name = "张三";//向obj中添加一个name属性,name属性值为'张三'
obj.gender = "男";//向obj中添加一个gender属性
obj.age = 18;//向obj中添加一个age属性
需要注意的是,我们通过=赋值运算符来添加对象的属性和方法,并且每个属性和方法之间要用分号;结束。
修改对象属性:按照对象.属性名=新值的形式修改对象属性。
obj.name = "孙悟空";
删除对象的属性:利用delete 对象.属性名来删除对象的属性。
delete obj.name;//删除name属性
console.log(obj.name);//undefined
(二)属性名与属性值
属性名:
对象的属性名并不强制要求遵守标识符号的规范,也就是说可以使用任何名字。但为了代码的规范性和可读性,我们在使用时最好还是遵循标识符的规范。倘若要使用特殊的属性名,比如数字,不能通过对象.属性名的方式来操作,而需要使用对象名['属性名']来读取。使用[]这种形式操作属性更为灵活,因为在[]中可以直接传递一个变量,如此一来,变量值是多少就会读取对应的属性。
var obj = new Object();
obj.name = "tom"; //向对象中添加属性
obj["123"] = "你好";
var n = "123"; //把'123'属性赋值给n
console.log(obj["123"]); //读取obj的'123'属性
console.log(obj[n]); //读取obj的'123'属性
属性值:JavaScript 对象的属性值可以是任意的数据类型,甚至可以是对象。
obj.test = true;
obj.test = undefined;
obj.test = "true";
obj.test = 123;
// 创建一个对象
var obj2 = new Object();
obj2.name = "猪八戒";
obj.test = obj2;
in 运算符:借助该运算符能够检查一个对象中是否含有指定的属性,如果存在则返回 true,不存在则返回 false。其语法为"属性名" in 对象。
//检查obj中是否含有test2属性
console.log("test2" in obj);//false
console.log("test" in obj);//true
(三)基本和引用数据类型
基本数据类型:包括 String、Number、Boolean、Null、Undefined。引用数据类型:主要是 Object。在 JavaScript 中,变量都是存储在栈内存中的。基本数据类型的值直接在栈内存中存储,每个值都是独立存在的,修改一个变量的值不会对其他变量造成影响。而引用数据类型的值是保存在堆内存中的,每创建一个新的对象,都会在堆内存中开辟一个新的空间。变量保存的是对象的内存地址(即对象的引用),若两个变量保存的是同一个对象引用,那么当通过其中一个变量修改对象属性时,另一个变量也会受到影响 。比较两个基本属性类型的值时,比较的是值本身;而比较两个引用数据类型时,比较的是对象的内存地址。即便两个对象的内容完全相同,但只要地址不同,比较结果就会返回 false。
//基本类型值存储在栈内存中
var a = 123;
var b = a;
a++;
console.log("a=" + a);//124
console.log("b=" + b);//123 ,因为b的变化不会影响到a
// 引用类型值存储在堆内存中,栈内存中保存的是指向堆内存的地址
var obj = new Object;
obj.name = 'java';
var obj2 = obj
// 修改obj的name值
obj.name = 'web'
console.log(obj.name);//web
console.log(obj2.name);//web
//因为obj和obj2指向同一个对象
三、创建对象方式二
(一)利用字面量创建对象
对象字面量,就是在花括号{}里面包含表达这个具体事物(对象)的属性和方法。
var obj = {};//创建一个空对象
var obj2 = {
name: "猪八戒",
age: 28,
gender: "男",
sayHi: function () {
console.log('hi~');
}
};
需要留意的是,对象内的属性或者方法以键值对的形式呈现,即键:值,也就是属性名:属性值。多个属性或者方法中间用逗号隔开,最后一个属性或者方法的逗号可以省略。方法冒号后跟的是一个匿名函数。
读取对象:
调用对象的属性:可以使用对象名.属性名或者对象名['属性名']。调用对象的方法:采用对象名.方法名()。
//读取对象属性
console.log(obj2.name);
console.log(obj2['age']);
//读取对象方法
obj2.sayHi();
(二)对象的方法
函数也能够成为对象的属性。当一个函数作为对象的属性保存时,我们将这个函数称作这个对象(obj)的方法。调用函数也就意味着调用对象(obj)的方法(method),实际上它和函数在本质上并无太大区别,只是名称有所不同。
/* 创建对象一*/
var obj = new Object();
//向对象中添加属性
obj.name = "孙悟空";
obj.age = 18;
//对象的属性值可以是任何的数据类型,也可以是个函数
obj.sayName = function () {
console.log(obj.name);
};
console.log(obj.sayName);//打印函数属性
obj.sayName();//调用obj.sayName()函数。也叫调用obj的sayName方法
/* 创建对象二 */
// function sayHi(){
// console.log('hallo')
// }
var obj2 = {
name: '小明',
age: 18,
sex: '男',
job: 123,
// 第一种写法
// sayHi:sayHi,(之前定义好的函数) //方法:函数名
// 第二种写法
// sayHi:function(){ //方法:匿名函数
console.log(obj2.name)
// 第三种写法es6
sayHi() {
console.log('hallo')
}
};
console.log(obj2.name)
console.log(obj2['sex'])
console.log(obj2.sayHi)
obj2.sayHi()
(三)枚举(遍历)对象属性
使用for...in语句可以实现对对象属性的枚举。其语法为for(var 变量 in 对象){ }。for...in语句会根据对象中属性的数量来决定循环体的执行次数。每次执行循环时,会将对象中的一个属性的名字赋值给变量。
var obj = {
name: "孙悟空",
age: 18,
gender: "男",
address: "花果山"
};
console.log(obj.name)
for (var n in obj) {
console.log('属性名:' + n)//对象中每个属性的名字赋值给n
console.log('属性值:' + obj[n]);//通过变量n读取属性值, []读取变量
}
(四)区分变量和属性,函数和方法
变量和属性:变量和属性都用于存储数据。变量需要单独声明并赋值,使用时直接写变量名,它是独立存在的;而属性存在于对象中,无需单独声明,使用时通过对象.属性的方式,依托对象而存在。函数和方法:函数和方法都用于实现某种功能,都可以用来做某件事。函数是独立存在的,而方法则是依托对象存在的。
(五)this 的初印象
解析器在调用函数时,每次都会向函数内部传递一个隐含的参数,这个参数就是 this。this 是由浏览器传递的,我们可以直接使用。this 指向的是一个对象,这个对象被称为函数执行的上下文对象。
根据函数调用方式的不同,this 会指向不同的对象:
当以函数的形式调用时,this 指向 window。当以方法的形式调用时,谁调用方法,this 就指向谁。当以构建函数的形式调用时,this 指向新创建的那个对象,即当前对象 。
//需求:可以输出各自obj的名字
//创建一个fun()函数
function fun() {
console.log(this.name);
}
//创建两个对象
var obj = {
name: "孙悟空",
sayName: fun,
};
var obj2 = {
name: "沙和尚",
sayName: fun,
};
var name = "全局的name属性";
fun(); //==window.fun() 以函数的形式调用,this就是window
obj.sayName(); //孙悟空 以方法的形式调用,this是调用方法的对象
obj2.sayName(); //沙和尚
四、创建对象方式三
使用工厂方法创建对象,能够实现大批量地创建对象。
//需求:创建一个对象
var obj = {
name: "孙悟空",
age: 18,
gender: "男",
sayName: function () {
alert(this.name);
},
};
/* 需求:批量的创建对象
使用工厂方法创建对象
*/
//函数里面加对象,靠参数生产
function createPerson(name, age, gender) {
//创建一个新的对象
var obj = new Object();
//向对象中添加属性
obj.name = name;
obj.age = age;
obj.gender = gender;
obj.sayName = function () {
alert(this.name);
};
//将新的对象返回
return obj;
}
var obj2 = createPerson("猪八戒", 28, "男");
var obj3 = createPerson("沙和尚", 38, "男");
var obj4 = createPerson("白骨精", 18, "女");
然而,使用工厂方法创建的对象,其构造函数都是 Object,这就导致创建的对象均为 Object 类型,使得我们难以区分多种不同类型的对象。
五、创建对象方式四
(一)利用构造函数创建对象
构造函数是一种特殊的函数,主要用于初始化对象,即为对象成员变量赋予初始值,它通常与 new 运算符一起使用。我们可以将对象中一些公共属性和方法抽取出来,封装到这个函数里面。
构造函数语法规范如下:
function 构造函数名() {
this.属性 = 值;
this.方法 = function () {}
}
new 构造函数名();
示例:
function Person(name, age, gender) {
//alert(this);//this就是新建的对象,然后赋值给per
this.name = name;
this.age = age;
this.gender = gender;
this.sayName = function () {
alert(this.name)
};
}
var per = new Person("孙悟空", 18, "男");
var per2 = new Person("玉兔精", 16, "女");
var per3 = new Person("沙和尚", 28, "男");
console.log(per);
console.log(per2);
console.log(per3);
需要注意的是,构造函数本质上就是一个普通的函数,其创建方式与普通函数并无差异,不同之处在于构造函数习惯上首字母大写。构造函数和普通函数的区别主要体现在调用方式上,普通函数直接调用,而构造函数需要使用 new 关键字来调用。在构造函数中创建属性和方法时,必须结合 this 使用,此时的 this 指向的就是新创建的那个对象。
(二)构造函数(new 关键字)的执行流程
立刻创建一个新的对象。将新建的对象设置为函数中 this,在构造函数中可以使用 this 来引用新建的对象。逐行执行函数中的代码。将新建的对象作为返回值返回。
当使用new创建一个对象时,会在堆中开辟一片区域,例如new Person()会在堆中开辟一块区域。
(三)对象的实例化
使用同一个构造函数创建的对象,我们将其视为一类对象,同时也将这个构造函数称为一个类。例如明星汽车设计图纸,就如同一个类。通过一个构造函数创建的对象,则被称为该类的实例,比如刘德华、某一辆宝马车。利用构造函数创建对象的过程,我们称之为对象的实例化。
(四)instanceof
instanceof的作用是检查一个对象是否是一个类的实例,就像是进行 “亲子鉴定”。其语法为对象 instanceof 构造函数,如果是,则返回 true,否则返回 false。需要注意的是,所有的对象都是 Object(对象源头)的后代,所以任何对象与instanceof进行检查时,都会与 Object 存在这种关系。
console.log(dog instanceof Person);
(五)构造函数优化
在前面的示例中,我们创建的 Person 构造函数,为每一个对象都添加了一个 sayName 的方法。当前的实现方式是在构造函数内部创建方法,这就意味着构造函数每执行一次,就会创建一个新的 sayName 方法,所有实例的 sayName 方法都是唯一的。这会导致构造函数执行多次时,创建大量重复的方法,造成不必要的资源浪费。实际上,完全可以让所有的对象共享同一个方法。
function Person(name, age, gender) {
this.name = name;
this.age = age;
this.gender = gender;
// 向对象中添加一个方法
this.sayNam = fun;
}
//将sayName方法在