一、引言
对象是JavaScript中的核心概念之一,理解对象、构造函数及包装类等相关知识对于深入理解掌握JavaScript至关重要,接下来我们将展开讲讲那些在JavaScript中我们不知道的关于“对象”的知识点。
二、万物皆对象
JS中,“万物皆对象”这一理念贯穿始终。JavaScript的数据类型可分为原始类型和引用类型。
🔍 原始类型:String(字符串)、Number(数字)、Boolean(布尔值)、undefined、null、symbol、bigint
🔍 引用类型:function(函数)、array(数组)、object(对象)
原始类型直接储存在栈内存中,引用类型的值存储在堆内存中,栈内存中存储的是指向堆内存中实际数据的引用。
三、 创建对象的方式
(一) 创建对象字面量
对象字面量是创建对象最简洁的方式,通过 花括号 {} 来定义对象,并在其中使用键值对来表示对象的属性和值。
示例:
let person = {
name: "张三",
age: 18,
sayHello: function() {
console.log("Hello!");
}
};
该示例中,person对象有两个属性name和age,及一个方法sayHello。
(二)new Object()
使用 new Object()也可以创建一个空对象,然后再为其添加属性和方法。
示例:
let newObj = new Object();
newObj.name = "张三";
newObj.age = 35;
newObj.sayHi = function() {
console.log("Hi!");
};
这种方法较为繁琐,不如对象字面量简洁直观,但在某些动态创建对象场景中有其独特的用途。
(三)new 调用自定义的构造函数
当需要批量创建具有相似结构和行为的对象时,构造函数就派上了用场。构造函数本质上是一个普通函数,但当它被new关键字调用时,就用于创建对象实例。
示例:
function Person(name, age) {
this.name = name;
this.age = age;
this.sayGoodbye = function() {
console.log("Goodbye!");
};
}
let p1 = new Person("张三", 17);
let p2 = new Person("李四", 18);
上述示例中,Person是一个构造函数,通过new Person()可以创建多个Person对象实例,每个实例都有自己的name、age属性和sayGoodbye方法。
四、关于‘构造函数’
(一)构造函数的作用
构造函数主要用于批量创建对象,它可以为对象示例初始化属性和方法。当我们需要创建多个具有相同属性和行为的对象时,使用构造函数可以提高代码的复用性和可维护性。
(二)构造函数的调用方式
当一个函数被new关键字调用时,它就成为了构造函数。
示例:
function Car(make, model) {
this.make = make;
this.model = model;
this.drive = function() {
console.log("Driving the " + this.make + " " + this.model);
};
}
let myCar = new Car("Toyota", "Corolla");
myCar.drive();
上述示例中,Car函数在被new调用后,创建了一个Car对象实例myCar,并为其初始化了make和model属性以及drive方法。
(三)普通函数与构造函数的区别
普通函数与构造函数在本质上并没有区别,它们都是函数。区别在于调用方式,当函数被直接调用时,它是普通函数;当被new调用时,它就是构造函数。
示例:
function greet() {
console.log("Hello!");
}
greet(); // 普通函数调用
function Animal(name) {
this.name = name;
}
let dog = new Animal("小茂密"); // 构造函数调用
在上述示例中,greet是普通函数,直接调用执行打印操作;Animal函数在被new调用时,用于创建Animal对象实例。
五、在构造函数中,new做了什么
当使用new关键字调用构造函数时,会发生以下几个步骤:
(一)创建一个this对象
new操作符首先会创建一个空的对象,这个对象就是this所指向的对象,它将作为新创建对象的实例。
(二)执行构造函数中的代码
将this对象绑定到构造函数的执行环境中,然后执行构造函数中的代码,为this对象添加属性和方法。
function House(rooms, floors) {
this.rooms = rooms;
this.floors = floors;
this.showDetails = function() {
console.log("This house has " + this.rooms + " rooms and " + this.floors + " floors.");
};
}
let myHouse = new House(5, 2);
myHouse.showDetails();
在上述示例中,new House()调用时,构造函数为myHouse对象添加了rooms、floors属性和showDetails方法。
(三)return这个this对象
最后,构造函数默认返回this对象,即新创建的对象实例。如果构造函数中显式地返回了一个非原始类型的值(如另一个对象),则new操作符返回的是这个显式返回的值,而不是this对象。
六、包装类
(一)包装类的概念
在JavaScript中,当用户定义一个原始类型的字面量时,V8引擎默认会执行new XXX()操作,将其包装成对应的包装类实例。例如,字符串字面量会被包装成String对象,数字字面量会被包装成Number对象,布尔值字面量会被包装成Boolean对象。
实例:
let strLiteral = "world";
console.log(strLiteral.length);
// 虽然 strLiteral 是原始类型字符串,但可以调用 length 属性,因为被包装成了 String 对象
(二)包装类的自动拆箱与装箱
🔍 一个包装类的实例对象,在参与运算时会自动拆箱成原始类型。
示例:
let numObj = new Number(5);
let result = numObj + 3; // numObj 自动拆箱为原始类型数字 5,然后进行加法运算
console.log(result); // 8
🔍 当需要使用包装类的属性和方法时,原始类型会自动装箱成包装类对象。
示例:
let bool = true;
let boolObj = bool.valueOf(); // 将布尔值装箱为 Boolean 对象,以便使用其方法
console.log(boolObj.toString()); // "true"
(三)弱类型语言与包装类的特性
由于 JavaScript 是弱类型语言,只有在赋值语句执行时才会判断值的类型。当值被判定为原始类型时,会自动将包装对象上添加的属性移除。
示例:
let str = "test";
str.foo = "bar";
console.log(str.foo); // undefined,因为 str 是原始类型字符串,添加的属性被移除
上述示例很好地体现了JS中包装类的动态特性和弱类型语言的特点。
八、总结
至此,所有关于对象的知识点都尽数了解,理解万物皆对象的理念,掌握创建对象的不同方式,深入了解构造函数的原理,new操作符的执行过程和包装类的特性,我们对JaveScript的掌握将更进一步。