博客列表 >JS的条件循环控制、函数和DOM常用操作

JS的条件循环控制、函数和DOM常用操作

吾逍遥
吾逍遥原创
2020年11月05日 08:46:461596浏览

一、学习的新认识

今天同样是夯实 JS 基础的课程,学习了实际使用中常遇到的条件控制、循环控制、函数、箭头函数、DOM 操作、classList 和 dataset 对象。新认识主要有以下几个:

  1. 三元运算符 虽然老师只讲了使用三元运算符简化双分支控制,但是我想到了压缩 JS 中它是常用的一种技巧,对它进行了测试并总结了如何使用。
  2. for…of ES6 新增的遍历操作,大有一统遍历界的意思,与之相近的有 forEach 只适合数组、for…in 一般用于 JSON 格式的对象。下文有具体介绍。
  3. 剩余运算符 rest 和展开运算符 spread 虽然二者运算符都是…(省略号),但在不同位置则表示不同含义。
  4. querySelector ,通过选择器查询 dom 元素,尤其是当朱老师用 querySelectorAll()实现 jquery 的\$()功能时,我感觉 JS 一下子就活了,原来 jquery 也是在 js 的基础上封装了常用操作,方便了开发者使用。正如上学时 C++老师用 C 语言实现 ping 程序,它不再只是打个九九乘法表的演示工具而已。

二、条件控制(也称分支控制)

JavaScript 中条件控制语句主要关键字同 C++语言一样,就是 if、if…else、if…else if…else if…else 或 switch…case。

1、单分支和双分支

最简单的两种分支,就是if 表示的单分支if…else 表示的双分支 。对于双分支的简化就是三元运算符。

  1. // 单分支
  2. let score = 70;
  3. if (score >= 60) {
  4. console.log('合格');
  5. }
  6. // 双分支
  7. score = 50;
  8. if (score >= 60) {
  9. console.log('合格');
  10. } else {
  11. // 默认分支
  12. console.log('补考');
  13. }

这里要单独说下三元运算符,它也是现在JS 压缩中一种常用技巧 。但是要注意以下两点:

  • 若分支中是一条语句时,直接写在返回值即可,冒号前的语句分号要去除,否则报错。
  • 若分支中是多条语句时,要使用圆括号()包裹所有语句 ,语句之间使用逗号,隔开 ,切记不要有 let 定义变量 ,变量要在运算符外定义。当然分号也要去除。

在常规使用三元运算符中,它一般将结果赋值给一个变量,就是如:
let result= scorce >=60 ? ‘合格’ : ‘补考’;

  1. // 简化
  2. // 若只有一行执行语句时,可直接写上
  3. score >= 60 ? console.log('合格') : console.log('补考');
  4. // 若是双分支中是多行语句时,要用圆括号()包裹,每个句子用逗号,隔开,不可有变量定义let,也不可有分号。
  5. // 这种写法也是压缩js的中最常用的写法。
  6. let name = 'woxiaoyao';
  7. score >= 60 ? ((name = 'xiaoyao'), console.log(name + '合格')) : ((name = 'peter zhu'), console.log(name + '补考'));

2、多分支

多分支最基本就是从单分支和双分支拓展而来的 if…else if…else if…else,也有另一种简化语法:switch…case。不过要注意的是:switch…case 一般用于 单值判断 ,若是区间判断 则要将条件设置为 true ,这个是以前我苦恼的,老师一讲就明白了。还有 switch…case 使用要注意 break

  1. // switch简化多分支
  2. score = 70;
  3. switch (true) {
  4. // 区间判断:一定返回布尔值【重要】
  5. case score >= 60 && score < 80:
  6. console.log('合格');
  7. break;
  8. case score >= 80 && score <= 100:
  9. console.log('优秀');
  10. break;
  11. case score > 100 || score < 0:
  12. console.log('非法分数');
  13. break;
  14. default:
  15. // 默认分支
  16. console.log('补考');
  17. }
  18. // switch最常用还单值判断
  19. let status = 'fail';
  20. status = 'abc';
  21. status = 'Success';
  22. switch (status.toLowerCase()) {
  23. case 'success':
  24. console.log('成功');
  25. break;
  26. case 'fail':
  27. console.log('失败');
  28. break;
  29. default:
  30. console.log('未知错误');
  31. }

三、循环控制(也称遍历)

循环控制是最常见的操作,尤其是遍历对象和数组时。循环的核心一般是 循环变量初始值、循环条件、更新循环变量

1、while(入口型循环)和 do…while(出口型循环)

最基本的循环,实际使用很少,只要知道就可以了。二者区别是前者是在循环开始就判断条件是否满足,不满足则不循环。而后者则先执行一次再判断,很明显这种循环有点鸡肋,在实在使用中很少使用。

  1. let arr = [10, 20, 30, 40, 50];
  2. // 循环变量初始值
  3. let i = 0;
  4. // 循环条件
  5. while (i < arr.length) {
  6. console.log(arr[i]);
  7. // 循环更新
  8. i++;
  9. }
  10. i = 0;
  11. do {
  12. console.log(arr[i]);
  13. i++;
  14. } while (i < arr.length);

2、for(各种语言都支持的经典循环)

语法格式:for(循环变量初始化;循环结束条件;循环变量更新){}。
for 循环可以使用两个关键字: break 和 continue ,前者是从 break 位置跳出并结束循环 ,后者是从 continue 位置跳出提前进入下一轮循环判断。

  1. // for循环
  2. for (let i = 0; i < arr.length; i++) {
  3. console.log(arr[i]);
  4. }

3、 forEach、for…in 和 for…of

forEach 和 for…in 是 ES6 之前就支持的,而 for…of 是 ES6 新增的循环遍历方法。这三者中 for…in 其实存在不少问题,完全可以使用更好的 for…of 来替代。我的原则是优先使用 for…of ,其次是 forEach,就是使用在不支持新语法for…of时使用forEach。

forEach:数组的每一个元素执行一次提供的函数(不能使用 return、break 等中断循环),不改变原数组无返回值。老师说遍历对象,我测试遍历对象报错。目前它只适合数组

  • 语法: arr.forEach(function(item[,key,arr]){}); 其中 item 是数组中每个值,key 是索引,arr 是原数组。除第一个参数必须外,后两个参数可以不要。
  1. // forEach:只能是对数组遍历,对象则报错。
  2. let user={name:'woxiaoyao',age:28};
  3. arr.forEach(function(item,key){console.log('forEach=>',item);});
  4. user.forEach(function(item){console.log('forEach=>',item)});

for…in 设计之初,是给普通以 字符串的值为 key 的对象(如 JSON 格式的对象)使用的,而非数组。它的几个特点:
for(let index in objArr){console.log(objArr[index])}

  1. index 是字符串 String 类型 , 数组索引、普通对象属性名都将转换为字符串,此时访问值就是objArr[index],对于数组和对象都可以。关于数组的索引或键名(默认是索引的字符串)访问都是合法的,详细见https://www.php.cn/blog/detail/24718.html。使用它进行运算时一定要切记它是字符串 ,尤其在数组中进行偏移时要注意。
  2. 作用于数组的 for-in 循环体除了遍历数组元素外,还会遍历自定义属性。举个例子,如果你的数组中有一个可枚举属性 myArray.name,循环将额外执行一次,遍历到名为“name”的索引。就连数组原型链上的属性都能被访问到。(没明白,以后再探讨)
  3. 最让人震惊的是,在某些情况下,这段代码可能按照随机顺序遍历数组元素。(都这么说,我没遇到过)

简而言之,for-in 是为字符串的值为 key的对象设计的,你可以遍历得到字符串类型的键,因此不适用于数组遍历。

  1. // for...in
  2. for (let item in user) {
  3. console.log('for...in=>', user[item]);
  4. }

for…of ES6 后推荐的遍历对象和数组的方式,用来循环获取一对键值对中的值。课中老师测试了 for…of 默认是不支持 Object,后面会探讨解决方案。它具有几下优点:

  1. 最简洁、最直接的遍历数组元素的语法,对数组默认遍历值。对键名或键-值遍历要使用 ES6 为数组新增的 keys()和 entries()方法。
  2. 避开了 for…in 循环的所有缺陷
  3. 不同于 forEach(),可以使用 break,continue 和 return。 (我还以为 break 和 continue 只是 for 的专利呢,它有点集大成者的趋势)
  4. 支持数组、类数组(如 HTMLCollection 和 NodeList,后面遍历 dom 元素时要用到)、字符串、Map 和 Set 等具有 Symbol.iterator 属性的数据对象。
  5. 默认不支持对象的访问,可使用 ES6 对 Object 新增的方法 Object.keys()、Object.values()或 Object.entries()将对象的键、值或键-值转变成可遍历的。如:
    for (let key of Object.keys(obj)){}。
    for (let value of Object.values(obj)){}。
    for (let [key,value] of Object.entries(obj)){}。要注意是解构赋值写法和顺序。
  6. 对数组也提供了和对象同样的方法,不同的是数组不是参数而是它们是数组的方法 。如下所示:
    for (let key of arr.keys()){}
    for (let value of arr.values()){}
    for (let [key,value] of arr.entries()){}

目前 Jquery 遍历方法是 each、foreach 和 for…in,正因为 for…of 使 ES6 在遍历方面目前比 jquery 更加先进。即有for 的 break、continue 和 return 控制,又有forEach 和 for…in 所有功能 ,强烈推荐 for…of 的使用

  1. // for...of
  2. // for...of默认访问数组的值
  3. for (let item of arr) {
  4. console.log('for...of=>', item);
  5. }
  6. // for...of访问数组的健、值或健-值对则可使用ES6给数组新增的keys()、values()和entries方法。
  7. for (let key of arr.keys()) {
  8. console.log('for...of=>key=>', key);
  9. }
  10. // for...of默认不支持对象,但可用Object.keys(obj)、Object.values(obj)和Object.entries(obj)将对象的对应部分转为可遍历
  11. for (let value of Object.values(user)) {
  12. console.log('for...of=>object=>', value);
  13. }

四、ES6 对函数的扩展

在阮一峰老师教程中有详细介绍,这里主要学习了剩余运算符 rest 和展开运算符 spread,顺便介绍下函数表达式和箭头函数。

1、剩余运算符 rest 和展开运算符 spread

  • 二者都是以 …(省略号) 开头,操作对象一般是 数组
  • 函数定义时圆括号中是剩余运算符 rest ,在函数调用时圆括号中是展开运算符 spread 。在 C++语言中,前者是接受参数,将所有参数打包到一个数组后者是传入参数,将数组解构赋值给相同模式的接受参数。二者主要面对 未知长度 的数组,已知长度的数组当然也支持。
  • 它们是现代函数优秀特性,可以处理未知多个变量。
  1. // 剩余运算符rest和展开运算符spread
  2. // 剩余运算符接受未知个数参数
  3. function sum(...args) {
  4. let ret = 0;
  5. for (let val of args) {
  6. ret += val;
  7. }
  8. return ret;
  9. }
  10. let arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
  11. // 展开运算符展开未知个数的数组,传递给函数处理
  12. console.log('sum=>', sum(...arr));

对于标签函数利用 ES6 的 rest 和 spread 可以改进

  1. // 对标签函数的改进
  2. function myTag(strings, ...args) {
  3. let thtext = '',
  4. tdtext = '';
  5. for (let val of strings.values()) {
  6. if (val) thtext += `<th>${val}</th>`;
  7. }
  8. for (let val of args) {
  9. tdtext += `<td>${val}</td>`;
  10. }
  11. let ret = `<table><tr>${thtext}</tr><tr>${tdtext}</tr></table>`;
  12. return ret;
  13. }
  14. let name = 'woxiaoyao';
  15. let age = '28';
  16. console.log(myTag`my name${name}my age${age}`);

2、函数表达式和箭头函数

如let demo=function(){}形式的,后面就是函数表达式 ,也称匿名函数 ,没有名字,通过把它赋值给一个变量,来引用它 ,常用于回调方法 。在ES6中简化就是箭头函数,删除关键字function, 在参数列表与大括号之间添加一个”=>”(胖箭头) ,它就是箭头函数名字的由来。

不需要使用this情况下,推荐使用箭头函数。它有几点注意:

  • 如果没有参数,也必须加上一对”()”
  • 如果函数体只有一行代码,可以省略掉{},有多行代码,函数体的大括号不能省略。
  • 箭头函数中没有自己的this ,这点尤其要注意。
  1. // 箭头函数
  2. let sumArrow = (...args) => {
  3. let ret = 0;
  4. for (let val of args) {
  5. ret += val;
  6. }
  7. return ret;
  8. };
  9. console.log('箭头函数改进=>',sumArrow(1, 2, 3, 4, 5, 6, 7, 8, 9));
  10. </script>

fun

五、dom常用操作

DOM即document object model,文档对象模型。dom元素都是对象 ,这个概念一定要牢记,正因为是对象,可以有属性和方法。

1、获取dom元素

  • 根据标签Tag: document.getElementsByTagName(),如document.getElementsByTagName(“li”);
  • 根据ID: document.getElementById(),如document.getElementById(“list”);
  • 根据类class: document.getElementsByClassName(),如document.getElementsByClassName(“item active”);
  • 根据name: document.getElementsByName(),如document.getElementsByName(“first”);
  • 根据选择器: document.querySelector和document.querySelectorAll(),这是推荐方式,简单灵活。juqery的$()也是这个思路。

对于常见的HTML元素,js都直接提供了获取方法,如html为document.documentElement,head为document.head,body为document.body。

  1. <div class="container" id="container">
  2. <p class="item active">item1</p>
  3. <p class="item" name="item">item2</p>
  4. <p class="item" name="item">item3</p>
  5. <p class="item" name="item">item4</p>
  6. </div>
  7. <script>
  8. // 一、获取dom元素
  9. const tag = document.getElementsByTagName('p');
  10. console.log('tag=>', tag);
  11. console.log('tag=>', tag[1].innerHTML);
  12. console.log('tag=>', tag.item(1).innerHTML);
  13. const classdom = document.getElementsByClassName('item active');
  14. console.log('class=>', classdom);
  15. console.log('class=>', classdom[0].innerHTML);
  16. const name = document.getElementsByName('item');
  17. console.log('name=>', name);
  18. // id获取的不是类数组,不要用索引或item访问,它相当于元素对象HTMLDivElement(可看下面打印的信息)
  19. const id = document.getElementById('container');
  20. console.log('id=>', Object.prototype.toString.call(id));
  21. const selector = document.querySelector(".item");
  22. console.log('selector=>', Object.prototype.toString.call(selector));
  23. const selectors = document.querySelectorAll(".item");
  24. console.log('selectors=>', selectors);
  25. const html=document.documentElement;
  26. console.log(Object.prototype.toString.call(html));
  27. const head=document.head;
  28. console.log(Object.prototype.toString.call(head));
  29. const body=document.body;
  30. console.log(Object.prototype.toString.call(body));
  31. </script>

dom

获取到dom元素分析: 只有知道获取什么,操作才会得心应手。

  • HTMLCollection类数组Tag、class两种获取,可通过索引或item()方法来访问。如tag[0].innerHTML和tag.item(0).innerHTML。
  • NodeList类数组name和querySelectorAll&&两种获取,访问方式同上**。
  • 单个元素对象id和querySelector两种获取,都是HTMLXxxElement形式的对象,其中Xxx是元素的英文,首字母大写。访问方式是直接属性或访问即可,其实上两种类数组通过索引或item()得到就是单个元素对象。id.innerHTML,若使用索引或item()将报错,因为它不是数组。

老师经典一例: 单独列出,是因为它让我眼前一亮,原生JS并不弱,尤其是ES6以后
// 模拟jQuery的$()来获取元素
const $ = (selector) => document.querySelectorAll(selector);
console.log($(“.item:last-of-type”));

2、遍历元素节点

所谓遍历元素节点最常见就是遍历父节点、兄弟节点和子节点,如下图

dom-all

parent父节点:parentElement或parentNode
sibling兄弟节点: 上一个兄弟previousElementSibling,下一个兄弟nextElementSibling。获取所有兄弟原生JS没有直接方法,可通过父节点遍历所有子元素得到。
child子节点:children 它是类数组,可以通过索引或item()方向。对于特殊位置子元素也有直接方法,如第一个子元素firstElementChild,最后一个子元素lastElementChild。子元素数量可通过children的length,也可是节点的直接属性childElementCount获取。

  1. // 父节点
  2. console.log('父节点parentNode=>', selector.parentNode);
  3. console.log('父节点parentElement=>', selector.parentElement);
  4. // 兄弟节点
  5. const second = document.querySelector('#second');
  6. console.log('上一个兄弟=>',second.previousElementSibling.innerHTML);
  7. console.log('上一个兄弟=>',Object.prototype.toString.call(second.previousElementSibling));
  8. console.log('上一个兄弟=>',Object.prototype.toString.call(second.previousSibling));
  9. console.log('下一个兄弟=>',second.nextElementSibling.innerHTML);
  10. // 子节点
  11. // 子节点数量
  12. console.log('子节点数量=>',id.children.length,id.childElementCount);
  13. // 所有子节点
  14. console.log('所有子节点=>',id.children);
  15. // 第一个或最后一个子节点
  16. console.log('第一个子节点=>',id.firstElementChild);
  17. console.log('最后一个子节点=>',id.lastElementChild);
  18. // 数组遍历,推荐for...of
  19. for(let val of id.children){
  20. console.log('for...of遍历元素=>',val.innerHTML);
  21. // forEach作为了解就可以了
  22. Array.from(id.children).forEach((item)=>item.style.color="red");

dom-all-method

遍历元素总结:

  • 元素需要遍历节点分为 父节点、兄弟节点和子节点 ,都是 Element元素对象属性中体现了类型,如children、firstElementChild表示子节点,parentElement或parentNode表示父节点,previousElementSibling和nextElementSiblings是兄弟节点。非常好记忆。
  • 以上获取的节点中,children是类数组,其它获取节点都是单个元素对象,要注意访问方式。
  • 对数组的遍历推荐for…of 。尤其本文在循环中已经对for…of进行了详细介绍,无论是遍历数组还是遍历对象,都不在话下,是ES6新增的,也是推荐的。至于老师说的forEach了解就可以,还有就是类数组转数组Array.from
  • 元素对象Element可直接对内置属性进行访问 ,如item.style.color就是访问元素的样式中color。

3、classList对象操作

classList其实就是元素Element对象的内置属性class的对象,只不过在js中class是关键字,所以取别名className代表,而对象则是classList。正如CSS中一样,我们控制元素样式最多的就是类,所以classList是JS中操作对象样式变化的重要手段。

classList常见操作:

  • 添加add(): 为元素增加类,如item.classList.add(‘red’);
  • 移除remove(): 移除元素中类,如item.classList.remove(‘red’);
  • 替换replace(): 替换元素指定的类,如item.classList.replace(‘red’,’blue’);
  • 自动切换toggle(): 当元素有指定类时则移除remove,若没有则添加add。如item.classList.toggle(‘red’)
  1. //classList对象操作
  2. for(let val of id.children){
  3. val.classList.add('red');
  4. val.classList.remove('red');
  5. val.classList.replace('red','blue');
  6. val.classList.toggle('red');
  7. }

classList

测试中遇到的问题:

  • replace替换时,若找不到第一个参数表示的类时,直接返回false。
  • classList仅仅是对元素的类名进行操作,最终效果还是由类的优先级和源码中顺序决定

4、dataset对象

dataset是用户自定义数据属性对象,这是HTML5新增的特性,支持用户给元素添加自定义属性,就如微信小程序中data-。它的特点是以data-为前缀 ,访问是通过元素的dataset属性来访问,获取时请省略掉”data-“。

  1. // dataset访问用户自定义属性
  2. const user=document.querySelector('#user');
  3. console.log('dataset=>',user.dataset.id);
  4. // 将使用连接线命名的多个单词的属性名,转换"驼峰命名法"来获取
  5. console.log('dataset=>',user.dataset.userName);

dataset

六、学习后的总结

  • 三元运算符和switch…case可简化分支控制,尤其是三元运算符在占用空间小的优良特性,被众多JS压缩所采用,要注意多行语句的书写。
  • for…of循环或遍历是ES6新增的,它是原生JS优秀于jquery一个点,无论是数组和对象,它都可以简便访问,尤其是配合新增数组的keys()、values()和entries()以及对象的keys()、values和entries(),没有什么不能遍历的。可以放弃forEach和for…in,原因在文中已经说明了
  • 剩余运算符rest和展开运算符spread使得JS越来越走近合格编程语言,前面let和const,现在的rest和spread,我们看到不断成熟的JavaScript编程语言。
  • dom操作对元素获取我推荐querySelectorAll(),一个就搞定了,对于元素遍历,按文中我提到方法记父节点、兄弟节点和子节点属性就可以,对于内置属性(包括classList)和自定义数据属性dataset要掌握。
  • 最后想说的话,本课中我影响最深的就是老师用querySelectorAll来模拟Jquery的$()时,我有种感觉原生JS正变得越来越成熟,结合我上篇博文https://www.php.cn/blog/detail/24718.html中就感觉更明显。
声明:本文内容转载自脚本之家,由网友自发贡献,版权归原作者所有,如您发现涉嫌抄袭侵权,请联系admin@php.cn 核实处理。
全部评论
文明上网理性发言,请遵守新闻评论服务协议