一、JavaScript中对象
1、对象的创建和访问
(1)直接创建
这种方式是直接给定对象的属性和值,用一个变量接收,这个变量指向的就是此对象,只能创建单个对象。
// (1)直接创建一个对象
var person = {
name: "John",
age: 30,
occupation: "Engineer",
// 添加一个方法
sayHello: function () {
console.log("Hello, my name is " + this.name);
},
};
// 访问对象的属性
console.log(person.name); // 输出: John
// 修改对象的属性值 并打印
person.age = 35;
console.log(person.age); //打印35
// 调用对象的方法
person.sayHello(); // 输出: Hello, my name is John
(2)通过构造函数创建
这种方式是先创建一个构造函数,可以是一类事物的共性特征、行为等,后续通过调用构造函数,传递参数来生成不同的对象。
// (2)构造函数创建
function Person(name, age) {
this.name = name;
this.age = age;
this.sayHello = function () {
console.log("Hello, my name is " + this.name);
};
}
// 使用构造函数创建不同对象
var person1 = new Person("John", 30);
var person2 = new Person("Jane", 25);
// 调用对象的方法
person1.sayHello(); // 输出: Hello, my name is John
person2.sayHello(); // 输出: Hello, my name is Jane
</script>
二、对象原型、原型链
1、构造函数、原型对象、实例对象三者的关系
(1)构造函数
每个构造函数都有一个内置属性prototype,这个属性表示该构造函数的原型,指向一个对象,该称为对象的原型对象。
(2)原型对象
一般存放的是这个构造函数的共有属性和方法,即该构造函数实例对象能访问到的所有属性、方法。
(3)实例对象
实例对象是通过构造函数生成的对象实例,每一个对象实例中,都含有“__proto__”属性,表示此对象的对象原型,指向的是该实例对象构造函数的原型对象,即本对象拥有的属性、方法所在的位置。
同时,在原型对象prototype和对象实例中,都含有一个constructor属性,指向的是他们所属的构造函数,表明他们是属于哪一个构造函数。
2、原型链
在JavaScript中,对象的继承可以通过原型链来体现。
原型链是一种对象之间的链接关系,每个对象都有一个指向其所属原型的属性“__proto__”。当访问一个对象的属性或方法时,如果该对象本身没有该属性或方法,会沿着原型链向上查找,直到找到该属性或方法或者到达链的末端。
案例:
子类对象访问父类构造函数原型对象的方法
function Parent() {
this.name = "Parent";
}
Parent.prototype.sayHello = function () {
console.log("Hello, " + this.name);
};
function Child() {
Parent.call(this);
this.name = "Child";
}
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
var child = new Child();
child.sayHello(); // 输出 'Hello, Child'
三、深浅拷贝
当我们需要复制一个数据对象时,就会涉及到对象的克隆。
如果是简单字面量的数据,可以直接赋值给新变量,因为是简单的值赋值,并不会对原有数据产生影响。
但当我们要复制的对象时引用数据类型的时候,直接赋值的方式就不再适用了,因为引用数据类型赋值给其他变量传递的是该对象的地址,此时两个变量指向的都是该对象的地址,任何一个变量发生改变,另一个变量都会同步变化。
对于引用数据类型的赋值,就不能采用简单赋值的方法了。
1、浅拷贝(只能拷贝单层)
前边说到,引用数据类型的直接赋值拷贝方式,传递的是该对象的地址,那么,在拷贝引用数据类型的时候,我们不采用直接赋值,而是将地址里的值作为副本给到新变量对象不就解决了吗?
浅拷贝的原理就是这样,不采用变量直接赋值,而是传递对象值副本。
// 使用Object.assign()进行浅拷贝
var obj1 = { name: "Alice", age: 25 };
var obj2 = Object.assign({}, obj1);
obj2.name = "Bob";
console.log(obj1); // { name: 'Alice', age: 25 }
console.log(obj2); // { name: 'Bob', age: 25 }
// 使用展开运算符进行浅拷贝
var obj1 = { name: "Alice", age: 25 };
var obj2 = { ...obj1 };
obj2.name = "Bob";
console.log(obj1); // { name: 'Alice', age: 25 }
console.log(obj2); // { name: 'Bob', age: 25 }
2、深拷贝
浅拷贝有一个局限性,就是只能拷贝一层的数据。
当对象的属性中出现了引用数据类型时,浅拷贝的结果就是,引用数据类型属性传递的依旧是一个地址引用,该属性的变化还是会和原对象同步。
因此,浅拷贝只适用于对象不存在引用数据类型属性的情况。
深拷贝是在浅拷贝的基础上,在拷贝每一个属性的时候,都对该属性的数据类型进行了判断,如果是基本数据类型,直接拷贝;如果是引用数据类型,则会创建该类型的对象,对该属性进行递归拷贝,直到拷完最底层。
深拷贝的几种方法:
(1)递归拷贝
//深拷贝
function deepClone(obj) {
// 检查如果是基本类型或者null,则直接返回
if (typeof obj !== "object" || obj === null) {
return obj;
}
// 创建一个新的对象或数组
var clone = Array.isArray(obj) ? [] : {};
// 遍历原对象的属性
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
// 递归调用深拷贝函数
clone[key] = deepClone(obj[key]);
}
}
return clone;
}
let obj = {
name: "andy",
age: 18,
address: ["江苏省", "扬州市", "广陵区"],
};
//拷贝多层对象并打印
let obj1 = deepClone(obj);
console.log(obj1);
(2)使用lodash库的cloneDeep方法
//(2)第三方库方法(需要导包)
let obj2 = _.cloneDeep(obj);
console.log(obj2);
(3)JSON.parse(JSON.stringify(obj))
//(3)JSON.parse(JSON.stringify()) 方法
var obj3 = JSON.parse(JSON.stringify(obj));
console.log(obj);