首页 > 编程语言 >JavaScript从零学起 —— 数据类型(进阶篇3)

JavaScript从零学起 —— 数据类型(进阶篇3)

时间:2024-10-18 20:16:48浏览次数:3  
标签:name 对象 age Object 数据类型 史莱姆 进阶篇 零学起 属性

说明:此文章用作个人学习记录,若有任何问题或建议欢迎大家在评论区讨论

文章目录


前言

在前面的两篇文章《JavaScript 数据类型进阶篇 1》《进阶篇 2》中,
我们已经详细讨论了 JavaScript 的 7 种基本数据类型。

接下来,我们将深入探讨复杂数据类型(也称为引用类型)
包括它们的定义、实现、用法,以及开发过程中常见的问题和解决方案

复杂数据类型之所以也被称为引用类型,是因为它们与基本数据类型在数据存储方式上不同
基本数据类型直接存储值本身,而引用类型则存储的是数据所在内存的地址,也就是对数据的引用

这种特性使得对引用类型的操作会影响到存储在内存中的实际数据,而不仅仅是变量本身。

引用类型包括:对象Object、数组Array、函数function、日期Date、正则表达式RegExp、错误对象Error,
以及ES6新加入的一些类型:MapSetWeakMapWeakSet

受限于篇幅以及本人的脑细胞死亡率,本篇将重点讲述对象Object的相关内容


对象 (Object)

1. 定义

  • 对象(Object) 是一个包含属性(键值对)的数据结构
  • 每个属性由键(也叫属性名)和对应的值组成
  • 对象的键是字符串(或者可以转换为字符串的类型)
  • 对象的值可以是任何数据类型(包括原始类型、其他对象、数组、函数等)

示例:

const person = {
    name: '小明',       // 键值对,属性名为 'name',值为 '小明',值的类型为字符串
    age: 18,            // 键值对,属性名为 'age',值为 18,值的类型为数字
    say: function() { 
        console.log('你好啊!'); // 键值对,键为 'say',值的类型为函数
    }
};

2. 对象创建方式

2.1 使用对象字面量(Object Literal)
  • 这是创建对象最简单、最常用的方法
const person = {
    name: '小明',
    age: 18
};

优点: 语法简单,系统消耗小,易读性强
缺点: 不适合大量创建对象,虽然也可以用Object.create()实现原型和继承,但不够灵活直观

2.2 使用 Object() 构造函数
  • 使用Object()构造函数和new关键字创建对象
const book = new Object();
book.title = 'JavaScript从零学起';
book.author = '史莱姆';

优点:

  • 可以使用函数创建对象
  • 能通过参数传递属性,使用更加灵活
  • 适合创建复杂对象

缺点:

  • 每次创建都会调用函数,系统资源消耗比字面量创建对象更多
  • 语法较字面量创建更复杂,不直观
  • 没有原生支持私有属性(但也可以通过闭包来实现)
2.3 使用自定义构造函数(Constructor Function)
  • 使用自定义构造函数和 new 关键字来创建对象
  • 这里强调自定义是为了区别于JavaScript内置的构造函数 Object()
function Person(name, age) {	//自定义构造函数通常以大写字母开头,可以更清晰的表示这是个构造函数
    this.name = name;			// 属性名是 name,变量名也是 name
    this.age = age;				// 在JS中属性名和变量名可以相同,在工厂函数的例子中我会使用不同的名字作为对比
}
const person1 = new Person('史莱姆', 30);
2.4 工厂函数(Factory Function)
  • 工厂函数严格来说是一种模式,它可以使用任何方法,包括 字面量Object()构造函数来创建和返回新对象
  • 可以通过工厂函数创建大量相似的对象

下面是一个结合了object()构造函数和工厂函数的例子

function createPerson(PersonName, PersonAge) {
    return new Object({
        name: PersonName, // 属性名是 name,变量名是 personName
        age: PersonAge	  // 属性名是 age,变量名是 personAge
    });					  // 为了提高可读性,了解参数传递的具体位置,推荐使用不同的属性和变量名
}						  

// 使用函数动态创建对象
const person1 = createPerson('小明', 18);
const person2 = createPerson('史莱姆', 30);

console.log(person1); 
console.log(person2); 

输出结果:

在这里插入图片描述

2.5 类(Class)

class 是 ES6 版本引入的新特性,可以更简洁的自定义构造函数

并且 class 在 ES13版本中引入了对 私有属性私有方法 的支持,
经测试最新版本的Chrome、Edge、Safari、火狐等浏览器都支持ES6和ES13
如果现在还在做IE项目的话,那就默认放弃所有新特性吧。。。

class Person {
    #secret; // ‘#’ 表示#secret是私有属性

    constructor(personName, personAge) { //constructor()是类的特有方法,
        this.name = personName; 		 //用于初始化类的实例,并且在使用new关键字创建对象时自动调用
        this.age = personAge; 			
        this.#secret = '身高2米8'; // 初始化私有属性
    }

    // 公共方法
    say() {
        console.log(`你好,我的名字是 ${this.name}`);
    }

    // 私有方法
    #getSecret() { //‘#’ 表示#getSecret()是私有方法
        return this.#secret;
    }

    // 公共方法访问私有方法
    publicSecret() {
        console.log(this.#getSecret());
    }
}

const person1 = new Person('史莱姆', 30);

person1.say(); // 输出: 你好,我的名字是史莱姆
person1.publicSecret(); // 输出: 身高2米8
// person1.#getSecret(); // 会抛出错误,因为私有方法无法在外部访问

优点:
1. 原生支持私有属性和方法
使用 # 前缀定义私有属性和方法,比闭包更为简洁明了
2. 支持继承
可以使用 extends 关键字灵活实现继承
3. 语法简洁易懂

缺点: 大量创建对象时 class 创建对象的性能消耗,大于使用工厂函数的性能消耗


3. 对象的常见操作

3.1 访问和修改属性
  • 可以使用点操作符或方括号语法访问和修改对象的属性
const person = { name: '史莱姆', age: 18};
console.log(person.name); // 输出: 史莱姆
person.age = 30;
console.log(person['age']); // 输出: 30
3.2 遍历对象
  • 可以使用 for...in 循环来遍历对象的属性
  • 此方法可以遍历出所有属性,包括原型链上被继承对象的属性
const user = { name: '史莱姆', age: 18};
for (let key in user) {
    console.log(key + ': ' + user[key]);
}
3.3 删除属性
  • 可以使用 delete 操作符删除对象的属性。
const user = { name: '史莱姆', age: 18 };
delete user.age;
console.log(user); // 输出: { name: '史莱姆' }

4. 开发过程中常见的问题和解决方案

4.1 对象属性未定义
  • 问题:访问不存在的对象属性时返回 undefined
  • 解决方案:在访问属性前,使用条件语句检查属性是否存在。
const animal = { name: 'cat' };
console.log(animal.age); // 输出: undefined
if ('age' in animal) {
    console.log(animal.age);
}
4.2 对象引用的问题
  • 问题:当你复制一个对象时,实际上复制的是它的引用,而不是对象本身
    修改复制对象的属性会影响到原对象
  • 解决方案:使用浅拷贝(如 Object.assign)或深拷贝(如 JSON.parse(JSON.stringify()))来创建对象的副本
const original = { name: '史莱姆' };
const copy = original;
copy.name = '史莱姆复制体';
console.log(original.name); // 输出: 史莱姆复制体

// 使用浅拷贝
const lowCopy = Object.assign({}, original);
lowCopy.name = '史莱姆复制体2号';
console.log(original.name); // 输出: 史莱姆复制体,因为name属性的值是个字符串,
							//字符串属于原始类型,所以浅拷贝的属性值被直接复制了,修改不影响原对象

// 深拷贝
const deepCopy = JSON.parse(JSON.stringify(original));
deepCopy.name = '史莱姆复制体3号';
console.log(original.name); // 输出: 史莱姆复制体,因为深拷贝的对象与原对象完全独立

拓展阅读:浅拷贝与深拷贝

浅拷贝(Shallow Copy)和深拷贝(Deep Copy)是两种复制对象的方法

浅拷贝(Shallow Copy):

  • 浅拷贝是指创建一个新对象,这个新对象的属性值是原始对象属性值的副本
  • 浅拷贝只会复制对象的第一层属性
  • 如果属性是原始类型(如字符串、数字、布尔值),那么拷贝的是这个值本身
  • 如果属性是引用类型(如对象、数组)拷贝的只是引用地址,而不是对象本身

深拷贝(Deep Copy):

  • 深拷贝会递归地复制对象中的每一层属性
  • 生成的新对象与原始对象完全独立
4.3 this 关键字的上下文问题
  • 问题:在对象方法中,this 的上下文可能会发生变化
  • 解决方案:使用箭头函数或 bind() 方法来保持 this 的正确上下文
const person = {
    name: '小明',
    say() {
        setTimeout(function() {							 //setTimeout()是一个异步函数,function()是一个普通函数
            console.log(`你好,我的名字叫 ${this.name}`); //当普通函数在异步回调中执行时,this 的上下文会改变
        }, 1000);										 //在这种情况下,this 默认指向的是全局对象,而不是原来的 person 对象
        												 //全局对象没有name属性,所以输出undefined
    }
};
person.say(); // 你好,我的名字叫 undefined

// 解决方法:使用箭头函数
const person2 = {
    name: '史莱姆',
    say() {
        setTimeout(() => {								 //箭头函数不会创建它自己的 this,它会继承外层函数的 this 值
            console.log(`你好,我的名字叫 ${this.name}`); // this 正确地指向 person2 对象
        }, 1000);
    }
};
person2.say(); // 输出: 你好,我的名字叫史莱姆

标签:name,对象,age,Object,数据类型,史莱姆,进阶篇,零学起,属性
From: https://blog.csdn.net/2401_87242367/article/details/143020447

相关文章

  • C++顺序结构(3)、数据类型_____教学
    一、设置域宽setw()输出的内容所占的总宽度成为域宽,有些高级语言中称为场宽。使用setw()前,必须包含头文件iomanip,即#include<iomanip>头文件iomanip,用来声明一些“流操作符”,需要一定格式输入输出时,就需要用到它,比较常用的有设置域宽、设置左右对齐、设置实数的精确度等。set......
  • PHP常量与数据类型
    PHP常量与数据类型PHP常量在PHP中,常量是值在脚本执行期间不会改变的量。常量使用define()函数或const关键字来定义。使用define()函数:define("GREETING","Hello,测试小罡!");echoGREETING;//输出:Hello,测试小罡!使用const关键字:constPI=3.14159;echoPI;//......
  • 数据类型
    基础概念在C#中,变量分为以下几种类型:值类型(Valuetypes)引用类型(Referencetypes)指针类型(Pointertypes)值类型(Valuetypes)值类型变量可以直接分配给一个值。它们是从类System.ValueType中派生的。值类型直接包含数据。比如int、char、float,它们分别存储数字、字符、......
  • java中的基本数据类型自动转换
    java中的基本数据类型自动转换在Java中,基本数据类型的自动转换(也称为隐式类型转换或提升)是指小范围的数据类型能够自动转换为更大范围的数据类型,而无需进行显式转换(即不需要编写类型转换的代码)。这种自动转换主要发生在数值类型之间,包括整型(byte、short、int、long)、浮点型(float......
  • JavaScript 数据类型转换全解析:转换为数值、字符串与布尔型
    目录非VIP用户可前往公众号“前端基地”进行免费阅读转换为数值型转换为数值型Number()函数parseInt()函数parseFloat()函数转换为字符串型转换方法toString()函数String()函数转换为布尔型转换方法Boolean()函数非VIP用户可前往公众号“前端基地”进行免......
  • js数据类型-cnblog
    js数据类型数值型数值型是js最基础的数据类型,与其他语言不同的地方是,js并不区分整型数值和浮点型数值,在js中,所有的数值都是有浮点型数值组成的js进制js能够识别3种进制的数据类型1.十进制2.十六进制3.八进制十六进制js的十六进制由0x或0X开头八进制八进制以......
  • JavaScript从零学起 —— 数据类型(进阶篇2)
    说明:此文章用作个人学习记录,若有任何问题或建议欢迎大家在评论区讨论文章目录前言一、Boolean(布尔值)1.定义2.实现3.用法示例4.常见问题与解决方法二、Undefined(未定义)1.定义2.实现3.常见问题与解决方法三、Null(空值)1.定义2.实现3.用法示例4.常见问题与......
  • Chrome开发者工具不完全指南(四、性能进阶篇)
    https://blog.csdn.net/lisheng19870305/article/details/106507511前言Profiles面板功能的作用主要是监控网页中各种方法执行时间和内存的变化,简单来说它就是Timeline的数字化版本。它的功能选项卡不是很多(只有三个),操作起来比较前面的几块功能版本来说简单,但是里面的数据确......
  • python基础(数据类型一)
    在python语言中数据类型包含整数(int),浮点(float),复数(complex),布尔(bool),字符串(str),列表(list),元组(tuple),字典(dict),集合(set)以及空值(None)#数据类型比较多,今天之讲解整数(int),浮点(float),复数(complex),布尔(bool)这三个,其余的类型会单独分六个章节逐一讲解。一.整数(int):1.整数表示数值,没有小......
  • 变量与数据类型:程序的基本构建块!
    Java入门之旅:变量与数据类型的奥秘在编程的世界里,变量就像是每个程序员的好伙伴,它们在代码中存储着各种信息,帮助我们在计算机中执行复杂的逻辑。而数据类型则是对这些变量的基本属性定义,影响着它们的行为和存储方式。今天,就让我们一起探索Java中的变量和数据类型,揭开它们的......