数组应用、AJAX

一、数组 sort 应用

  1. var ary = [12, 1, 3, 5, 9, 2, 7];
  2. var ary1 = [
  3. {
  4. name: 'zhangsan',
  5. age: 15
  6. },
  7. {
  8. name: 'lisi',
  9. age: 18
  10. },
  11. {
  12. name: 'wangwu',
  13. age: 14
  14. },
  15. {
  16. name: 'liuliu',
  17. age: 19
  18. }
  19. ];
  20. var ary3 = [
  21. [12],
  22. [6],
  23. [3],
  24. [24]
  25. ];

sort 原理:

让相邻两项进行比较,如果 return 的数字【回调函数的返回值】大于0,那么会让这两项交换置;如果 return 的结果小于等于0,那么不交换;

  • 回调函数的执行次数和数组的成员个数,还跟数组的成员大小有关;
  1. ary.sort(function (a, b) {
  2. // console.log(a, b);
  3. // console.log(ary);
  4. return a - b;
  5. });
  6. console.log(ary);
  • 如果数组项是对象,sort 排序时交换的是数组项,而不是数组项的某一个具体的值;
  1. ary1.sort(function (a, b) {
  2. return a.age - b.age;
  3. });
  4. console.log(ary1);

二、数组的复制

  1. let ary = [1, 2, 3, 4, 5];
  2. let ary2 = [
  3. {
  4. name: 1
  5. },
  6. {
  7. name: 2
  8. },
  9. {
  10. name: 3
  11. }
  12. ];
  13. // 复制数组 ary 和 ary2;
  14. let a1 = ary.slice();
  15. a1[1] = 100;
  16. console.log(a1); // [1, 100, 3, 4, 5]
  17. console.log(ary); // [1, 2, 3, 4, 5]
  18. let a2 = ary2.slice();
  19. a2[1].name = 100;
  20. console.log(a2); // [{name: 1}, {name: 100}, {name: 3}]
  21. console.log(ary2); // [{name: 1}, {name: 100}, {name: 3}]
  • 一个数组中的数组项是一个基本数据类型的值时,这个数组项存储的就是这个值本身;
  • 如果数组项是一个引用数据类型的值时,这个数组项实际存储的是这个引用数据类型值的堆内存地址
  • 所以在复制数组时,如果数组项是基本数据类型的值,那么复制出来的新数组中的数组项和原数组中的项没关系;
  • 如果复制的项是引用数据类型,在复制这一项时,其实是复制的堆内存地址,所以新复制出来的数组时一个新数组,但是数组项由于是堆内存地址,所以在操作新数组中的这一项时,原数组项也会受到影响;所以这种复制成为浅复制;

三、DOM 映射

  1. let ulLis = document.getElementById('ulWrapper');
  2. let liList = ulLis.getElementsByTagName('li');
  • 需求:按照页面内列表的数字升序排列这些 li

因为数组才能排序,所以我们需要先把获取回来的类数组集合转变成数组

  1. let ary = utils.arrLikeToAry(liList);
  2. ary.sort(function (a, b) {
  3. return a.innerHTML - b.innerHTML;
  4. });
  5. console.log(ary);
  • ? 数组中已经排好序了,但是页面中没有发生改变
  • 我们需要把这些 li 重新插入到页面中,页面中的 li 的顺序才会发生改变
  1. for (let i = 0; i < ary.length; i++) {
  2. ulLis.appendChild(ary[i]);
  3. }
  • 思考? 页面中的 li 还是10个,为啥不是20个?原来不是有10个来着?我们又 appendChild 了10个啊?

DOM映射

页面中的 html 元素和我们通过 js 相关方法(getElementsById、getElementsByTagName/getElementsByXXX)获取的元素对象或者元素集合存在映射关系(一个改另一个跟着改);

常见的 DOM 映射:

  • liList[i].style.backgroundColor = ‘red’; 将 liList[i] 元素对象对应的堆内存下的 style 属性下的 backgroundColor 属性修改为 red(本质是操作 js 堆内存空间),但是由于 DOM 映射机制,页面中的元素和元素对象的堆内存空间是绑定在一起的。所以我们修改了元素对象堆内存空间里的值,页面中的标签会按照最新的值来渲染;
  • liList 是 ul 下面的 li 元素集合,集合中存储的都是元素对象,而对象都是堆内存;所以 liList 这个类数组中存储的形如,{0: aaafff111, 1: aaafff222, …..,length: 1};然后我们通过类数组转数组的方法将类数组转为数组后数组,数组中存储的也只是元素对象的堆内存地址,形如 [aaafff111, aaafff222, ….,length: 10],此时虽然是把类数组转成了数组,但是并不是重新创造了10个 li 元素对象,而是把原来的10个 li 搞到一个数组中了。然后我们通过 appendChild 把数组中的元素对象插入到页面中时,其实这些元素对象就是页面中的元素对象,此时 appendChild 会把这个元素对象移动到容器末尾。
  • appendChild:向容器末尾追加元素,如果追加的元素已经存在于容器中,此时不会克隆(按照原来的再造一份一模一样的新的)一份再追加,而是把原来的移动到容器末尾;
  • 数据绑定后:数据绑定之前,我们获取的 ul 下面的 li 得到一个空集合,但是当我们绑定数据后不需要重新获取,DOM 映射机制会把新增加的元素映射到我们之前已经获取的集合中,此时元素集合不再是空集合。但是 querySelector 和 querySelectorAll 获取的集合叫做静态集合 staticNodeList,是掐断了 DOM 映射的,基于这种方式获取元素集合,在数据绑定完成后需要重新获取。

四、JSON 数据

JSON 数据格式:

JSON 数据不是一种数据类型,而是一种数据格式。是一种前后端交互时常用的数据格式;

JSON 对象

属性名和属性值都是双引号,如果属性值是数字可以不用双引号包裹;

普通对象:
  1. let obj = {
  2. "name": "珠峰",
  3. "age": 10
  4. };

二维数组:数组中的对象的属性名和属性值都需要使用双引号包裹
  1. let data = [
  2. {
  3. "name": "张三",
  4. "age": 18
  5. },
  6. {
  7. "name": "李四",
  8. "age": 19
  9. }
  10. ];

JSON 格式的字符串: 长得很像 JSON 对象的字符串

  1. let str = '{"name": "珠峰", "id": 1, "age": 18}';
  2. let str2 = '[{"name": "张三", "age": 1}, {"name": "李四", "age": 19}]';

JSON 格式的字符串和 JSON 格式的对象互转:

Window 对象上提供了 JSON 的对象

  1. console.log(window.JSON);
  • window.JSON.stringify() 方法:把 JSON 格式的对象转成 JSON 格式的字符串
  • window.JSON.parse() 方法:把 JSON 格式的字符串转化成 JSON 格式的对象

window 对象上的方法调用时,可以省略 window

  1. let str3 = JSON.stringify(obj); // window.JSON.stringify(obj)
  2. console.log(str3);
  3. let str4 = JSON.stringify(data); // window.JSON.stringify(data)
  4. console.log(str4);
  5. let obj2 = JSON.parse(str);
  6. console.log(obj2);
  7. let obj3 = JSON.parse(str2);
  8. console.log(obj3);
  • 注意:IE6/7 没有 JSON 对象,在低版本浏览器将 JSON 字符串 转换为 JSON 对象需要使用 eval 方法。
  1. let str5 = '{"name": "珠峰", "id": 1, "age": 18}';
  2. // let obj4 = eval(str5);
  3. // console.log(obj4); // 报错:Unexpected token。。

报错的原因是 eval 把字符串转成代码执行,当遇到对象的花括号时,eval 会首先认定花括号是代码块,而不是对象;为了解决这个问题,我们需要给字符串外面多拼接一层小括号;

  1. let obj5 = eval('(' + str5 + ')');
  2. console.log(obj5); // 加小括号后eva会把({...})当成一个对象

封装方法 json 字符串转 JSON:

  1. /** desc
  2. * @desc JSON字符串转JSON
  3. * @param jsonstr json格式字符串
  4. * @returns json格式对象
  5. */
  6. function toJson(jsonstr) {
  7. if ('JSON' in window) {
  8. return JSON.parse(jsonstr);
  9. } else {
  10. return eval('(' + jsonstr + ')');
  11. }
  12. }

五、ajax 初步

AJAX

Asynchronous javascript and xml 异步的 javascript 和 xml

在我们过往的项目中的数据都是写死的,但是真实项目中,数据都是存储数据库中的。而服务端通过服务端的技术(nodejs、php、java、python…)把数据从数据库中读取出来再处理成客户端需要的数据,然后通过网络技术(ajax、jsonp等)把数据传递给客户端;

ajax 四步

第一步:创建一个 ajax 对象
  1. var xhr = new XMLHttpRequest();

第二步:调用 ajax 对象的 open 方法设置请求信息:
  1. xhr.open('GET', './data.json', false);

参数详解:

  • 请求方式 http method(GET POST PUT DELETE OPTIONS…)
  • 请求的服务端 url 地址(接口)
  • 同步或异步 true 表示异步,false 表示同步

第三步:设置 xhr 的 onreadystatechange 事件监听

  1. xhr.onreadystatechange = function () {
  2. if (xhr.readyState === 4 && xhr.status === 200) {
  3. // 当 xhr.readyState 变成 4 并且 xhr 的 status 为 200 时表示当前请求成功
  4. // 当成功后,xhr 的 responseText 属性会存储着本次请求获取的数据
  5. console.log(xhr.responseText);
  6. }
  7. };

第四步:发送请求

  1. xhr.send();

六、数据绑定

  • 页面中的数据通常都不是写死的,大多数情况下都需要动态绑定数据;
  1. let ul = document.getElementById('ul');
  1. var ary = [
  2. {
  3. name: '张三',
  4. age: 18
  5. },
  6. {
  7. name: '李四',
  8. age: 19
  9. },
  10. {
  11. name: '王五',
  12. age: 20
  13. }
  14. ];

1. 创建元素

利用动态创建 DOM 的方法,动态创建 dom 对象

  1. for (let i = 0; i < ary.length; i++) {
  2. let cur = ary[i];
  3. let newLi = document.createElement('li');
  4. newLi.innerHTML = 'name: ' + cur.name + ' age:' + cur.age;
  5. ul.appendChild(newLi); // 所以在这里每次都插入一个新元素,就会引发一次 DOM 回流,性能开销很大,不推荐这种做法
  6. }
  • DOM回流(reflow): 在页面中某个元素的插入、删除、位置、大小发生变化,那么会重新计算其他元素的位置,这样做非常消耗性能;
  • DOM重绘(repaint):当页面中的元素的背景、字体颜色发生改变、那么浏览器需要对其进行重新绘制,这种现象称为重绘;

2. 利用文档碎片

文档碎片:一个通过 DOM api 创建的临时存放 DOM 元素的容器

  1. let frg = document.createDocumentFragment(); // 创建文档碎片
  2. for (let i = 0; i < ary.length; i++) {
  3. let cur = ary[i];
  4. let newLi = document.createElement('li');
  5. newLi.innerHTML = 'name: ' + cur.name + ' age:' + cur.age;
  6. frg.appendChild(newLi);
  7. }
  8. ul.appendChild(frg);
  9. frg = null; // 当插入后,手动释放 frg 的内存(手动释放临存储对象的内存是一种优秀的编程习惯)

3. 拼接字符串+innerHTML(元素对象的 innerHTML 可以识别字符串中的 html 标签)

  1. let str = '';
  2. for (let i = 0; i < ary.length; i++) {
  3. let cur = ary[i];
  4. str += '<li>' + 'name: ' + cur.name + 'age: ' + cur.age + '</li>';
  5. }
  6. console.log(str);
  7. ul.innerHTML = str;

4. 模板字符串

  1. let tpl = ``;
  2. for (let i = 0; i < ary.length; i++) {
  3. let cur = ary[i];
  4. tpl += `<li>
  5. <strong>name: ${cur.name}</strong>
  6. <strong>age: ${cur.age}</strong>
  7. </li>`
  8. }
  9. ul.innerHTML = tpl;