Blogger Information
Blog 54
fans 6
comment 31
visits 107467
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template
JS的条件循环控制、函数和DOM常用操作
吾逍遥
Original
1615 people have browsed it

一、学习的新认识

今天同样是夯实 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中就感觉更明显。
Correcting teacher:天蓬老师天蓬老师

Correction status:qualified

Teacher's comments:不少地方的分析,非常精辟, js中的一些概念, 很多人有不同的解读, 这是正常的, 毕竟ECMA只制定了一个标准,目前 主流的有好几个JS引擎,实现手段不同, 并不统一
Statement of this Website
The copyright of this blog article belongs to the blogger. Please specify the address when reprinting! If there is any infringement or violation of the law, please contact admin@php.cn Report processing!
All comments Speak rationally on civilized internet, please comply with News Comment Service Agreement
0 comments
Author's latest blog post