当前位置: 首页 > article >正文

【前端开发入门】JavaScript快速入门--数据操作

目录

  • 引言
  • 一、js数据操作
    • 1. 常用操作
      • 1.1 其他类型转字符串
      • 1.2 其他类型转Boolean
      • 1.3 判断赋值无意义的情况下使用默认值
      • 1.4 判断条件成立的情况下执行右侧表达式
      • 1.5 其他
  • 二、引用类型的数据使用
    • 1. Object类型数据
      • 1.1 访问Object属性
      • 1.2 添加、修改、删除属性
      • 1.3 遍历属性
    • 2. Array类型数据
  • 三、数据拷贝
    • 1. 浅拷贝
      • 1.1 扩展运算符方式
      • 1.2 Object.assign()
    • 2. 深拷贝
      • 2.1 使用 JSON.parse() 和 JSON.stringify()配合完成
      • 2.2 使用递归函数循环复制对象或数组
      • 2.3 使用第三方库
  • 四、js原型链
  • 五、总结

引言

本系列教程旨在帮助一些零基础的玩家快速上手前端开发。基于我自学的经验会删减部分使用频率不高的内容,并不代表这部分内容不重要,只是对于初学者来说没必要一开始就学的面面俱到。我希望可以先通过主干内容带大家入门前端,细节技巧性内容,可以在后续的开发工作中自行发现并掌握。

在上一篇【前端开发入门】JavaScript快速入门–js变量 中,我们已经熟悉了js定义变量的技巧,本篇主要介绍js变量的使用技巧(数据操作)。


一、js数据操作

首先要认识js的两个概念 赋值表达式

  • 赋值:用 = 等号连接左右两边,将右侧内容赋值给左侧变量。
  • 表达式:使用js运算符进行两个值(也可以是变量)之间的计算,并将整体视为一个表达式。

我们来曲解一下 表达式 的定义,以便更好的理解这个东西。
我们可以把表达式认定为一个 东西

  • 一个常量值:1 、“hello world !” 、true 、[1, 2, 3, 4, 5] 、 { name: ‘张三’, age: 28, sex: ‘男’ } 等等。
  • 一个算式:1+1 、Math.max(1, 2)
  • 一个函数调用结果的返回值:[1,2,3,4,5].find( item => item == 2)、add(1,2)
  • 一个函数:function F1(){} 、 ()=>{}
  • 以上的内容都有一个共同的特点,他们不论进行了多么复杂的运行逻辑,最终都表示一个确定的东西。最终这一整段的表达式可以抽象为一个整体,参与到赋值操作、其他表达式运算或是函数传参等逻辑中去。

1. 常用操作

js是一个动态语言,一些类型可以随意变换,但是要时刻警惕转换后的类型是否是你需要的结果。

以下为常用操作,但不包含所有的符号操作。js的运算符以及表达式全部内容请参考:表达式与运算符

1.1 其他类型转字符串

当需要快速获得字符串时,测试如下代码

// 字符串加任意Number、Boolean、undefined、null都会做字面拼接转换为字符串
console.log("str" + 1111);// 输出: str1111
console.log("str" + true);// 输出: strtrue
console.log("str" + false);// 输出: strfalse
console.log("str" + undefined);// 输出: strundefined
console.log("str" + null);// 输出: strnull
// 遵循加法执行顺序
console.log(1 + 2 + "str");// 输出: 3str
// 当需要快速转为字符串时可以直接使用空字符串 '' 拼接以上类型数据

1.2 其他类型转Boolean

当需要进行逻辑判断时,测试如下代码

// 在js中一些类型的值可以视为true
const condition1 = true;
const condition2 = 12; // 数字不为0即为true
const condition3 = "hello"; // 字符串不为空即为true
const condition4 = []; // 只要有个括号在就算true
const condition5 = {}; // 同上
// 以下被视为false
const condition6 = false;
const condition7 = 0;
const condition8 = "";
const condition9 = undefined;
const condition10 = null;
const condition11 = NaN;

if (condition4) {
  console.log("hello world");
}

1.3 判断赋值无意义的情况下使用默认值

js中 || 符号代表如果前边那个表达式能用(有内容的,参考1.2中的表达式视为true的情况),就直接用。如果前边那个表达式不能用(false),那就用后边那个(无论后边那个是true还是false)。
换句话说 || 符号就是为了尽可能寻找一个可用的表达式(true),从左侧到右侧依次寻找,找到即输出该表达式,如果没找到那就只好输出最后那个不可用表达式(false)

测试如下代码(注意区分定义函数形参与调用函数的实参区别):

const data1 = undefined
const data2 = "true data"
const actionFunc = (data) => {
	// 当传参无意义,则提供一个默认值给useData
    const useData = data || 'default data'
    console.log(useData);
}

actionFunc(data1)
actionFunc(data2)

1.4 判断条件成立的情况下执行右侧表达式

js中 && 符号代表如果前边那个表达式不可用(false),那就直接输出这个表达式。如果可用(有内容的,参考1.2中的表达式视为true的情况),那就输出后边那个表达式(不论后边那个是否为true)
换句话说 && 符号就是为了尽可能找一个不可用的表达式(false),从左侧到右侧依次寻找,找到即输出该表达式,如果没找到那就只好输出最后那个可用表达式(true)

const data1 = undefined
const data2 = {
    next: () => {
        console.log('next action');
        return '执行完成'
    }
}
const actionFunc = (data) => {
	// 判断函数执行的主体是否存在,当存在时再执行具体函数,确保函数正确执行。
    const useData = data && data.next()
    console.log('useData', useData);
}

actionFunc(data1)
actionFunc(data2)

1.5 其他

其他逻辑操作参考:表达式与运算符
高阶数学计算参考:Math


二、引用类型的数据使用

引用类型的数据主要是 ObjectArray 两种,function单独说

提前说一个小技巧 扩展运算符
利用 ... (三个英文键盘的 . 符号)将Object、Array的外壳({}、[])打开,将里边的元素平铺到另一个Object、Array中(注意只能平铺第一层,如果Object、Array中嵌套了其他Object、Array,那么会原样输出被嵌套部分),例如

// 平铺Object
const Obj1 = {
  name: "qbbmnn",
  age: 18,
  sex: "男",
};
const Obj2 = {
  ...Obj1,
  hobby: "女",
};
console.log(Obj2); // 输出: {name: 'qbbmnn', age: 18, sex: '男', hobby: '女'}
// 平铺Array
const arr1 = [1, 2, 3];
const arr2 = [...arr1, 4]
console.log(arr2); // 输出: [1, 2, 3, 4]

// 嵌套情况以Array为例,嵌套部分不会展开,作为一个整体子元素跟随同级展开
const arr1 = [1, 2, 3, [1, 2]];
const arr2 = [...arr1, 4];
console.log(arr2); // 输出: [1, 2, 3, Array(2), 4]

1. Object类型数据

Object 类型数据是以大括号包裹键值对的形式存在,,键名为字符串形式,键值可以为任意类型。

1.1 访问Object属性

const Obj1 = {
  name: "qbbmnn",
  age: 18,
  sex: "男",
  project: {
    name: "test",
  },
};

// 两种访问属性的方式都可以
console.log(Obj1.name);// 输出: qbbmnn
console.log(Obj1["name"]);// 输出: qbbmnn

// 遇到嵌套情况同理
console.log(Obj1.project.name);// 输出: test
console.log(Obj1["project"]["name"]);// 输出: test

// 遇到不确定属性存在与否的情况,获取的数据并不一定满足当前需要,可能缺斤短两
// 操作符为 `?.` ,表示判断接下来的属性是否存在,存在就正常输出,不存在就返回undefined
console.log(Obj1?.name);// 输出: qbbmnn
console.log(Obj1?.time);// 输出: undefined
console.log(Obj1?.project?.name);// 输出: test
console.log(Obj1?.project?.time);// 输出: undefined

1.2 添加、修改、删除属性

js是一门动态语言,可以随时向一个Object中添加或移除属性。遇到获取数据不符合前端渲染条件时,即使用Object的操作方法将数据进行改造。

  • 添加、修改属性:直接给属性赋值即可。
  • 删除属性::使用 delete 关键字。
// 添加属性
Obj1.time = new Date();
// 或
Obj1["time"] = new Date();

// 修改属性
Obj1.age = 19;
// 或
Obj1["age"] = 19;

// 删除属性,`delete关键字+ 对象属性名`
delete Obj1.project;

// 最终打印
console.log(Obj1);// 输出: {name: 'qbbmnn', age: 19, sex: '男',  time: Thu Oct 31 2024 11:48:03 GMT+0800 (中国标准时间)}

1.3 遍历属性

尝试输入以下代码,运行调试查看结果

const Obj1 = {
  name: "qbbmnn",
  age: 18,
  sex: "男",
};
// 方法1
for (key in Obj1) {
  console.log("key =", key);
  console.log("value =", Obj1[key]);
}

// 方法2,Object为关键字,Object对象。
const keys = Object.keys(Obj1);
console.log(keys);
const values = Object.values(Obj1);
console.log(values);
const entries = Object.entries(Obj1);
console.log(entries);
console.log(entries[0]);
console.log(entries[1]);
console.log(entries[2]);

2. Array类型数据

数组的基础操作请参考:JavaScript 数组方法

在上述网站中详细列举了js数组的操作方法,重点在于掌握写法,在实际开发过程中所操作的数组往往更为复杂。
数组操作一般为按特征查询、按特征分类、以及批量新增属性。

准备一组数据:

const users = [
    { name: '张三', age: 28, sex: '男' },
    { name: '李四', age: 22, sex: '女' },
    { name: '王五', age: 35, sex: '男' },
    { name: '赵六', age: 29, sex: '女' }
];

设定一个场景1:找出年龄在28岁的一组信息

const target = users.find(item => {
    return item.age == 28
})
// 此时将返回指定的那组数据对象,而不是一个数组
console.log(target);

设定一个场景2:找出年龄在28岁及以下的一组信息

const targetArr = users.filter(item => {
    return item.age <= 28
})
// 此时将返回一个数组
console.log(targetArr);

设定一个场景3:为所有数据添加班级属性

const targetArr = users.map(item => {
    return {
        ...item,
        class: "1班"
    }
})
// 此时将返回指定的那组数据对象,而不是一个数组
console.log(targetArr);

三、数据拷贝

数据拷贝这一问题主要针对引用类型数据。引用类型数据的赋值给不同的变量,是将对象的内存地址赋值给不同的变量,当其中一个发生改变,则所有的变量都会跟着变。这往往会导致一些意想不到的bug,所以对引用类型数据进行拷贝是非常重要的。

对引用类型数据拷贝分为 :浅拷贝深拷贝 两种。

  • 浅拷贝 只复制对象的第一层,对于嵌套的对象或数组,仍然是引用复制。
  • 深拷贝 会递归地复制对象的所有层级,确保原始对象和拷贝对象之间没有引用关系。

1. 浅拷贝

浅拷贝只会复制对象的一层,对于对象内部的引用类型(比如嵌套的对象或数组),它只是复制了引用而不是创建新的副本。这意味着如果原始对象中的某个属性是一个对象或数组,那么浅拷贝后的对象中的该属性仍然指向同一个内存地址。

1.1 扩展运算符方式

const original = { a: 1, b: { c: 2 } };
const shallowCopy = { ...original };
// 修改原始对象中的第一层基本数据类型
original.a = 3;
// 修改原始对象中的嵌套对象
original.b.c = 3;
console.log(original.a); // 输出:3
console.log(shallowCopy.a); // 输出:1
console.log(original.b); // 输出:{ c: 3 } 
console.log(shallowCopy.b); // 输出:{ c: 3 } 

1.2 Object.assign()

Object的静态方法,将一个或者多个源对象中所有可枚举的自有属性复制到目标对象,并返回修改后的目标对象。

// 其他代码同扩展运算符方式浅拷贝
const shallowCopy = Object.assign({}, original);

2. 深拷贝

深拷贝会递归地复制对象的所有层级,包括嵌套的对象或数组。这意味着原始对象和拷贝对象之间没有任何引用关系,对其中一个对象的修改不会影响另一个对象。

2.1 使用 JSON.parse() 和 JSON.stringify()配合完成

这种方法只能处理属性值为一般常见的对象和数组,不能处理属性值为:函数、日期对象、正则表达式对象等非标准类型。

这个方法的原理是将对象或数组转为字符串,进行基本数据类型的值拷贝,拷贝完成后再解码成为对象或数组。

const original = { a: 1, b: { c: 2 } };
const deepCopy = JSON.parse(JSON.stringify(original));

// 修改原始对象中的嵌套对象
original.b.c = 3;

console.log(original); // 输出:{ a: 1, b: { c: 3 } }
console.log(deepCopy); // 输出:{ a: 1, b: { c: 2 } }

2.2 使用递归函数循环复制对象或数组

function isObject(obj) {
  return obj != null && typeof obj === 'object';
}

function deepClone(obj, hash = new WeakMap()) {
  if (!isObject(obj)) return obj;
  if (hash.has(obj)) return hash.get(obj);
  
  let cloneObj = Array.isArray(obj) ? [] : {};
  hash.set(obj, cloneObj);
  
  for (let key in obj) {
    if (obj.hasOwnProperty(key)) {
      cloneObj[key] = deepClone(obj[key], hash);
    }
  }
  
  return cloneObj;
}

const original = { a: 1, b: { c: 2 } };
const deepCopy = deepClone(original);

// 修改原始对象中的嵌套对象
original.b.c = 3;

console.log(original); // 输出:{ a: 1, b: { c: 3 } }
console.log(deepCopy); // 输出:{ a: 1, b: { c: 2 } }

2.3 使用第三方库

这里只做推荐,因为还没讲到第三方库引用的部分。

  • Lodash ,文档参考:lodash深拷贝
  • radash ,文档参考:radash 深拷贝

以上两个库都包含了大量js操作方法,相较于自己写的方法,这些库都经历了各种环境的验证,使用起来会更加稳定。建议自己写只做思路学习,实际上生产环境尽量使用这种比较稳定的第三方库,保证代码执行结果的一致性。


四、js原型链

这其实是一个非常简单的概念,先试图理解一个前提条件:你所定义的js变量如字符串、数组、布尔值、对象、数组等这些都不是凭空而来的。它们都对应了一个创建它们的原型类,分别是String、Number、Boolean、Object、Array等。js在设计这些类时会默认提供一些方法供我们使用。

举个例子:你是你家庭实例化的一个对象,在外边看你身上什么都没有,但是你默认可以使用家里所有的物品。

在以下图片中可以看到,数组定义时并没有提供 map 方法,这些方法都是来自于 Array 定义的。
在这里插入图片描述

通常情况下这些方法或属性都是可以直接使用的,当你不清楚一个变量有什么可用方法时,可以通过 __proto__ 属性来查询一下。这个属性表示当前变量家里有什么固有的方法及属性。

在这里插入图片描述

如图上代码测试后可以发现,Array其实是Object构建出来的。把 users.__proto__ 作为一个整体(Array),可以继续使用 __proto__ 属性查询他的父级的默认属性和方法,直到查询到 null 就到头了。

例子中users也可以使用Object的固有方法例如:users.toLocaleString(),这里就好比你除了可以使用你父母家的物品,当然可以使用你爷爷家里的物品。

如此一级一级往上查询属性及方法,就构成了js的原型链,这条链路上的东西都可以直接拿来用, __proto__ 只是用来辅助查询有哪些内容,实际使用时可以直接像调用Object属性或方法一样使用 . 符号。


五、总结

以上即为js数据操作的大体内容,试图理解原型链,掌握引用类型的数据使用方法,进而支持自己可以编写一系列逻辑运算代码,同时利用深浅拷贝规避一些意想不到的bug。当你可以熟练的编写js代码逻辑后,那么就可以进入下一阶段函数封装。

再接再厉~


http://www.kler.cn/a/379785.html

相关文章:

  • Redis- 内核的分配内存限制的警告“WARNING Memory overcommit must be enabled!”
  • 【Qt】使用Qt发送http请求封装一个通用类
  • 基于大语言模型(LLM)自主Agent 智能体综述
  • React第十三章(useTransition)
  • 云轴科技ZStack在CID大会上分享VF网卡热迁移技术
  • qt QTabWidget详解
  • [vulnhub] DC:9
  • antdesignvue + AWS-S3实现Minio大文件分片上传
  • qt QPainter详解
  • React面试基础题大全(all)
  • C++ | Leetcode C++题解之第539题最小时间差
  • 创建型模式-建造者模式:构建复杂对象的优雅解决方案
  • springboot 单元测试-各个模块举例
  • 科研绘图系列:R语言多个组合堆积图(stacked plot)
  • 【1个月速成Java】基于Android平台开发个人记账app学习日记——第4天,注册登录逻辑代码
  • 理解 WordPress | 第一篇
  • 占地1.1万平,2亿投资的智能仓储系统:高架库、AGV、码垛机器人……
  • 基于MATLAB疲劳监测系统
  • 【基于LSM的ELF文件安全模块设计】参考
  • iOS 18.2 可让欧盟用户删除App Store、Safari、信息、相机和照片应用
  • PyTorch核心概念:从梯度、计算图到连续性的全面解析(一)
  • docker-compose安装rabbitmq 并开启延迟队列和管理面板插件(rabbitmq_delayed_message_exchange)
  • Harmony Next集成支付宝sdk失败
  • 2024-11-04 问AI: [AI面试题] 解释计算机视觉的概念
  • Linux/Unix grep命令
  • 实体(Entity)详解