5、数据类型与四种检测方法、数字方法、字符串方法(含正则)、数组方法、对象方法(含克隆)、两行布局、两列布局、三列布局、五种盒子居中、em与rem自适应、Flex和grid布局、多图片延迟加载下载、五角星国旗|党徽国旗|时钟、弹窗之内置|拖拽缩放、表格排序、拖拽之普通|zTree(3350行)

一、js数据类型与四种检测方法
 来源:https://www.cnblogs.com/dushao/p/5999563.html
1、数据类型
  (1)5种基本数据类型:number、string、boolean、undefined、null
    A、undefined
      a、变量未声明。报错为"a is not defined"
        console.log( value)
      c、变量未赋值。默认为undefined
        var value;
        console.log( value)
      d、对象属性不存在。默认为undefined
        var obj = {}
        console.log( obj.value ) // undefined
      e、void(0),对后面的表达式进行求值,然后始终返回undefined
        html示例,<a href="javascript:void(0)">href="#"会使页面滚动到顶部</a>
        js示例,console.log(void(0) === undefined) //true
    B、null
      a、变量置空
        var value = null;
      b、普通对象判断 
        console.log( typeof value == "object" && value ) // 为true,则value为普通对象
  (2)2种引用数据类型:
    A、函数
    B、对象(Array、Arguments、Object、Date、Error、RegExp) 
2、arguments,实际传入的参数
  (1)说明
    A、它是函数内部自动生成的一个局部变量,只能在函数内部访问
    B、数据类型是类数组,一种特殊对象,
    C、不能直接调用数组方法,如push、forEach,
    D、不能new Arguments()
  (2)模拟如下
    var argument = {
      0: 'param0',
      1: 'param1',
      2: 'param2',
      length: 3,
    }
  (3)使用
    A、应通过Array.prototype.slice.call(arguments)转换为数组,
      function convertToArray() {
        var argsArray = [...arguments]; //展开运算符,只能展开可迭代对象
        var argsArray = Array.from(arguments); //Array.from,把类数组对象和可迭代对象转换为真正的数组
        var argsArray = Array.prototype.slice.call(arguments);
        console.log(argsArray, Array.isArray(argsArray));
        return argsArray;
      }
      convertToArray(1, 2, 3, "a", "b", "c");
    B、Array.prototype.slice的伪定义
      Array.prototype.slice = function(start,end){
        var result = new Array();
        start = start || 0;
        end = end || this.length;
        for(var i = start; i < end; i++){
          result.push(this[i]);
        }
        return result;
      } 
  (4)arguments的数据类型检测
    function fn(){
      console.log( arguments );//[Arguments] { '0': 8, '1': 8, '2': 8 }
      console.log( '以下用对象的方法操作arguments:');
      console.log( '1:',Object.keys(arguments));//[ '0', '1', '2' ],仅包含可枚举
      console.log( '2:',Object.getOwnPropertyNames(arguments));//[ '0', '1', '2', 'length', 'callee' ],还包含不可枚举
      console.log( '3:',Object.values(arguments));//[ 8, 8, 8 ]
      console.log( '4:',arguments.hasOwnProperty(0));//true
      console.log( '5:',arguments.hasOwnProperty('0'));//true
      console.log( '6:',arguments.hasOwnProperty('length'));//true,还包含不可枚举
      console.log( '以下用数组的方法操作arguments:');
      console.log( '7:',arguments.length );//3
      console.log( '8:',Array.prototype.slice.call(arguments));//[ 8, 8, 8 ]
      console.log( '以下检测arguments的数据类型:');
      console.log( '9:',typeof arguments === 'object');//object
      console.log( '10:',arguments instanceof Object) // true
      console.log( '11:',arguments.constructor === Object) // true
      console.log( '12:',Object.prototype.toString.call(arguments));//[object Arguments]
      console.log( '以下检测-其它引用数据-的数据类型:');
      console.log( '13:',Object.prototype.toString.call([ 8, 8, 8 ]));//[object Array]
      console.log( '14:',Object.prototype.toString.call({ '0': 8, '1': 8, '2': 8 }));//[object Object]
      console.log( '15:',Object.prototype.toString.call(new Object()));//[object Object]
      console.log( '16:',Object.prototype.toString.call(new Date()));//[object Date]
      console.log( '17:',Object.prototype.toString.call(new Error()));//[object Error]
      console.log( '18:',Object.prototype.toString.call(/test/));//[object RegExp]
      console.log( '19:',Object.prototype.toString.call(function(){}));//[object Function]
    }
    fn(8,8,8)
3、任意数据的4种检测方法   
  //附、被检测的数据
    var myNull = null;
    var myUndefined = undefined;
    var myString = "string";
    var myNumber = 222;
    var myBoolean = true;
    var myFunction = function(){};
    var myArray= [1,2,3];
    var myObject = {a:1};
    var myRegexp = /test/;
    var myDate = new Date();
    var myError = new Error();
  //(1)检测方法1,typeof
    console.log("以下,检测方法1,typeof,返回值是布尔")
    console.log(typeof myNull === 'object') // true
    console.log(typeof myUndefined === 'undefined') // true
    console.log( '----------------------------------------' );
    console.log(typeof myString === 'string') // true
    console.log(typeof myNumber === 'number') // true
    console.log(typeof myBoolean === 'boolean') // true
    console.log(typeof myFunction === 'function') // true
    console.log(typeof myArray === 'object') // true
    console.log(typeof myObject === 'object') // true
    console.log(typeof myRegexp === 'object') // true
    console.log(typeof myDate === 'object') // true
    console.log(typeof myError === 'object') // true
    console.log( '----------------------------------------' );
    var obj = {
      1: 'one',
    }
    for(var attr in obj){
      console.log( typeof attr );//为什么是string,不是number
    }
  //(2)检测方法2,instanceof 
    console.log("以下,检测方法2,instanceof,返回值是布尔")
    console.log(new String() instanceof String) // true 
    console.log(new Number() instanceof Number) // true 
    console.log(new Boolean() instanceof Boolean) // true 
    console.log( '----------------------------------------' );
    console.log(myString instanceof String) // false
    console.log(myNumber instanceof Number) // false 
    console.log(myBoolean instanceof Boolean) // false 
    console.log(myFunction instanceof Function) // true 
    console.log(myArray instanceof Array) // true
    console.log(myObject instanceof Object) // true
    console.log(myRegexp instanceof RegExp) // true
    console.log(myDate instanceof Date) // true
    console.log(myError instanceof Error) // true
  //(3)检测方法3,constructor 
    console.log("以下,检测方法2,constructor,返回值是类")
    console.log(myString.constructor === String) // true
    console.log(myNumber.constructor === Number) // true
    console.log(myBoolean.constructor === Boolean) // true
    console.log(myFunction.constructor === Function) // true 
    console.log(myArray.constructor === Array) // true
    console.log(myObject.constructor === Object) // true
    console.log(myRegexp.constructor === RegExp) // true
    console.log(myDate.constructor === Date) // true
    console.log(myError.constructor === Error) // true
  //(4)检测方法4,prototype 
    console.log("以下,检测方法4,prototype,返回值是字符串")
    console.log(Object.prototype.toString.call(myNull) === "[object Null]") // true;
    console.log(Object.prototype.toString.call(myUndefined) === "[object Undefined]") // true;
    console.log( '----------------------------------------' );
    console.log(Object.prototype.toString.call(myString) === "[object String]") // true;
    console.log(Object.prototype.toString.call(myNumber) === "[object Number]") // true;
    console.log(Object.prototype.toString.call(myBoolean) === "[object Boolean]") // true;
    console.log(Object.prototype.toString.call(myFunction) === "[object Function]") // true;
    console.log(Object.prototype.toString.call(myArray) === "[object Array]") // true;
    console.log(Object.prototype.toString.call(myObject) === "[object Object]") // true;
    console.log(Object.prototype.toString.call(myRegexp) === "[object RegExp]") // true;
    console.log(Object.prototype.toString.call(myDate) === "[object Date]") // true;
    console.log(Object.prototype.toString.call(myError) === "[object Error]") // true;

二、数字方法(数字的方法)
1、数字转换方法(Number.prototype方法)
  (1)toString(radix),将数字转换为字符串,可指定进制(2-36之间,默认十进制)
    const num=10;
    console.log(num.toString());//"10"(十进制)
    console.log(num.toString(2));//"1010"(二进制)
    console.log(num.toString(16));//"a"(十六进制)
  (2)toFixed(digits),将数字转换为指定小数位数的字符串(四舍五入)。
    注意:digits范围为0-20,超出则可能不准确。
    const num=3.14159;
    console.log(num.toFixed(2));//"3.14"
    console.log(num.toFixed(0));//"3"
  (3)toPrecision(precision),将数字转换为指定总位数(整数+小数)的字符串(四舍五入)。
    const num=123.456;
    console.log(num.toPrecision(4));//"123.5"(总位数4)
    console.log(num.toPrecision(2));//"1.2e+2"(科学计数法,总位数2)
  (4)toExponential(fractionDigits),将数字转换为科学计数法字符串,可指定小数位数。
    const num=12345;
    console.log(num.toExponential());//"1.2345e+4"
    console.log(num.toExponential(2));//"1.23e+4"
2、全局数字处理函数
  (1)parseInt(str,radix),解析字符串并返回整数(忽略非数字部分,支持指定进制)。
    注意:radix为2-36的整数,默认10(若字符串以0x开头,默认16)。
    console.log(parseInt("123"));//123
    console.log(parseInt("12.34"));//12(忽略小数部分)
    console.log(parseInt("11",2));//3(二进制转十进制)
  (2)parseFloat(str),解析字符串并返回浮点数(支持小数,忽略后续非数字部分)。
    console.log(parseFloat("3.14"));//3.14
    console.log(parseFloat("123abc"));//123
  (3)Number(value),将任意值转换为数字(失败返回NaN)。
    console.log(Number("123"));//123
    console.log(Number("12.3"));//12.3
    console.log(Number("abc"));//NaN
  (4)isNaN(value),判断值是否为NaN(注意:NaN是唯一不等于自身的值)。
    console.log(isNaN(NaN));//true
    console.log(isNaN(123));//false
    console.log(isNaN("123"));//false(字符串会先转换为数字)
  (5)isFinite(value),判断值是否为有限数字(非Infinity、-Infinity或NaN)。
    console.log(isFinite(123));//true
    console.log(isFinite(Infinity));//false
    console.log(isFinite("123"));//true(字符串会先转换为数字)
  (6)isInteger(value),判断值是否为整数(ES6新增)。
    console.log(Number.isInteger(123));//true
    console.log(Number.isInteger(123.0));//true(123.0本质是整数)
    console.log(Number.isInteger(123.45));//false
  (7)isSafeInteger(value),判断值是否为“安全整数”(范围:-(2^53-1)到2^53-1,ES6新增)。
    console.log(Number.isSafeInteger(253));//false(超出安全范围)
    console.log(Number.isSafeInteger(253-1));//true
3、数学计算函数(Math对象方法)
  (1)Math.abs(x):返回x的绝对值
    Math.abs(-5)→5  
  (2)Math.ceil(x):向上取整(返回大于等于x的最小整数)
    Math.ceil(3.1)→4
  (3)Math.floor(x):向下取整(返回小于等于x的最大整数)
    Math.floor(3.9)→3
  (4)Math.round(x):四舍五入取整
    Math.round(3.5)→4,Math.round(3.4)→3
  (5)Math.max(...values):返回一组数中的最大值
    Math.max(1,3,5)→5
  (6)Math.min(...values):返回一组数中的最小值
    Math.min(1,3,5)→1
  (7)Math.random():返回[0,1)之间的随机浮点数
    Math.random()→0.783(随机值)
  (8)Math.pow(base,exponent):返回base的exponent次幂(base^exponent)
    Math.pow(2,3)→8
  (9)Math.sqrt(x):返回x的平方根
    Math.sqrt(16)→4
  (10)Math.PI:常量,表示圆周率(约3.14159)
    Math.PI→3.141592653589793
4、整数范围验证方法封装,验证一个数是不是指定范围内的“整数”,且多位数的首位数不能为0
  /**
    *验证一个值是否为指定范围内的整数,且多位数首位数不为0
    *@param{*}value-待验证的值(可能是数字、字符串等类型)
    *@param{number}min-范围的最小值(包含min,必须是整数)
    *@param{number}max-范围的最大值(包含max,必须是整数,且max>min)
    *@returns{boolean}验证通过返回true,否则返回false
  */
  functionisValidIntegerInRange(value, min, max) {
    //1.先验证min和max是否为有效整数,且max>min(避免传入非法范围)
    if (!Number.isInteger(min) || !Number.isInteger(max) || max <= min) {
      console.error("范围参数错误:min和max必须是整数,且max>min");
      returnfalse;
    }
    //2.验证待验证值是否为整数(排除小数、字符串等非整数类型)
    //先尝试将value转为数字,若转换失败(如NaN),直接返回false
    const num = Number(value);
    if (!Number.isInteger(num)) {
      returnfalse;
    }
    //3.验证数字是否在[min,max]范围内
    if (num < min || num > max) {
      returnfalse;
    }
    //4.验证多位数的首位数是否不为0(1位数无需验证,因0~9首位数就是自身)
    if (num >= 10 || num <= -10) { //绝对值>=10即为多位数
      const numStr = String(Math.abs(num)); //转绝对值字符串,避免负号影响首字符判断
      if (numStr[0] === "0") { //首字符为0则无效
        returnfalse;
      }
    }
    //所有条件均满足,返回true
    returntrue;
  }
  //----------------------使用示例----------------------
  //示例1:验证15是否在[1,20]范围内(有效)
  console.log(isValidIntegerInRange(15,1,20));//true
  //示例2:验证"08"是否在[1,10]范围内("08"转数字为8,但多位数首为0,无效)
  console.log(isValidIntegerInRange("08",1,10));//false
  //示例3:验证3.5是否在[1,5]范围内(小数,无效)
  console.log(isValidIntegerInRange(3.5,1,5));//false
  //示例4:验证-12是否在[-20,-5]范围内(多位数,首为1非0,有效)
  console.log(isValidIntegerInRange(-12,-20,-5));//true
  //示例5:验证0是否在[0,0]范围内(1位数,有效)
  console.log(isValidIntegerInRange(0,0,0));//true
  //示例6:验证25是否在[30,50]范围内(超出范围,无效)
  console.log(isValidIntegerInRange(25,30,50));//false
  //示例7:范围参数错误(max<=min,返回false并报错)
  console.log(isValidIntegerInRange(10,15,5));//false(控制台输出错误提示)
5、小数相加
  (1)现象,console.log( 0.1+0.2 ); //0.30000000000000004,3和4之间有15个0
    说明,参与运算的两个小数,都是分母不是2的整数幂的分数
    另外,整数部分较大的小数,也会出现这个问题 //比如console.log(2**53 + 0.1); //2**53意为2的53次方
  (2)原因
    A、JavaScript采用64位双精度浮点数
    B、计算机使用IEEE-754标准的二进制浮点数表示法
    C、有些小数在二进制中是无限循环的,就像十进制中的1/3一样
    D、有些小数指的是,分母不是2的整数幂的分数,比如0.1,0.2,0.3...
    E、2的整数幂指的是,2、4、8、16、32...
  (3)解决
    A、放大为整数运算,console.log((0.1*10+0.2*10)/10); //0.3
    B、使用toFixed()控制显示位数,console.log((0.1+0.2).toFixed(1)); //"0.3",注意返回的是字符串
  (4)如何验证某个小数是否安全?
    A、用toString(2),查看二进制表示,若出现无限循环则可能有问题
    B、console.log((0.1).toString(2))

三、字符串方法(字符串的方法)!!!
  (1)数组也有的方法,如concat、includes(存在性判断,ES6新增)、indexOf(位置获取)、length(属性)、slice
  (2)参数可以是正则,match()、matchAll()、replace()、replaceAll()、split()、search()
  (3)JS字符串为不可变类型,所有字符串方法均不修改原字符串。返回值类型不同
  (4)字符串和数组的循环遍历都可以用,for(通过索引访问,可提前结束)、for...of(直接遍历字符,可提前结束)、for...in(主用遍历对象)
  (5)break提前结束循环遍历、continue跳过本次循环遍历
1、截取与分割
  (1)slice(start,end)
      A、作用:从字符串的指定索引范围截取部分字符,生成并返回新的子字符串(用于精准截取字符串片段,支持从末尾反向定位)。
      B、参数:两个可选参数,均为数字类型:
        start:起始索引(可选,默认值为0)
          若为负数,从字符串末尾开始计算(如-2表示倒数第二个字符);
          若start超出字符串长度,返回空字符串
        end:结束索引(可选,默认值为字符串长度)
          若为负数,从字符串末尾开始计算;截取范围为[start,end),即包含start对应的字符,不包含end对应的字符;
          若end<=start,返回空字符串
      C、返回值:新的子字符串,内容为原字符串中start到end(不包含end)之间的字符;若不符合截取条件,返回空字符串
  (2)substring(start,end)
      A、作用:从字符串的指定索引范围截取部分字符,生成并返回新的子字符串(功能与slice类似,但不支持负数索引,且会自动调整索引顺序)
      B、参数:两个可选参数,均为非负数字类型(若传入负数,会自动转为0):
        start:起始索引(可选,默认值为0)
        end:结束索引(可选,默认值为字符串长度)
          若start>end,方法会自动交换两者位置(如substring(5,2)等同于substring(2,5));截取范围同样为[start,end)
      C、返回值:新的子字符串,内容为原字符串中调整后start到end(不包含end)之间的字符;若不符合截取条件,返回空字符串
  (3)substr(start,length)
      A、作用:从字符串的指定起始索引开始,截取指定长度的字符,生成并返回新的子字符串
      注意:该方法已被部分浏览器标记为“不推荐使用”,建议优先使用slice
      B、参数:
        start:起始索引(必需)。若为负数,从字符串末尾开始计算(如-3表示从倒数第三个字符开始)
        length:可选参数,指定要截取的字符个数(默认值为“从start到字符串末尾的所有字符”);若为负数或0,返回空字符串
      C、返回值:新的子字符串,内容为原字符串中从start开始、长度为length的字符;若不符合截取条件,返回空字符串
  (4)split(separator,limit)
      A、作用:根据指定的分隔符,将字符串拆分为多个子字符串,最终组成一个数组,用于“字符串转数组”
      B、参数:
        separator:必需参数,分隔符(可传入字符串或正则表达式)
          若为''(空字符串),会将字符串拆分为单个字符的数组;
          若分隔符不在原字符串中,返回包含原字符串的单元素数组
        limit:可选参数,非负整数,指定数组的最大长度
          若设置该参数,拆分后数组长度不会超过limit;
          若limit为0,返回空数组
      C、返回值:一个数组,数组元素为拆分后的子字符串;若limit生效,数组长度受限于limit
      D、正则示例:"a1b2c3d4e5".split(/\d/, 3); //["a","b","c"]    
2、查找与判断
  (5)includes(substring)
    A、作用:判断原字符串是否包含指定的子字符串,返回布尔值,用于“子串存在性判断”
    B、参数:
      substring:必需参数,要查找的子字符串(若为''空字符串,始终返回true)
      position:可选参数,非负整数,指定从原字符串的哪个索引开始查找(默认值为0);若position大于字符串长度,返回false
    C、返回值:布尔值。若原字符串包含指定子字符串(从position开始查找),返回true;否则返回false
  (6)indexOf(substring)/lastIndexOf(substring)
    A、作用:
      indexOf(substring):从原字符串的开头(索引0)开始查找指定子字符串,返回其首次出现的起始索引;若不存在,返回-1
      lastIndexOf(substring):从原字符串的末尾(最后一个字符)开始查找指定子字符串,返回其最后一次出现的起始索引;若不存在,返回-1
    B、参数:
      substring:必需参数,要查找的子字符串(若为''空字符串,indexOf返回0,lastIndexOf返回字符串长度)
      position:可选参数,非负整数。
        indexOf中表示“从该索引开始向后查找”(默认0);
        lastIndexOf中表示“从该索引开始向前查找”(默认字符串长度);
        若position超出字符串范围,按边界值处理(如indexOf中position过大则返回-1)
    C、返回值:整数。找到子字符串则返回其起始索引,未找到则返回-1
  (7)search(separator)
    A、作用:在字符串中搜索与指定正则表达式匹配的第一个子串,并返回该子串的起始索引;
      若未找到匹配,则返回-1。主要用于快速定位字符串中符合正则规则的内容的首次出现位置。
    B、参数:separator:可为正则表达式对象(RegExp)或字符串(会自动转换为正则表达式,不包含修饰符)
      若为字符串,会被当作正则模式处理,仅查找首次匹配的位置
    C、返回值:数字类型
      若找到匹配的子串,返回该子串的起始索引(从0开始)
      若未找到任何匹配的子串,返回-1
    D、正则示例:"helloWorld".search(/[A-Z]/);//5    
  (8)startsWith(substring)
    A、作用:判断原字符串是否以指定的子字符串开头,返回布尔值(用于“字符串前缀判断”,如验证URL协议、文件后缀等场景)
    B、参数:
      substring:必需参数,要判断的前缀子字符串(若为''空字符串,始终返回true)
      position:可选参数,非负整数,指定将原字符串的“前position个字符”视为判断对象(默认值为0,即判断整个字符串的开头)
        若position大于字符串长度,返回false
    C、返回值:布尔值。若原字符串(或指定position范围内的片段)以子字符串开头,返回true;否则返回false
  (9)endsWith(substring)
    A、作用:判断原字符串是否以指定的子字符串结尾,返回布尔值(用于“字符串后缀判断”,如验证文件格式、邮箱域名等场景)
    B、参数:
      substring:必需参数,要判断的后缀子字符串(若为''空字符串,始终返回true)
      length:可选参数,非负整数,指定将原字符串的“前length个字符”视为判断对象(默认值为字符串长度,即判断整个字符串的结尾);
        若length为0,仅当substring为''时返回true
    C、返回值:布尔值。若原字符串(或指定length范围内的片段)以子字符串结尾,返回true;否则返回false
  (10)padStart(targetLength,padString)!!!
    A、作用:用指定的字符串填充原字符串的开头,直到原字符串达到指定的长度,返回填充后的新字符串
    B、参数:
      targetLength:必需参数,非负整数,指定填充后字符串的目标长度。若该值小于或等于原字符串的长度,则直接返回原字符串
      padString:可选参数,用于填充的字符串(默认值为' '空格)。若填充字符串过长,会截取其前部分以满足目标长度
    C、返回值:字符串。经过填充后达到目标长度的新字符串,原字符串不变
    D、示例:用于格式化字符串长度,如统一数字位数、补全日期格式等场景,console.log( String(1).padStart(2, '0') );
  (11)padEnd(targetLength,padString)
    A、作用:用指定的字符串填充原字符串的结尾,直到原字符串达到指定的长度,返回填充后的新字符串
    B、参数:
      targetLength:必需参数,非负整数,指定填充后字符串的目标长度。若该值小于或等于原字符串的长度,则直接返回原字符串
      padString:可选参数,用于填充的字符串(默认值为' '空格)。若填充字符串过长,会截取其前部分以满足目标长度
    C、返回值:字符串。经过填充后达到目标长度的新字符串,原字符串不变
    D、示例:用于格式化字符串长度,如对齐文本、补全编号等场景,console.log( "2025-07".padStart(7+8," ").padEnd(7+16," ") );
  (12)match(regexp)
    A、作用:根据指定的正则表达式,在原字符串中匹配符合规则的子字符串,返回匹配结果数组
    B、参数:必需参数,可为正则表达式对象(RegExp)或字符串
       若为字符串,会自动转为正则表达式,且不包含修饰符,如'abc'等同于/abc/
       若正则表达式带有g(全局匹配)修饰符,会匹配所有符合规则的子串;若无g修饰符,仅匹配第一个符合规则的子串
    C、返回值:
      若有匹配结果: 
        无g修饰符时,返回数组(第0项为匹配的子串,后续项为正则捕获组内容,数组还包含index(匹配起始索引)和input(原字符串)属性);
        有g修饰符时,返回数组(包含所有匹配子串,无捕获组和额外属性)
      若无匹配结果:返回null
  (13)matchAll (regexp)
    A、作用:在原字符串中匹配所有符合正则表达式规则的子字符串
    B、参数:必需参数,且必须是正则表达式对象(RegExp),不可为字符串,必须带有g(全局匹配)修饰符
    C、返回值:
      若有匹配结果:返回RegExpStringIterator类型的迭代器,迭代器的每一项为一个匹配结果数组
        结构与match()无g修饰符时的返回数组一致,
        第0项为匹配子串,
        后续项为捕获组内容,包含index(匹配起始索引)和input(原字符串)属性
        若正则有命名捕获组,还包含groups属性
      若无匹配结果:返回的迭代器为空,遍历后无任何内容(不会返回null)
  (14)localeCompare(targetStr,locales,options)
    A、作用:
      按照指定的语言环境(或默认环境)排序规则,比较当前字符串与目标字符串的顺序关系,
      用于实现符合本地化习惯的字符串排序(如多语言环境下的字母、汉字、数字等排序)
    B、参数:
      targetStr:必需参数,字符串类型,指要与当前字符串进行比较的目标字符串
      locales:可选参数,字符串或字符串数组类型,用于指定语言环境,如"zh-CN"表示中文简体、"en-US"表示美式英语
    C、返回值:
      -1,当前字符串在目标字符串之前
      0,两个字符串按排序规则相等
      1,当前字符串在目标字符串之后
    D、按数字或字典序排序,字典序其实也是数字
    E、示例,!!!
      //字符串的localeCompare方法判断位置,数组的sort方法调整位置
      //下例,如果在Unicode编码中a在b前,那么参数返回值将小于0,sort方法根据小于0,实现参数在前,数组项在前
      const fruits = ['d', 'a', 'c', 'b'];
      const sortedFruits = fruits.sort((a, b) => a.localeCompare(b)); 
3、转换与修改
  (13)toUpperCase()
    A、作用:将原字符串中的所有小写英文字母转换为大写英文字母,生成并返回新字符串
    B、参数:无参数
    C、返回值:新字符串,内容为原字符串所有小写字母转为大写后的结果;非英文字母字符保持不变
    D、是否改变原字符串:否
  (14)toLowerCase()
    A、作用:将原字符串中的所有大写英文字母转换为小写英文字母,生成并返回新字符串(功能与toUpperCase相反,用途类似)
    B、参数:无参数
    C、返回值:新字符串,内容为原字符串所有大写字母转为小写后的结果;非英文字母字符保持不变
    D、是否改变原字符串:否
  (15)trim()
    A、作用:去除原字符串开头和结尾的空白字符(包括空格、制表符\t、换行符\n、回车符\r等),生成并返回新字符串
    B、参数:无参数
    C、返回值:新字符串,内容为原字符串去除首尾空白后的结果;字符串中间的空白字符保持不变
    D、是否改变原字符串:否。(扩展:trimStart()仅去除开头空白,trimEnd()仅去除结尾空白,用法与trim()一致)
    下列说法和示例对吗
  (16)replace(regexp, replacement)
    A、作用:根据指定的正则表达式或字符串,在原字符串中匹配目标子串,并将其替换为指定内容,生成并返回新字符串
    B、参数:
      regexp:第一个参数,可为正则表达式对象(RegExp)或字符串
        若为字符串,仅替换第一个匹配的子串
        若为不带g修饰符的正则,仅替换第一个匹配的子串
        若为带g修饰符的正则,替换所有匹配的子串
      replacement:第二个参数,可为字符串或函数
        若为字符串,可使用特殊占位符,如
          $&,匹配的子串,"abc".replace(/b/,"[$&]") →"a[b]c"
          $1,第一个捕获组内容,"2024-05-20".replace(/(\d{4})-(\d{2})-(\d{2})/,"$2/$3/$1") →"05/20/2024"
        若为函数,
          参数依次为“匹配的子串、捕获组1、捕获组2...、匹配起始索引、原字符串、命名捕获组对象”
          返回值将作为替换内容
    C、返回值:
      新字符串,内容为原字符串完成替换后的结果
      若无匹配子串,返回与原字符串相同的新字符串
  (17)replaceAll(searchValue, replacement)
    A、作用:在原字符串中匹配所有符合规则的目标子串(正则需带g修饰符,字符串直接全匹配),并将其统一替换为指定内容,生成并返回新字符串
    B、参数:
      searchValue:第一个参数,可为正则表达式对象(RegExp)或字符串
        若为字符串,替换所有匹配的子串
        若为正则,必须带g修饰符,替换所有匹配的子串
      replacement:第二个参数,可为字符串或函数
        若为字符串,可使用特殊占位符,如
          $&,匹配的子串,
            "abcb".replaceAll("b", "[$&]")//"a[b]c[b]";
            "abcb".replaceAll(/b/g, "[$&]")//"a[b]c[b]"
          $1,第一个捕获组内容,
            "2024-05-20 2024-06-21".replaceAll(/(\d{4})-(\d{2})-(\d{2})/g, "$2/$3/$1") //"05/20/2024 06/21/2024"
        若为函数,
          参数依次为“匹配的子串、捕获组1、捕获组2...、匹配起始索引、原字符串、命名捕获组对象”
          返回值将作为替换内容
    C、返回值:
      新字符串,内容为原字符串完成所有匹配子串替换后的结果
      若无任何匹配子串,返回与原字符串完全相同的新字符串
  (18)repeat(n)
    A、作用:将原字符串重复指定的次数,生成并返回由重复结果拼接而成的新字符串
    B、参数:必需参数n,为非负整数
      若n为0,返回空字符串
      若n为小数,会自动向下取整(如3.9等同于3)
      若n为负数或Infinity,会抛出RangeError错误
    C、返回值:新字符串,内容为原字符串重复n次后的拼接结果;若n=0,返回空字符串
    D、是否改变原字符串:否
4、其他常用方法
  (19)concat(str1,str2)
    A、作用:将原字符串与一个或多个指定字符串拼接,生成并返回新的拼接字符串
    B、参数:可选参数,
      可传入一个或多个字符串(如concat(str1,str2,str3));
      若传入非字符串参数,会自动转为字符串后再拼接。
    C、返回值:新字符串,内容为原字符串依次拼接所有参数后的结果。
  (20)charAt(index)
    A、作用:获取原字符串中指定索引位置对应的字符,返回该字符
    B、参数:必需参数index,为非负整数。
      若index大于等于字符串长度或小于0,返回空字符串;
      若index为小数,会自动向下取整(如2.9等同于2)。
    C、返回值:字符串,内容为原字符串index位置的字符;若索引无效,返回空字符串
  (21)charCodeAt(index)
    A、作用:获取原字符串中指定索引位置字符的Unicode编码(十进制数),返回该编码值
    B、参数:必需参数index,规则与charAt(index)一致(索引无效时,返回NaN)。
    C、返回值:数字,为指定索引字符的Unicode编码;若索引无效,返回NaN。
  (22)padStart(length,padStr)/padEnd(length,padStr)
    A、作用:
      padStart(length,padStr):在原字符串的开头填充指定字符,使填充后的字符串总长度达到length,生成并返回新字符串
      padEnd(length,padStr):在原字符串的结尾填充指定字符,使填充后的字符串总长度达到length,生成并返回新字符串
    B、参数:
      length:必需参数,非负整数,指定补全后字符串的总长度。若length小于等于原字符串长度,不进行填充,直接返回原字符串的副本。
      padStr:可选参数,用于填充的字符串(默认值为空格'')。若padStr长度大于需要填充的字符数,会截取padStr的前N个字符(N为需要填充的数量)。
    C、返回值:新字符串,内容为原字符串开头/结尾填充padStr后、总长度为length的结果;若无需填充,返回与原字符串相同的新字符串
5、字符串常用判断
  (1)为空的判断方法
    A、if(!str.trim().length)//trim,修剪
    B、if(/^\s*$/.test(str))//只有0到多个空格时,为true
  (2)含有“某个字符”的判断方法,
    A、if(str.indexOf(item) != -1)
  (3)获取item的位置,str.indexOf(item)
  (4)获取index的项
    A、str.charAt(index)
    B、str[index]
  (5)遍历的方法,for,for…in,for…of,
    var str = 'abc';
    for (var i = 0; i < str.length; i++) {//可以没有var
      console.log( 'for循环', str[i] );
    }
    for(var index in str){//可以没有var
      console.log( 'for-in循环', index,str[index] );
    }
    for(var item of str){//可以没有var
      console.log( 'for-of循环', item );
    }
6、字符串的匹配工具—正则(元字符、修饰符、实例获取、方法、用法)
  (1)元字符
    A、\w:匹配数字、字母、下划线
    B、\W:匹配非数字、字母、下划线
    C、\n:匹配换行符
    D、.:匹配非换行符
    E、\s:匹配空格
    F、\S:匹配非空格
    G、^:匹配输入字符串的开始位置;在方括号中使用时,表示排除方括号中的所有字符
    H、$:匹配输入字符串的结尾位置
    I、*:匹配前面的子表达式0-n次
    J、+:匹配前面的子表达式1-n次
    K、?:匹配前面的子表达式0或1次
    L、\r:匹配回车
    M、[]:匹配方括号内任意一个字符,如[abc]、[a-z]、[0-9]
    N、正则元字符的使用:MAC
    varMACMatch=/^[A-Fa-f0-9]{2}(:[A-Fa-f0-9]{2}){5}$|^[A-Fa-f0-9]{2}(-[A-Fa-f0-9]{2}){5}$/,
  (2)修饰符
    A、g,使用全局匹配
    B、i,忽略大小写(3)
  (3)实例获取
    A、console.log(/ab+c/i);///ab+c/i
    B、console.log(newRegExp("ab+c","i"));///ab+c/i
    C、console.log(newRegExp(/ab+c/,"i"));///ab+c/i
  (4)方法
    A、test()
      a、用途:检测字符串是否匹配正则表达式,返回布尔值用于判断“是否存在匹配”
      b、参数:仅一个参数,为string类型,即需要检测的目标字符串
      c、返回值:boolean类型,若字符串中存在与正则匹配的内容则返回true,否则返回false
      d、示例:/^1[3-9]\d{9}$/.test("13812345678")//true
    B、exec()
      a、用途:在字符串中执行正则匹配,可获取匹配结果的详细信息(包括匹配内容、捕获组、匹配位置等)
        若正则开启全局模式(g修饰符),多次调用可依次获取所有匹配结果
      b、参数:仅一个参数,为string类型,即需要匹配的目标字符串
      c、返回值:
        匹配成功时,返回Array类型数组
          第0项,为完整匹配内容,
          第1~n项,为对应捕获组内容,同时数组包含index(匹配起始位置)、input(原目标字符串)、groups(命名捕获组对象,无则为undefined)
        匹配失败时,返回null
      d、示例:/(\w+)@(\w+)\.(\w+)/.exec("我的邮箱是test@example.com,备用邮箱是abc@domain.cn");  
  (5)用法-解析URL
    function hashA(url) {
      var reg = /#([^#&=?]+)/;
      if (reg.test(url)) {
        return reg.exec(url)[1];
      }
    }
    function queryUrlParam1(url) {
      var obj = {};
      var reg1 = /([^?=&#]+)=([^?=&#]+)/g;
      var reg2 = /#([^?=&#]+)/;
      url.replace(reg1, function(keyValue, key, value) {
        obj[key] = value;
      });
      if (reg2.test(url)) {
        obj['HASH'] = reg2.exec(url)[1];
      }
      return obj;
    }
    function queryUrlParam2(url) {
      var obj = {};
      var askText = '';
      var hashText = '';
      var askIndex = url.indexOf('?') === -1 ? url.length : url.indexOf('?');
      var hashIndex = url.indexOf('#') === -1 ? url.length : url.indexOf('#');
      if (askIndex < hashIndex) {
        askText = url.substring(askIndex + 1, hashIndex);
        hashText = url.substring(hashIndex + 1);
      } else {
        askText = url.substring(askIndex + 1);
        hashText = url.substring(hashIndex + 1, askIndex);
      }
      if (askText) {
        askText.split('&')
          .forEach(function(item) {
            let [key, value] = item.split('=');
            obj[key] = value;
          });
      }
      if (hashText) obj['HASH'] = hashText;
      return obj;
    };
    var str = "https://www.baidu.com/newspage/data/landingsuper?AAA=1111&BBB=222&CCC=333#1234"
    console.log(hashA(str));
    console.log(queryUrlParam1(str));
    console.log(queryUrlParam2(str));
  (6)用法-其它
    A、替换特定字符
      var ary=["零","一","二","三","四","五","六","七","八","九"];
      var str="今年是2017年";
      var result=str.replace(/\d/g,function(value){
        returnary[value]
      });
      console.log(result);
    B、择出汉字
      var str='<imgsrc="haha.png"alt="你的朋友"/>';
      var reg=/alt="(.+)"/;
      console.log(reg.exec(str)[1]);
7、字符串里字符重复次数
  (1)对象法:把一个字母作为对象的属性名,字母出现的次数作为属性值;进而比较属性值的大小。
    var str = 'SSSadsdccCCCAdkdkkkkDccdddaaaccCCsSSSSSs';
    function maxstr(str) {
      var obj = {};
      var maxNum = 0;
      var maxVal = "";
      for (var i = 0; i < str.length; i++) {
        var strNum = str.charAt(i)
          .toLowerCase();
        if (obj[strNum]) {
          obj[strNum]++;
        } else {
          obj[strNum] = 1;
        }
      }
      for (var attr in obj) {
        if (obj[attr] > maxNum) {
          maxNum = obj[attr];
          maxVal = attr;
        }
        elseif(obj[attr] == maxNum) {
          maxVal += attr
        }
      }
      return "出现次数最多的字母是:" + maxVal + ";次数是:" + maxNum
    }
    var valNum = maxstr(str);
    console.log(valNum);
  (2)排序法:先对它们进行排序,再取出小分组和小分组被匹配的次数。
    var str = 'SS张SadsdccCCC四Adkdkk张kkDccdddaa三acc四CCsS李SSSSs';
    function maxstr(str) {
      var maxNum = 0;
      var maxVal = "";
      str = str.split('')
        .sort(function(one, two) {
          return one.localeCompare(two)
        })
        .join('');
      console.log("排序后的字符串是:" + str);
      //下面正则的意思是:"(\w)"字符、"\1"第1个括号、"+"1次到多次、"g全局"、"i"不区分大小写
      str.replace(/(\w)\1+/gi, function(bigReg, item) {
        if (bigReg.length > maxNum) {
          maxNum = bigReg.length;
          maxVal = item;
        }
        elseif(bigReg.length == maxNum) {
          maxVal += item;
        }
      });
      return "出现次数最多的字母是:" + maxVal + ",次数" + maxNum;
    }
    var valNum = maxstr(str);
    console.log(valNum);
8、计算任意字符串的宽度
  (1)说明
    A、半角空格(英文符号)\u0020,代码中常用的;
    B、全角空格(中文符号)\u3000,中文文章中使用;
    C、英文等宽、中文2倍英文宽的字体有:"宋体"、"黑体"、"楷体"
    D、英文等宽(不含中文)的字体有:"Consolas"、"Courier"、"CourierNew"
    E、根据不同字体获取字符串宽度
  (2)方法
    function getTextWidth(fontSize, fontFamily, innerText) {
      const span = document.createElement('span');
      // 使用 cssText 合并所有内联样式(注意样式属性值需用引号包裹,避免语法错误)
      span.style.cssText = `
        position: absolute;
        visibility: hidden;
        white-space: nowrap;
        font-weight: normal;
        font-size: ${fontSize};
        font-family: ${fontFamily};
      `;
      span.textContent = innerText;
      document.body.appendChild(span);
      // 获取元素实际宽度(getBoundingClientRect() 需元素挂载到 DOM 后才有效)
      const width = span.getBoundingClientRect().width;
      document.body.removeChild(span);
      return width;
    }
    
四、数组方法(数组的方法)!!!
  (1)字符串也有的方法,如concat、includes(存在性判断,ES6新增)、indexOf(位置获取)、length(属性)、slice
  (2)参数不可以是正则
  (3)JS数组为可变类型,有的数组方法不修改原数组,有的数组方法修改原数组。返回值类型不同
  (4)字符串和数组的循环遍历都可以用,for(通过索引访问,可提前结束)、for...of(直接遍历字符,可提前结束)、for...in(主用遍历对象)
  (5)break提前结束循环遍历、continue跳过本次循环遍历
1、改变原数组
  (1)push(element1,...,elementN)
    A、作用:向数组的末尾添加一个或多个元素
    B、参数:element1,...,elementN,即要添加到数组末尾的一个或多个元素
    C、返回值:添加元素后数组的新长度(一个非负整数)
  (2)pop()
    A、作用:删除数组的最后一个元素
    B、参数:无参数
    C、返回值:被删除的那个元素;若数组为空,则返回undefined
  (3)unshift(element1,...,elementN)
    A、作用:向数组的开头添加一个或多个元素
    B、参数:element1,...,elementN,即要添加到数组开头的一个或多个元素
    C、返回值:添加元素后数组的新长度(一个非负整数)
  (4)shift()
    A、作用:删除数组的第一个元素
    B、参数:无参数
    C、返回值:被删除的那个元素;若数组为空,则返回undefined
  (5)splice(start,deleteCount,item1,...)
    A、作用:从数组的指定位置删除元素、添加元素,或同时进行删除和添加操作
    B、参数:
      start:必需,指定操作的起始索引(若为负数,会从数组末尾开始计算,如-1表示最后一个元素)
      deleteCount:必需,指定要删除的元素个数(若为0或负数,则不删除元素)
      item1,...:可选,要添加到数组中的一个或多个元素(若不指定,则仅执行删除操作)
    C、返回值:一个包含被删除元素的数组;若未删除元素,则返回空数组
  (6)sort((a,b)=>{})
    A、作用:对数组元素进行排序(默认按“字符串Unicode编码”排序,可通过自定义回调函数指定排序规则)
    B、参数:可选参数
      若不指定回调函数:先把数组项转为字符串,再按Unicode编码从小到大排序
      若指定回调函数:a和b分别代表数组中相邻的两个元素,回调函数返回值决定排序位置
        返回值<0,a排在b前面
        返回值>0,b排在a前面
        返回值=0,a和b位置不变
    C、返回值:排序后的原数组(返回的是修改后的原数组本身,而非新数组)
    D、示例,!!!
      //字符串的localeCompare方法判断位置,数组的sort方法调整位置
      //下例,如果在Unicode编码中a在b前,那么参数返回值将小于0,sort方法根据小于0,实现参数在前,数组项在前
      const fruits = ['d', 'a', 'c', 'b'];
      const sortedFruits = fruits.sort((a, b) => a.localeCompare(b)); 
  (7)reverse()
    A、作用:将数组的元素顺序反转(如[1,2,3]变为[3,2,1])
    B、参数:无参数
    C、返回值:反转后的原数组(返回的是修改后的原数组本身)
2、不改变原数组
  (8)forEach((item,index,array)=>{})
    A、作用:遍历数组的每个元素,并对每个元素执行指定的回调函数
    B、参数:仅一个必需参数,即回调函数(item,index,array)=>{}:
      item:回调函数的第一个参数,当前遍历到的数组元素
      index:回调函数的第二个参数(可选),当前遍历元素的索引
      array:回调函数的第三个参数(可选),调用forEach方法的原数组
    C、返回值:undefined(无实际返回值)
  (9)map((item)=>{})
    A、作用:遍历数组的每个元素,对每个元素执行回调函数处理,并将所有回调函数的返回值组成一个新数组(用于“数组元素转换”)
    B、参数:仅一个必需参数,即回调函数(item,index,array)=>{}(参数含义与forEach回调函数一致,item为必需,index和array可选)
    C、返回值:一个新数组,数组中的元素是原数组每个元素经过回调函数处理后的结果(新数组长度与原数组一致)
    D、是否改变原数组:否,map不修改原数组,仅返回新数组
  (10)every((item)=>{})
    A、作用:遍历数组的每个元素,判断所有元素是否都符合回调函数的条件(返回布尔值),若所有元素都符合则返回true,否则返回false
    B、参数:仅一个必需参数,即回调函数(item,index,array)=>{}(参数含义与forEach一致),回调函数需返回布尔值
    C、返回值:布尔值,
      若数组中所有元素使回调函数返回true,则返回true;
      若有任意一个元素使回调函数返回false,则立即停止遍历并返回false;
      若数组为空,返回true(空数组默认“全满足”)
  (11)some((item)=>{})
    A、作用:遍历数组的每个元素,判断是否存在至少一个元素符合回调函数的条件(返回布尔值),若存在则返回true,否则返回false
    B、参数:仅一个必需参数,即回调函数(item,index,array)=>{}(参数含义与forEach一致),回调函数需返回布尔值
    C、返回值:布尔值,
      若数组中存在至少一个元素使回调函数返回true,则立即停止遍历并返回true;
      若所有元素都使回调函数返回false,则返回false;
      若数组为空,返回false(空数组默认“无满足元素”)
  (12)filter((item)=>{})
    A、作用:遍历数组的每个元素,根据回调函数的判断条件(返回布尔值)筛选出符合条件的元素,组成一个新数组
    B、参数:仅一个必需参数,即回调函数(item,index,array)=>{}(参数含义与forEach一致),回调函数需返回一个布尔值
       true,表示保留元素
       false,表示排除元素
    C、返回值:一个新数组,包含原数组中所有使回调函数返回true的元素(新数组长度可能小于原数组);若无符合条件的元素,返回空数组
  (13)includes(item)
    A、作用:判断数组是否包含指定的元素,返回布尔值(用于“元素存在性判断”,支持NaN的判断,而indexOf不支持)
    B、参数:两个参数,第一个为必需,第二个为可选:
      item:必需参数,要判断是否存在于数组中的元素(类型不限)
      fromIndex:可选参数,指定从哪个索引开始查找(默认值为0;若为负数,从数组末尾开始计算,如-2表示从倒数第二个元素开始查找)
    C、返回值:布尔值,true表示数组包含该元素,false表示不包含
  (14)find((item)=>{})
    A、作用:遍历数组的每个元素,根据回调函数的判断条件,查找并返回第一个符合条件的元素
    B、参数:仅一个必需参数,即回调函数(item,index,array)=>{}(参数含义与forEach一致),回调函数需返回布尔值(true表示找到目标元素)
    C、返回值:原数组中第一个使回调函数返回true的元素;若遍历完数组无符合条件的元素,返回undefined
  (15)findIndex((item)=>{})
    A、作用:遍历数组的每个元素,根据回调函数的判断条件,查找并返回第一个符合条件的元素的索引(用于“查找单个符合条件元素的位置”)
    B、参数:仅一个必需参数,即回调函数(item,index,array)=>{}(参数含义与forEach一致),回调函数需返回布尔值
    C、返回值:原数组中第一个使回调函数返回true的元素的索引(非负整数);若无符合条件的元素,返回-1
  (16)indexOf(item)/lastIndexOf(item)
    A、作用:
      indexOf(item):从数组的开头(索引0)开始查找指定元素,返回该元素首次出现的索引;若不存在,返回-1
      lastIndexOf(item):从数组的末尾(最后一个元素)开始查找指定元素,返回该元素最后一次出现的索引;若不存在,返回-1
    B、参数:两个参数,第一个为必需,第二个为可选:
      item:必需参数,要查找的元素(类型不限,不支持NaN的查找,因NaN!==NaN)
      fromIndex:可选参数,indexOf中表示“从该索引开始向后查找”,lastIndexOf中表示“从该索引开始向前查找”
        默认值分别为0和数组长度-1;负数规则与includes一致
    C、返回值:整数,若找到元素则返回其对应索引(indexOf返首次索引,lastIndexOf返最后索引),若未找到则返回-1
  (17)reduce((total,item)=>{},initialValue)
    A、作用:从数组的第一个元素开始(或从指定的初始值开始),对数组元素进行“累加/累积处理”,最终将数组缩减为一个单一值
    B、参数:两个参数,第一个为必需回调函数,第二个为可选初始值:
      回调函数(total,item,index,array)=>{}:
        total:回调函数的第一个参数,“累积器”,存储上一次回调函数的返回值(或初始值initialValue)
        item:回调函数的第二个参数,当前遍历到的数组元素
        index(可选):当前元素的索引;
        array(可选):原数组
      initialValue(可选):初始值,
        若指定,则total初始化为该值,从数组第一个元素开始遍历;
        若不指定,则total初始化为数组第一个元素,从数组第二个元素开始遍历
    C、返回值:最终的累积结果(单一值,类型由回调函数逻辑决定,如数字、字符串、对象等)
    D、示例 
      var totalNum=ary.reduce(function(total,num) {
        return total+num;
      });
      var totalScore = todos.reduce(function(total, item){
        return item.completed ? total+item.score : total
      },888);
  (18)join(separator)
    A、作用:将数组的所有元素拼接成一个字符串(用于“数组转字符串”)
    B、参数:可选参数separator,指定拼接元素的分隔符(默认值为英文逗号,;若传入空字符串'',则元素直接拼接,无分隔符)
    C、返回值:一个字符串,由数组元素按顺序拼接而成,元素间用指定的separator分隔
  (19)concat(arr1,arr2)
    A、作用:将调用该方法的原数组与一个或多个数组(或值)合并,组成一个新数组(用于“数组合并”)
    B、参数:可选参数,可传入一个或多个参数,参数类型可以是数组或单个值(如concat(arr1,arr2,val1,val2))
    C、返回值:一个新数组,包含原数组的所有元素,以及所有传入参数(数组会展开为元素,单个值直接作为元素)
  (20)slice(start,end)
    A、作用:从数组的指定索引范围截取元素,组成一个新数组(用于“数组截取”,不包含结束索引对应的元素)
    B、参数:两个可选参数:
      start:起始索引(可选,默认值为0);若为负数,从数组末尾开始计算(如-1表示最后一个元素)
      end:结束索引(可选,默认值为数组长度);
        若为负数,从数组末尾开始计算;截取范围为[start,end),即包含start对应的元素,不包含end对应的元素
    C、返回值:一个新数组,包含原数组中从start到end(不包含end)的所有元素;若start>=end,返回空数组
3、数组常用判断
  (1)为空的判断方法
    A、if(!ary.length)//==长度判断法,
    B、if(JSON.stringify(ary)=="[]")//==字符串判断法,空数组里有空格,字符串里没有空格,结果也是true
  (2)含有“某个项”的判断方法
    A、if(ary.indexOf(item) != -1)//首选方案
    B、if(ary.includes(item))
  (3)获取item的位置,ary.indexOf(item)
  (4)获取index的项,ary[index]
  (5)遍历的方法,for,for…in,for…of,另有forEach、map
    var array = ['a','b'];
    for (var i = 0; i < array.length; i++) {//可以没有var
      console.log( 'for循环', array[i] );
    }
    for(var index in array){//可以没有var
      console.log( 'for-in循环', index,array[index] );
    }
    for(var item of array){//可以没有var
      console.log( 'for-of循环', item );
    }
    // const person = {
    //   name: 'Alice',
    //   age: 30,
    // };
    // const entries = Object.entries(person); //返回一个数组,其中每个元素都是一个包含对象属性名和属性值的数组
    // console.log(entries);
    // for (const [key, value] of entries) {
    //   console.log( key, value );
    // }
4、数组4种去重方法
  (1)新数组法:
    重新构建一个新数组,遍历原数组的每一项,
    如果该项在新数组的索引为-1,则把该项添加到新数组中。
    var ary = [21, 32, 32, 4, 5, 61, 61, 21, 4, 5, 61];
    function removeMany(ary) {
      varnewAry = [];
      for (vari = 0; i < ary.length; i++) {
        if (newAry.indexOf(ary[i]) === -1) {
          newAry[newAry.length] = ary[i];
        }
      }
      return newAry
    }
    console.log(removeMany(ary))
  (2)新对象法:
    如果对象中没有这一项,给对象添加属性名和属性值,
    如果对象中已经有了,说明是重复的,直接在数组中删除这一项;
    var ary = [21, 32, 32, 4, 5, 61, 61, 21, 4, 5, 61];
    function removeMany(ary) {
      varobj = {};
      for (vari = 0; i < ary.length; i++) {
        varcur = ary[i];
        if (obj[cur] === cur) { //说明重复了
          ary.splice(i, 1);
          i--;
        } else {
          obj[cur] = cur;
        }
      }
      return ary
    }
    console.log(removeMany(ary))
  (3)sort排序法:
    先用sort方法对数组进行排序,然后拿前一项与后一项进行比较,
    如果相等,则删除前一项,同时为了防止数组塌陷,下一次仍从该项开始比较。
    var ary = [21, 32, 32, 4, 5, 61, 61, 21, 4, 5, 61];
    function removeMany(ary) {
      ary.sort(function(a, b) {
        return a - b; //升序
      });
      for (vari = 0; i < ary.length; i++) {
        if (ary[i] === ary[i + 1]) {
          ary.splice(i, 1);
          i--;
        }
      }
      return ary
    }
    console.log(removeMany(ary))
  (4)双循环法:
    第一个循环,依次取出数组的每一项A;
    第二个循环,从数组的A项开始,依次与A项进行比较,如果相等则删除,同时为了防止数组塌陷,下一次仍从该项开始比较。
    var ary = [21, 32, 32, 4, 5, 61, 61, 21, 4, 5, 61];
    function removeMany(ary) {
      for (vari = 0; i < ary.length; i++) {
        varcur = ary[i];
        for (varj = i + 1; j < ary.length; j++) {
          if (cur === ary[j]) {
            ary.splice(j, 1);
            j--;
          }
        }
      }
      return ary
    }
    console.log(removeMany(ary))
5、数组3种排序方法
  (1)冒泡排序(目标:实现升序排列)
    var ary = [5, 4, 3, 2, 1];
    function bubbleSort(ary) {
      for (vari = 0; i < ary.length - 1; i++) {
        //正在进行第几次循环
        for (varj = 0; j < ary.length - i - 1; j++) {
          //本次循环需要执行几次
          if (ary[j] > ary[j + 1]) {
            vartmp = ary[j];
            ary[j] = ary[j + 1];
            ary[j + 1] = tmp;
          }
        }
        console.log(ary);
      }
      return ary;
    }
    console.log(bubbleSort(ary))
  (2)插入排序(目标:实现升序排列)
    var ary = [50, 40, 30, 20, 10];
    function insertSort(ary) {
      varleft = ary.splice(0, 1);
      for (vari = 0; i < ary.length; i++) {
        //正在进行第几次循环
        vararyAry = ary[i];
        for (varj = left.length - 1; j >= 0;) {
          //本次循环需要执行几次
          varleftAry = left[j];
          if (aryAry < leftAry) {
            j--;
            if (j === -1) {
              left.unshift(aryAry);
            }
          } else {
            left.splice(j + 1, 0, aryAry);
            break;
          }
        }
      }
      return left;
    }
    console.log(insertSort(ary));
  (3)快速排序(目标:实现升序排列,用到了递归)
    var ary = [500, 400, 300, 200, 100];
    function quickSort(ary) {
      if (ary.length <= 1) {
        return ary;
      }
      varnum = Math.floor(ary.length / 2);
      varnumValue = ary.splice(num, 1)[0];
      varleft = [];
      varright = [];
      for (vari = 0; i < ary.length; i++) {
        varcur = ary[i];
        if (cur < numValue) {
          left.push(cur);
        } else {
          right.push(cur);
        }
      }
      return quickSort(left)
        .concat([numValue], quickSort(right));
    }
    console.log(quickSort(ary));

五、对象方法(对象的方法)
1、Object静态方法(直接通过Object调用)
  (1)Object.keys()
    A、用途:获取对象自身所有可枚举属性的名称,以字符串数组形式返回(不包含继承属性和Symbol属性)
    B、参数:obj(必需),要获取属性名称的目标对象
    C、返回值:字符串数组,包含对象自身可枚举属性的名称,顺序与for...in循环遍历顺序一致(但不遍历继承属性)
  (2)Object.values()
    A、用途:获取对象自身所有可枚举属性的对应值,以数组形式返回(不包含继承属性和Symbol属性对应的值)
    B、参数:obj(必需),要获取属性值的目标对象
    C、返回值:数组,包含对象自身可枚举属性的对应值,顺序与Object.keys()返回的属性名称顺序一致
  (3)Object.entries()
    A、用途:获取对象自身所有可枚举属性的「键值对数组」,每个子数组格式为[属性名,属性值](不包含继承属性和Symbol属性)
    B、参数:obj(必需),要获取键值对的目标对象
    C、返回值:二维数组,每个子数组对应对象的一个可枚举属性,顺序与Object.keys()一致
  (4)Object.assign()
    A、用途:将一个或多个「源对象」的可枚举属性复制到「目标对象」,返回合并后的目标对象(浅拷贝,即属性值为引用类型时仅复制引用)
    B、参数:target(必需,目标对象)、source1,source2,...(可选,一个或多个源对象,后续源对象会覆盖前面源对象的同名属性)
    C、返回值:合并后的目标对象target
  (5)Object.create()
    A、用途:创建一个新对象,并指定该新对象的「原型对象」和可选的「自身属性描述符」
    B、参数:
      proto(必需,新对象的原型对象,若为null则新对象无原型)、
      propertiesObject(可选,以对象形式定义新对象的自身属性,格式需符合属性描述符规范,如{name:{value:'张三',writable:true}})
    C、返回值:新创建的对象,其原型为proto,自身属性由propertiesObject定义(若提供)
  (6)Object.freeze()
    A、用途:冻结对象,使其成为「不可变对象」——无法添加新属性、删除现有属性、修改属性值或属性描述符
      (浅冻结,若属性值为引用类型,引用类型内部仍可修改)
    B、参数:obj(必需),要冻结的目标对象
    C、返回值:被冻结后的原对象obj(冻结后对象的extensible、writable等描述符会被修改为不可变状态)
  (7)Object.isFrozen()
    A、用途:判断目标对象是否已被冻结(即是否通过Object.freeze()处理过)
    B、参数:obj(必需),要检测的目标对象
    C、返回值:布尔值,true表示对象已冻结,false表示未冻结(非对象类型会直接返回true,因原始值本身不可变)
  (8)Object.is()
    A、用途:判断两个值是否「严格相等」,修复了===运算符的两个缺陷(NaN===NaN为false,+0===-0为true)
    B、参数:value1(必需,第一个要比较的值)、value2(必需,第二个要比较的值)
    C、返回值:布尔值,true表示两个值严格相等(规则:NaN与自身相等、+0与-0不相等、其他情况同===),false表示不相等
  (9)Object.getPrototypeOf()
    A、用途:获取目标对象的「原型对象」(即[[Prototype]]内部属性的值,等同于ES5前的obj.__proto__,但更标准)
    B、参数:obj(必需),要获取原型的目标对象
    C、返回值:目标对象的原型对象,若对象无原型(如Object.create(null)创建的对象)则返回null
  (10)Object.setPrototypeOf()
    A、用途:修改目标对象的「原型对象」(即设置[[Prototype]]内部属性的值),谨慎使用(可能影响对象的继承链和性能)
    B、参数:obj(必需,要修改原型的目标对象)、proto(必需,新的原型对象,可为null)
    C、返回值:修改原型后的原对象obj
2、Object.prototype实例方法(所有对象都能调用)
  (1)obj.toString()
    A、用途:返回对象的字符串表示形式,默认返回[object类型](如{}.toString()返回"[objectObject]"),可被自定义对象重写以实现特定逻辑
    B、参数:无
    C、返回值:字符串,默认格式为"[object构造函数名]"(如数组[1,2].toString()返回"1,2",因数组重写了该方法)
  (2)obj.valueOf()
    A、用途:返回对象的原始值表示(如数字对象newNumber(123).valueOf()返回123),当对象参与原始值运算时会自动调用
    B、参数:无
    C、返回值:对象对应的原始值(如Date对象返回时间戳、Array对象返回自身而非原始值),若无法转换为原始值则返回对象本身
  (3)obj.hasOwnProperty()
    A、用途:判断对象「自身是否拥有指定属性」(不包含继承的属性,无论属性是否可枚举)
    B、参数:prop(必需,要检测的属性名,可为字符串或Symbol)
    C、返回值:布尔值,true表示对象自身拥有该属性,false表示属性是继承的或不存在
  (4)obj.isPrototypeOf()
    A、用途:判断当前对象是否是另一个对象的「原型对象」(即当前对象是否在另一个对象的原型链上)
    B、参数:anotherObj(必需,要检测的目标对象,即判断当前对象是否是该对象的原型)
    C、返回值:布尔值,true表示当前对象是anotherObj的原型(或在其原型链上),false表示不是
  (5)obj.propertyIsEnumerable()
    A、用途:判断对象「自身的指定属性是否可枚举」(不包含继承属性,仅检测自身属性的enumerable描述符)
    B、参数:prop(必需,要检测的属性名,可为字符串或Symbol)
    C、返回值:布尔值,true表示对象自身拥有该属性且属性可枚举,false表示属性不存在、是继承的或不可枚举
  (6)obj.hasOwn()
    A、用途:ES2022新增,功能与hasOwnProperty()类似,判断对象「自身是否拥有指定属性」,但更简洁且避免了hasOwnProperty被重写的风险
    B、参数:prop(必需,要检测的属性名,可为字符串或Symbol)
    C、返回值:布尔值,true表示对象自身拥有该属性,false表示属性是继承的或不存在
     (与hasOwnProperty区别:若对象重写了hasOwnProperty,hasOwn仍能正常工作)
  (7)obj.toLocaleString()
    A、用途:返回对象的「本地化字符串表示」,默认调用toString(),但部分内置对象(如Date、Number、Array)重写了该方法以适配不同地区的格式(如newDate().toLocaleString()返回本地时间格式字符串)
    B、参数:无默认强制参数,部分重写该方法的对象会接收本地化相关参数(如newNumber(1234).toLocaleString('zh-CN')返回"1,234")
    C、返回值:本地化格式的字符串,具体格式由对象类型和地区决定
3、改变this指向的三个方法
  (1)bind
    A、用途:创建一个新函数,将原函数的this永久绑定到指定对象,同时可预先传入部分参数(柯里化),新函数调用时会使用绑定的this和预设参数
    B、参数:
      第一个参数(必需):thisArg,指定新函数中this的指向(若用new调用新函数,此参数失效)
      后续参数(可选):arg1,arg2,...,预先传入的参数,新函数调用时会在实参前插入这些参数
    C、返回值:返回一个新的绑定函数,该函数与原函数逻辑一致,但this指向固定,且包含预设参数
  (2)call
    A、用途:立即调用原函数,强制将函数执行时的this绑定到指定对象,并传入参数列表,用于临时改变this指向并执行函数
    B、参数:
      第一个参数(必需):thisArg,指定函数执行时this的指向(非严格模式下为null/undefined时,this指向全局对象)
      后续参数(可选):arg1,arg2,...,以逗号分隔的参数列表,作为原函数的实参
    C、返回值:返回原函数的执行结果(若原函数无返回值,则返回undefined)
  (3)apply
    A、用途:立即调用原函数,强制将函数执行时的this绑定到指定对象,并传入参数数组(或类数组对象),功能与call类似,仅参数传递方式不同
    B、参数:
      第一个参数(必需):thisArg,同call,指定函数执行时this的指向
      第二个参数(可选):argsArray,参数数组或类数组对象(如[arg1,arg2]),数组元素作为原函数的实参
    C、返回值:返回原函数的执行结果(与call一致)
  (4)核心区别总结
    A、调用时机:
      bind返回新函数(需手动调用),
      call和apply会立即执行原函数
    B、参数传递:
      call接收逗号分隔的参数列表,
      apply接收参数数组(或类数组),
      bind既可以预设参数也可在调用新函数时补充参数(预设参数在前,新参数在后)
    C、this绑定:三者都能改变this指向,
      bind的绑定是永久的(新函数的this无法再次修改),
      call和apply的绑定仅在当前调用有效  
4、对象常用判断
  附、属性获取说明
    A、Object.keys(仅含可枚举属性)
    B、Object.getOwnPropertyNames(还含不可枚举属性)
    C、Object.hasOwnProperty(还含不可枚举属性)
  附、属性可枚举说明   
    A、for...in,遍历的属性
    B、Object.keys(),访问的属性
    C、描述符(enumerable)决定,默认为true
    D、自身属性、原型链属性:有的可枚举,有的不可枚举
  (1)为空的判断方法
    A、if(!Object.keys(obj).length)//==长度判断法1/2
    B、if(!Object.getOwnPropertyNames(obj).length)//==长度判断法2/2
    C、if(JSON.stringify(obj)=="{}")//==字符串判断法,空对象里有空格,字符串里没有空格,结果也是true
  (2)含有“某个键”的判断方法
    A、if(key in obj)//------等同于if(Reflect.has(obj, name))
    B、if(obj.hasOwnProperty(key))
    注意,if(obj.key)不可以,因为排除了obj={ key: 0 || false || null || undefined }
  (3)获取key属性项的位置
    A、对象里面的项是无序的,如果有位置要求,则应该写成数组形式
  (4)获取index位置的项
    A、对象里面的项是无序的,如果有位置要求,则应该写成数组形式
  (5)遍历的方法
    A、示例
      var object = {
        1: 1,
        '2': 2,
        true: 3,
        null: 4,
        undefined: 5,
      };
      for(var key in object){
        console.log( object[key], typeof key, key, );
      }
      Object.keys(object).forEach(key => {
        console.log( object[key], typeof key, key, );
      });
      Object.entries(object).forEach(([key, value]) => {
        console.log( value, typeof key, key, );
      });
    B、问题与原因
      a、问题:typeof key,的结果都是string
      b、原因:JS对象的属性名只能是字符串或Symbol,其他类型,引擎会隐式调用.toString()将其转换为字符串
    C、解决:用Map,任意唯一键对象
      const map = new Map([
        [1, 2],
        [1, 1], //覆盖前一个
        ['2', 2],
        [true, 3],
        [null, 4],
        [undefined, 5],
      ]);
      for (const [key, value] of map) {
        console.log(value, key, typeof key);
      }
5、对象相等的判断方法
  // 封装一个判断两个js数据是否相等的方法
  //(1)方法1
  function isDeepEqualA(aaa, bbb) { 
    if (typeof aaa !== typeof bbb || Array.isArray(aaa) !== Array.isArray(bbb)) { 
      //以下,A、数据类型不同;B、数据类型相同,且1个是数组,1个不是数组(是对象)
      return false;
    } else if (Array.isArray(aaa)) { //以下,2个同为数组
      for (let i = 0; i < aaa.length; i++) {
        if (!isDeepEqualA(aaa[i], bbb[i])) {
          return false;
        }
      }
    } else if (typeof aaa === 'object' && aaa !== null && bbb !== null) { //以下,2个都为对象(都不为null)
      const aaaKeys = Object.keys(aaa).sort();
      const bbbKeys = Object.keys(bbb).sort();
      if (JSON.stringify(aaaKeys) !== JSON.stringify(bbbKeys)) {
        return false;
      }
      for (let key of aaaKeys) {
        if (!isDeepEqualA(aaa[key], bbb[key])) {
          return false;
        }
      }
    } else { //以下,A、2个同为基本类型;B、2个同为对象,且1个为null,1个(不)为null
      return aaa === bbb;
    }
    return true;
  }
  //(2)方法2
  function isDeepEqualB(aaa, bbb) {
    if (Object.prototype.toString.call(aaa) !== Object.prototype.toString.call(bbb)) { //以下,2个不同类型的数据
      return false;
    } else if (Object.prototype.toString.call(aaa) === "[object Array]") { //以下,2个同为数组类型的数据
      for (let i = 0; i < aaa.length; i++) {
        if (!isDeepEqualB(aaa[i], bbb[i])) {
          return false;
        }
      }
    } else if (Object.prototype.toString.call(aaa) === "[object Object]") { //以下,2个同为对象类型的数据
      const aaaKeys = Object.keys(aaa).sort();
      const bbbKeys = Object.keys(bbb).sort();
      if (JSON.stringify(aaaKeys) !== JSON.stringify(bbbKeys)) {
        return false;
      }
      for (let key of aaaKeys) {
        if (!isDeepEqualB(aaa[key], bbb[key])) {
          return false;
        }
      }
    } else { //以下,2个同为基本类型或同为null的数据
      return aaa === bbb;
    }
    return true;
  }
  //(3)数据
  var flag = true;
  if(flag){
    console.log( '正在使用A组数据' );
    var aaa = [ 0,  1,  2,  3 ];
    var bbb = { 0:0, 1:1, 2:2, 3:3 };
  }else{
    console.log( '正在使用B组数据' );
    var aaa = { a: 1, b: 'b', c: false, d: null, };
    var bbb = { a: 1, b: 'b', c: false, d: null, e: undefined};
  }
  //(4)比较
  console.log('用,isDeepEqualA,比较',isDeepEqualA(aaa, bbb)); //false
  console.log('用,isDeepEqualB,比较',isDeepEqualB(aaa, bbb)); //false
6、对象浅克隆
  (1)特点
    A、复制对象第一层基本类型的值
    B、复制对象第一层引用类型的引用地址
    C、新旧对象第一层项目增减与改变都不影响对方
  (2)数组的浅克隆方法
    A、slice
    B、concat
    C、[...arr]
    D、Array.from(arr)
  (3)对象的浅克隆方法
    A、Object.assign({}, obj)
    B、{...obj}
7、对象深克隆(含递归)
  //以下,定义3个深克隆方法
  function deepCloneA(arrayOrObject) { //方法1,value为undefined的键值对将消失
    return JSON.parse(JSON.stringify(arrayOrObject));
  } 
  function deepCloneB(arrayOrObject) { //方法2
    var target = null;
    //{ }.toString.call也可以用Object.prototype.toString.call代替
    if ({}.toString.call(arrayOrObject) === "[object Array]") target = [];
    if ({}.toString.call(arrayOrObject) === "[object Object]") target = {};
    for (var key in arrayOrObject) {
      var value = arrayOrObject[key];
      if ({}.toString.call(value) === "[object Array]" || {}.toString.call(value) === "[object Object]" ) {
        target[key] = deepCloneB(value); //递归
      } else {
        target[key] = value;
      }
    }
    return target;
  } 
  function deepCloneC(obj) { //方法3
    if (typeof obj !== 'object' || obj === null) { // 如果不是对象或者是 null,直接返回
      return obj;
    }
    if (Array.isArray(obj)) { // 处理数组
        const arrCopy = [];
        for (let i = 0; i < obj.length; i++) {
          arrCopy[i] = deepCloneC(obj[i]); //递归
        }
        return arrCopy;
    }
    const objCopy = {}; // 处理对象
    for (const key in obj) {
      if (obj.hasOwnProperty(key)) {
        objCopy[key] = deepCloneC(obj[key]);
      }
    }
    return objCopy;
  }
  //以下,数据
  var aaa = { a: 1, b: 'b', c: false, d: null, e: undefined};
  var bbb = { a: 1, b: { c: 3 }, d: { e: [1, 2] } };
  //以下,运行效果
  console.log('用,deepCloneA,JSON简易克隆');
  console.log(deepCloneA(aaa)); 
  console.log(deepCloneA(bbb));
  console.log('用,deepCloneB,不区分数据类型克隆');
  console.log(deepCloneB(aaa)); 
  console.log(deepCloneB(bbb));
  console.log('用,deepCloneC,区分数据类型克隆');
  console.log(deepCloneC(aaa)); 
  console.log(deepCloneC(bbb));

六、两行布局
1、固定高,此增彼减;flex实现
  <!DOCTYPE html>
  <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>Title</title>
      <style>
        * {
          margin: 0;
          padding: 0;
        }
        .full {
          height: 600px;
          background: red;
          display: flex;/* 相关代码 */
          flex-direction: column;/* 相关代码 */
        }
        .up {
          background: blue;
        }
        .down {
          background: green;
          flex: 1;/* 相关代码 */ /*height:100%;*/ 
        }
      </style>
    </head>
    <body>
      <div class="full">
        <div class="up">
          <p>ABCD</p>
        </div>
        <div class="down">
          <p>DCBA</p>
        </div>
      </div>
    </body>
  </html>
2、正好占满全屏
  (1)calc实现,先上,后下左、下右
    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <style>
          * {
            padding: 0;
            margin: 0;
          }
          html, body {
            height: 100%;
          }
          .up {
            height: 50px;
            background: #BBE8F2;
            width: 100%;
          }
          .down {
            height: calc(100% - 60px);/* 相关代码 */
            background: #D9C666;
            margin-top: 10px;
            font-size: 0;
            display: flex;/* 相关代码 */
          }
          .left {
            width: 200px;
            height: 100%;
            overflow: auto;
            background: palevioletred;
            margin-right: 10px;
            font-size: 16px;
          }
          .right {
            width: calc(100% - 210px);/* 相关代码 */
            height: 100%;
            overflow: auto;
            background: rebeccapurple;
            font-size: 16px;
          }
        </style>
      </head>
      <body>
        <div class="up"></div>
        <div class="down">
          <div class="left">
          </div>
          <div class="right">
          </div>
        </div>
      </body>
    </html>
  (2)flex实现,先上,后下左、下右
    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <style>
          * {
            padding: 0;
            margin: 0;
          }
          html, body {
            height: 100%;
          }
          .body {
            display: flex;
            flex-direction: column;
          }
          .up {
            height: 50px;
            background: #BBE8F2;
            width: 100%;
          }
          .down {
            flex: 1;
            background: #D9C666;
            font-size: 0;
            display: flex;
          }
          .left {
            width: 200px;
            height: 100%;
            overflow: auto;
            background: rebeccapurple;
            font-size: 16px;
            margin-right: 10px;
          }
          .right {
            flex: 1;
            overflow: auto;
            background: rebeccapurple;
            font-size: 16px;
          }
        </style>
      </head>
      <body class="body">
        <div class="up"></div>
        <div class="down">
          <div class="left"></div>
          <div class="right"></div>
        </div>
      </body>
    </html>
    
七、两列布局 
  附、两个需求 
    (1)左侧固定宽、右侧占满余屏 
    (2)左、右自适应高 
  附、四个实现 
    (1)table 
    (2)flex 
    (3)内外补+浮动 
    (4)大高赋给小高 
1、table 
  <!DOCTYPE html>
  <html lang="en">
    <head>
      <title>两列高度自适应</title>
      <style type="text/css">
        * { margin: 0; padding: 0; }
      </style>
    </head>
    <body>
      <table style="width:100%">
        <tr>
          <td style="width:200px;background: #ddd;">
            <br/>
          </td>
          <td style="width:calc(100% - 200px);background: #ddd;">
            <br/>
            <br/>
            <br/>
            <br/>
          </td>
        </tr>
      </table>
      <p>我是尾部,我是尾部,我是尾部,我是尾部,我是尾部,我是尾部!</p>
    </body>
  </html> 
2、flex
  <!DOCTYPE html>
  <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title></title>
      <style>
        * { margin: 0; padding: 0; }
      </style>
    </head>
    <body>
      <div style="display:flex">
        <div style="width:200px;background: #ddd;margin-right: 10px;"><br/></div>
        <div style="flex:1;background: #ddd;"><br/><br/><br/><br/><br/></div>
      </div>
    </body>
  </html>   
3、内外补+浮动
  <!DOCTYPE html>
  <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>两列高度自适应</title>
      <style type="text/tailwindcss">
        #main {
          /* 
            创建BFC(块级格式化上下文),包含内部浮动元素的高度,防止高度塌陷。
            BFC的高度计算会包含其内部浮动元素的完整高度(内容+内边距+边框)。
            overflow:hidden同时隐藏超出容器的内容(包括扩展的背景区域)。
          */
          overflow: hidden;
        }
        .both {
          padding-bottom: 10000px; /* 扩展元素的背景区域(默认包含内容区和内边距) */
          background: #ddd;
          margin-bottom: -10000px; /* 
            负边距会将元素在文档流中的位置向上偏移,抵消padding-bottom增加的高度,
            防止父容器被撑开。但背景色已随padding扩展,负边距不会影响背景的显示范围。
          */
        }
        .left {
          /* 浮动元素脱离文档流并向左排列,实现左列布局。
            浮动会触发BFC,但此处父容器的BFC由#main的overflow:hidden创建。 */
          float: left;
          width: 200px;
        }
        .right {
          margin-left: 210px; /* 与左侧浮动元素保持10px间距,形成两列布局 */
        }
      </style>
    </head>
    <body>
      <div id="main">
        <div class="left both">
          <p>我是左边!</p>
          <p>我是左边!</p>
          <p>我是左边!</p>
          <p>我是左边!</p>
          <p>我是左边!</p>
          <p>我是左边!</p>
          <p>我是左边!</p>
          <p>我是左边!</p>
        </div>
        <div class="right both">
          <p>我是右边!</p>
          <p>我是右边!</p>
          <p>我是右边!</p>
        </div>
      </div>
      <p>我是尾部,我是尾部,我是尾部,我是尾部,我是尾部,我是尾部!</p>
    </body>
  </html>
4、大高赋给小高
  (1)浮动
    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8">
        <title></title>
        <style>
          * {
            margin: 0;
            padding: 0;
          }
          #left {
            float: left;
            width: 200px;
            background: #ddd;
          }
          #right {
            margin-left: 210px;
            background: #ddd;
          }
        </style>
      </head>
      <body>
        <div>
          <div id="left"><br/><br/><br/></div>
          <div id="right"><br/><br/><br/><br/><br/><br/><br/></div>
        </div>
        <p>我是尾部,我是尾部,我是尾部,我是尾部,我是尾部,我是尾部!</p>
      </body>
    </html>
    <script type="text/javascript">
      var left = document.getElementById("left");
      var right = document.getElementById("right");
      var leftHeight = parseFloat(getComputedStyle(left).height);
      var rightHeight = parseFloat(getComputedStyle(right).height);
      var distanceHeight = leftHeight - rightHeight;
      if (distanceHeight > 0) {
        right.style.height = leftHeight + "px";//right.style.height =rightHeight +distanceHeight+ "px";
        //right.style.height只能赋值,不能获取,所以此处用rightHeight代替right.style.height      
      } else {
        left.style.height = rightHeight + "px";//left.style.height = leftHeight-distanceHeight + "px";
      }
    </script>
  (2)定位
    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8">
        <title>左右固定宽,中间自适应</title>
        <style type="text/css">
          * {
            margin: 0;
            padding: 0;
          }
          .left {
            top: 0;
            left: 0;
            position: absolute;
            width: 200px;
            background: #ddd;
          }
          .right {
            top: 0;
            left: 210px;
            right: 0;
            position: absolute;
            background: #ddd;
          }
        </style>
      </head>
      <body>
        <div id="left" class="left"><br/><br/><br/><br/><br/><br/></div>
        <div id="right" class="right"><br/></div>
      </body>
    </html>
    <script type="text/javascript">
      var left = document.getElementById("left");
      var right = document.getElementById("right");
      var leftHeight = parseFloat(getComputedStyle(left).height);
      var rightHeight = parseFloat(getComputedStyle(right).height);
      var distanceHeight = leftHeight - rightHeight;
      if (distanceHeight > 0) {
        right.style.height = leftHeight + "px";//right.style.height =rightHeight +distanceHeight+ "px";
        //right.style.height只能赋值,不能获取,所以此处用rightHeight代替right.style.height      
      } else {
        left.style.height = rightHeight + "px";//left.style.height = leftHeight-distanceHeight + "px";
      }
    </script>
5、jQuery实现多div等高(与方案4相似)
  $(function() {
    var maxHeight = 0;
    //以下求最高的div的高度
    $(".height").each(function() {
      var thisHeight = $(this).innerHeight();
      maxHeight = thisHeight > maxHeight ? thisHeight : maxHeight;
    })
    //以下将最高的div的高度赋值给每一个(需要与最高div等高的)div,
    $(".height").each(function() {
      var thisPadding = $(this).innerHeight() - $(this).height();
      //在jQuery中,innerheight=padding+内容height
      $(this).height(maxHeight - thisPadding);
    })
  })

八、三列布局
  附、两个需求
    (1)左右两侧固定宽、中间占满余屏
    (2)左、中、右自适应高
  附、四个实现
    (1)table
    (2)flex
    (3)内外补+浮动
    (4)大高赋给小高
1、table
  <!DOCTYPE html>
  <html lang="en">
    <head>
      <title>两列高度自适应</title>
      <style type="text/css">
        * {
          margin: 0;
          padding: 0;
        }
      </style>
    </head>
    <body>
    <table style="width:100%">   
      <tr>
        <td style="width:200px;background: #ddd;"><br/></td>
        <td style="width:calc(100% - 400px);background: #ddd;"><br/><br/><br/><br/></td>
        <td style="width:200px;background: #ddd;"><br/></td>
      </tr>
    </table>
    <p>我是尾部,我是尾部,我是尾部,我是尾部,我是尾部,我是尾部!</p>
    </body>
  </html>
2、flex
  <!DOCTYPE html>
  <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>左右固定宽,中间自适应</title>
      <style>
        * {
          margin: 0;
          padding: 0;
        }
        .flex {
          display: flex;
        }
        .leftAndRight{
          width: 200px;
          background: #ddd;
        }
        .middle{
          flex: 1;
          margin:0 10px;
          background: #ddd;
        }
      </style>
    </head>
    <body>
      <div class="flex">
        <div class="leftAndRight"><br/></div>
        <div class="middle"><br/><br/><br/><br/></div>
        <div class="leftAndRight"><br/></div>
      </div>
    </body>
  </html>
3、内外补+浮动
  <!DOCTYPE html>
  <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>三列高度自适应</title>
      <style type="text/css">
        * {
          margin: 0;
          padding: 0;
        }
        #main {
          overflow: hidden;
        }
        .three{
          background: #ddd;
          padding-bottom: 10000px;
          margin-bottom: -10000px;
          float: left; 
        }
        .leftAndRight {
          width: 200px;
        }
        .middle {
          margin-left: 10px;
          margin-right: 10px;
          width:calc(100% - 420px)
        }
      </style>
    </head>
    <body>
      <div id="main">
        <div class="leftAndRight three"><br/></div>
        <div class="middle three"><br/><br/><br/><br/><br/></div>
        <div class="leftAndRight three"><br/></div>
      </div>
      <p>我是尾部,我是尾部,我是尾部,我是尾部,我是尾部,我是尾部!</p>
    </body>
  </html>
4、大高赋给小高
  (1)浮动
    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8">
        <title>三列高度自适应</title>
        <style type="text/css">
          * {
            margin: 0;
            padding: 0;
          }
          .three{
            background: #ddd;
            float: left; 
          }
          .leftAndRight {
            width: 200px;
          }
          .middle {
            margin-left: 10px;
            margin-right: 10px;
            width:calc(100% - 420px)
          }
        </style>
      </head>
      <body>
        <div>
          <div class="leftAndRight three" id="left"><br/></div>
          <div class="middle three" id="middle"><br/><br/><br/><br/><br/></div>
          <div class="leftAndRight three" id="right"><br/></div>
        </div>
        <p>我是尾部,我是尾部,我是尾部,我是尾部,我是尾部,我是尾部!</p>
      </body>
    </html>
    <script type="text/javascript">
      var left = document.getElementById("left");
      var middle = document.getElementById("middle");
      var right = document.getElementById("right");
      var leftHeight=parseFloat(getComputedStyle(left).height);
      var middleHeight=parseFloat(getComputedStyle(middle).height);
      var rightHeight=parseFloat(getComputedStyle(right).height);
      var maxHeight=0;
      if(leftHeight-middleHeight>0){
          maxHeight= leftHeight;
      }else{
          maxHeight= middleHeight;
      }
      if(rightHeight-maxHeight>0){
          maxHeight= rightHeight;
      }
      left.style.height =maxHeight+ "px";
      middle.style.height =maxHeight+ "px";
      right.style.height =maxHeight+ "px"; 
    </script>
  (2)定位
    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8">
        <title>左右固定宽,中间自适应</title>
        <style type="text/css">
          * {
            margin: 0;
            padding: 0;
          }
          .leftAndRight {
            top: 0;
            width: 200px;
            position: absolute;
            background: #ddd;
          }
          .left {
            left: 0;
          }
          .right {
            right: 0;
          }
          .middle {
            top: 0;
            left: 210px;
            right: 210px;
            background: #ddd;
            position: absolute;
          }
        </style>
      </head>
      <body>
        <div id="left" class="left leftAndRight"><br/><br/></div>
        <div id="middle" class="middle"><br/><br/><br/><br/><br/><br/></div>
        <div id="right" class="right leftAndRight"><br/></div>
      </body>
    </html>
    <script type="text/javascript">
      var left = document.getElementById("left");
      var middle = document.getElementById("middle");
      var right = document.getElementById("right");
      var leftHeight=parseFloat(getComputedStyle(left).height);
      var middleHeight=parseFloat(getComputedStyle(middle).height);
      var rightHeight=parseFloat(getComputedStyle(right).height);
      var maxHeight=0;
      if(leftHeight-middleHeight>0){
        maxHeight= leftHeight;
      }else{
        maxHeight= middleHeight;
      }
      if(rightHeight-maxHeight>0){
        maxHeight= rightHeight;
      }
      left.style.height =maxHeight+ "px";
      middle.style.height =maxHeight+ "px";
      right.style.height =maxHeight+ "px";
      console.log(maxHeight)  
    </script>
 
九、五种盒子四方居中
1、4种定位四方居中
  <!DOCTYPE html>
  <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>4种定位四方居中</title>
      <style>
        #mask1_1 {
          width: 100%;
          height: 100%;
          position: fixed;
          left: 0;
          top: 0;
          display: flex;
          justify-content: center;
          align-items: center;
        }
        #content1_1{
          background-color: gray;
        }
        #mask2_4 {
          width: 100%;
          height: 100%;
          position: fixed;
          left: 0;
          top: 0;
        }
        #content2_2{
          position: fixed;
          left: 50%;/* 父级宽度的50% */
          top: 50%;
          transform: translate(-50%, -50%);/* 自身宽度的50% */
          background-color: gray;
        }
        #content3_3{
          position: fixed;
          top: 0;
          left: 0;
          right: 0;
          bottom: 0;
          margin: auto;
          width: 400px;
          height: 300px;
          background-color: gray;
        }
        #content4_4{
          position: fixed;
          background-color: gray;
        }
      </style>
    </head>
    <body>
      <div id="mask1_1"><div id="content1_1">居中一</div></div>
      <!-- <div id="mask1_1"><div id="content1_1">居中一</div></div> -->
      <!-- <div id="mask2_4"><div id="content2_2">居中二</div></div> -->
      <!-- <div id="mask2_4"><div id="content3_3">居中三</div></div> -->
      <!-- <div id="mask2_4"><div id="content4_4">居中四</div></div> -->
    </body>
  </html>
  <script>
    var oDiv = document.getElementById('content4_4');
    if(oDiv){
      var clientWidth = document.documentElement.clientWidth || document.body.clientWidth;
      var clientHeight = document.documentElement.clientHeight || document.body.clientHeight;
      oDiv.style.left = (clientWidth - oDiv.clientWidth) / 2 + 'px';
      oDiv.style.top = (clientHeight - oDiv.clientHeight) / 2 + 'px';
    }
  </script>
2、1种无定位四方居中
  <!DOCTYPE html>
  <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>CSS无定位居中</title>
      <style>
        .outer{
          width: 1000px;
          height: 500px;
          background: gray;
          text-align: center;/* 此处,左右居中 */
        }
        /* 以下,上下居中 */
        .middleShow{
          width: 200px;
          height: 100px;
          display: inline-block;
          vertical-align: middle;
          /* 以下,标识区域 */
          background: gainsboro;
        }
        .middleHide{
          width: 0;
          height: 100%;
          display: inline-block;
          vertical-align: middle;
        }
      </style>
    </head>
    <body>
      <div class="outer">
        <div class="middleShow"></div>
        <div class="middleHide"></div>
      </div>
    </body>
  </html>
3、三种横向换行也居中
  <!DOCTYPE html>
  <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>3种横向换行也居中</title>
      <style>
        .center{
          display: flex;
          flex-wrap: wrap;
          flex-direction: column;
          border: 1px solid green;
          align-items: center;/* 独特之处 */
        }
        .center1{
          display: flex;
          flex-wrap: wrap;
          flex-direction: row;
          border: 1px solid green;
          justify-content: center;/* 独特之处 */
        }
        .center2{
          display: flex;
          flex-wrap: wrap;
          flex-direction: row;
          border: 1px solid green;
          justify-content: space-around;/* 独特之处 */
        }
        .center3{
          display: flex;
          flex-wrap: wrap;
          flex-direction: row;
          border: 1px solid green;
          justify-content: space-between;/* 独特之处 */
        }
        .center > div,.center1 > div,.center2 > div,.center3 > div{
          width: 400px;
          height: 80px;
          border: 1px solid red;
        }
      </style>
    </head>
    <body>
      <div class="center">
        <div>竖向换行也居中</div>
        <div></div>
      </div>
      <div class="center1">
        <div>横向换行也居中-1,之间无间距</div>
        <div>justify-content: center</div>
      </div>
      <div class="center2">
        <div>横向换行也居中-2,之间大间距</div>
        <div>justify-content: space-around</div>
      </div>
      <div class="center3">
        <div>横向换行也居中-3,之间超大间距</div>
        <div>justify-content: space-between</div>
      </div>
    </body>
  </html>

十、em、rem自适应
1、em示例
  <!DOCTYPE html>
  <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>em示例</title>
      <style>
        .parent {
          font-size: 30px
        }
        .child {
          width: 40em;
          height: 10em;
          background: #888888;
          padding: 1em;
        }
      </style>
    </head>
    <body>
      <div class="parent">
        <div class="child">
            em的计算规则:<br>
            1、用于font-size属性时,em基于父元素的字体大小<br>
            2、用于其他属性,如(margin: 0.5em 0;)时,em基于当前元素自身的font-size<br>
        </div>
      </div>
    </body>
  </html>
2、rem自适应示例
  (1)rem自适应示例之css版(实用程度低)
    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8">
        <title>rem自适应示例之css版(实用程度低)</title>
        <style>
            html { font-size: 20px; }
          /* :root { font-size: 20px; } */
        </style>
      </head>
      <body>
        <div style="font-size: 1.6rem">
          <div style="width: 50rem;height:18rem;background: red;border:1px solid green;border-radius:10px;font-family:'楷体';padding-left:20px;">
            <p>根字体的大小即1rem,用rem做单位的都是相对于根字体大小的。</p>
            <p>CSS设置根字体,有2种方法,见style标签</p>
          </div>
        </div>
      </body>
    </html>
  (2)rem自适应示例之js版(实用程度高)
    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8">
        <title>rem自适应示例之js版(实用程度高)</title>
      </head>
      <body>
        <div style="font-size: 16rem">
          <div style="width: 500rem;height:180rem;background: red;border:1px solid green;border-radius:10px;font-family:'楷体';padding-left:20px;">
            <p>根字体的大小即1rem,用rem做单位的都是相对于根字体大小的。</p>
            <p>下面把1rem定义为:(屏幕宽度/设计稿的宽度)+"px"。</p>
            <p>document.documentElement.style.fontSize=</p>
            <p>document.documentElement.clientWidth/750+"px";</p>
          </div>
        </div>
      </body>
    </html>
    <script>
      var designWidth = 1000 ;
      document.documentElement.style.fontSize = document.documentElement.clientWidth/designWidth+"px";
    </script>
 
十一、Flex布局和grid布局的区别
1、Flex布局,W3C在2009年提出的一种布局方案,子元素的float、clear和vertical-align属性失效
  <!DOCTYPE html>
  <html lang="zh-CN">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>Flex 布局</title>
      <style>
        .flex-container {
          display: flex;
          /* 1. 以上,定义弹性盒子,取值如下
            (1)flex:生成弹性盒子网格容器
            (2)inline-flex:生成行内弹性盒子容器 */
          flex-direction: row;
          /* 2. 以上,主轴方向,取值如下
            (1)row(默认值):主轴为水平方向,起点在左端
            (2)row-reverse:主轴为水平方向,起点在右端
            (3)column:主轴为垂直方向,起点在上沿
            (4)column-reverse:主轴为垂直方向,起点在下沿 */
          flex-wrap: wrap; 
          /* 3. 以上,主轴换行,取值如下
            (1)nowrap(默认):不换行
            (2)wrap:换行,第一行在上方
            (3)wrap-reverse:换行,第一行在下方 */
          justify-content: space-between; 
          /* 4. 以上,主轴对齐方式,取值如下
            (1)flex-start(默认值):主轴起点对齐
            (2)flex-end:主轴终点对齐
            (3)center: 主轴居中对齐,之间0间距
            (4)space-around:主轴居中对齐,之间小间距,项目与外边框的间隔,是项目之间间隔的一半
            (5)space-between:主轴两端对齐,之间大间距,项目与外边框的间隔,是0 */
          align-items: center;
          /* 5. 以上,交叉轴对齐方式,取值如下
            (1)flex-start:交叉轴的起点对齐
            (2)flex-end:交叉轴的终点对齐
            (3)center:交叉轴的中点对齐
            (4)baseline: 项目的第一行文字的基线对齐
            (5)stretch(默认值):如果项目未设置高度或设为auto,将占满整个容器的高度 */
          gap: 10px;
          /* 6. 以上,子项间距,也可分开设置,取值如下
            (1)row-gap:行间距
            (2)column-gap:列间距 */
          height: 200px;
          border: 1px solid #ddd;
          padding: 10px;
          background: #f5f5f5;
        }
        /* 子项样式 */
        .item {
          background: #999999;
          color: white;
          padding: 20px;
          text-align: center;
          border-radius: 4px;
        }
      </style>
    </head>
    <body>
      <div class="flex-container">
      <div class="item">Item 1</div>
      <div class="item">Item 2</div>
      <div class="item">Item 3</div>
    </div>
    </body>
  </html>
2、grid布局
  <!DOCTYPE html>
  <html lang="zh-CN">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>Grid 布局</title>
      <style>
        body {
          font-family: Arial, sans-serif;
          margin: 0;
          padding: 20px;
          background-color: #f5f5f5;
        }
        h2 {
          color: #333;
          border-bottom: 2px solid #666;
          padding-bottom: 5px;
        }
        .container {
          background-color: #fff;
          border-radius: 8px;
          padding: 20px;
          margin-bottom: 30px;
          box-shadow: 0 2px 5px rgba(0,0,0,0.1);
        }
        .grid-container {
          display: grid;
          /* 1. 以上,定义网格容器;说明如下 */
          /* display: grid; 定义网格容器类型,取值如下
            (1)grid:生成块级网格容器
            (2)inline-grid:生成行内网格容器 */
          grid-template-rows: 80px 1fr 60px; /* 定义行数量和高度 */
          grid-template-columns: 400px 1fr; /* 定义列数量和宽度 */
          /* 2. 以上,定义行数量和高度、列数量和宽度,取值如下 
            (1)固定值:100px / 20% / 5em
            (2)弹性单位:1fr(剩余空间分配比例)
            (3)函数:
                - repeat(3, 1fr),重复3列,每列1fr
                - repeat(auto-fill, minmax(100px, 1fr)); 
                //auto-fill,自动填充尽可能多的列,即使某些列没有内容
                //minmax(100px, auto),列宽最小100px,最大自适应
                //minmax(100px, 1fr),列宽最小100px,最大为1fr(等分剩余空间)
            (4)auto:由浏览器决定 */
          grid-template-areas: /* 在grid容器内,多个header类div只生效第1个div;不管该div是第几个子级,都会渲染到第1行 */
            "header header" /* 第一行:header占据两列 */
            "sidebar main" /* 第二行:sidebar和main各占一列 */
            "footer footer"; /* 第三行:footer 占据两列 */
          /* 3. 以上,定义网格区域(可视化布局),通过命名区域定义布局;说明如下 
            (1)每行用引号定义,空格分隔区域
            (2)相同名称区域会被合并 */
          gap: 10px;
          /* 4. 以上,网格项间距,也可分开设置;说明如下
            (1)row-gap:行间距
            (2)column-gap:列间距 */
          justify-content: center; /* 网格水平对齐方式 */
          align-content: center; /* 网格垂直对齐方式 */
          /* 5. 以上,网格对齐方式,取值如下 
            (1)start:左对齐
            (2)end:右对齐
            (3)center:居中对齐
            (4)space-around:居中对齐(两侧有间隙)
            (5)space-between:两端对齐(无外侧间隙)
            (6)space-evenly:完全均匀分布 */
          justify-items: stretch; /* 网格项水平对齐方式 */
          align-items: center;  /* 网格项垂直对齐方式 */
          /* 6. 以上,网格项对齐方式,取值如下
            (1)start:左对齐
            (2)end:右对齐
            (3)center:居中对齐
            (4)stretch(默认值):拉伸填满 */
          height: 80vh;
          background: #999999;
          padding: 10px;
        }
        /* 网格项样式 */
        .item {
          background-color: #888888;
          color: white;
          padding: 15px;
          text-align: center;
          border-radius: 4px;
        }
        /* 网格项与grid-template-areas绑定 */
        .header { grid-area: header;  }
        .sidebar { grid-area: sidebar; }
        .main { grid-area: main; }
        .footer { 
          grid-area: footer; 
          /* justify-self: start;单个网格项的水平对齐,覆盖justify-items,取值同justify-items */
          /* align-self: end;单个网格项的垂直对齐,覆盖align-items,取值同align-items */
        }
      </style>
    </head>
    <body>
      <div class="grid-container">
        <div class="item header">第一行:header 占据两列</div>
        <div class="item sidebar">
          <div>第二行:sidebar 占一列</div>
          <div>第二行:sidebar 占一列</div>
        </div>
        <div class="item main">第二行:main占一列</div>
        <div class="item footer">第三行:footer 占据两列</div>
      </div>
    </body>
  </html> 

十二、多图片延迟加载下载
  附、真实案例
    const getDetailInfo = () => {
      changeRecord({ sid: route.query.sid }).then(res => {
          dataInfo.info = JSON.parse(JSON.stringify(res.data));
          dataInfo.info.forEach(item => {
              item.base64Image = false;  
              querySaveImage({sid: item.sid}).then(res => {
                  item.base64Image = AES.decrypt( res.data.base64Image, secretStr )
              })
          })
      })
    }
    getDetailInfo();
  <!DOCTYPE html>
  <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title></title>
      <style>
        * {
          margin: 0;
          padding: 0;
        }
        img {
          border: none;
        }
        div {
          margin: 0 auto;
          width: 800px;
          height: 400px;
          background: url("http://img.pconline.com.cn/images/upload/upc/tx/photoblog/1509/06/c5/12227263_12227263_1441518060703_mthumb.jpg") no-repeat center #e1e1e1;
        }
        div img {
          width: 100%;
          height: 100%;
        }
        p {
          width: 800px;
          height: 600px;
          line-height: 60px;
          background: lightgrey;
          font-size: 30px;
          margin: 0 auto;
          text-indent: 13px;
        }
      </style>
    </head>
    <body>
      <p>
        <span>多图片延迟加载:</span><br/>
        <span>(1)多图片延迟加载;</span><br/>
        <span>(2)多图片延迟加载;</span><br/>
        <span>(3)多图片延迟加载。</span>
      </p>
    </body>
    <script>
      var data = [
        {
          src:
            "http://img.pconline.com.cn/images/upload/upc/tx/photoblog/1509/06/c5/12227263_12227263_1441518066203_mthumb.jpg",
        },
        {
          src:
            "http://img.pconline.com.cn/images/upload/upc/tx/photoblog/1509/06/c5/12227263_12227263_1441518066203_mthumb.jpg",
        },
        {
          src:
            "http://img.pconline.com.cn/images/upload/upc/tx/photoblog/1509/06/c5/12227263_12227263_1441518066203_mthumb.jpg",
        },
        {
          src:
            "http://img.pconline.com.cn/images/upload/upc/tx/photoblog/1509/06/c5/12227263_12227263_1441518066203_mthumb.jpg",
        },
        {
          src:
            "http://img.pconline.com.cn/images/upload/upc/tx/photoblog/1509/06/c5/12227263_12227263_1441518066203_mthumb.jpg",
        },
        {
          src:
            "http://img.pconline.com.cn/images/upload/upc/tx/photoblog/1509/06/c5/12227263_12227263_1441518066203_mthumb.jpg",
        },
        {
          src:
            "http://img.pconline.com.cn/images/upload/upc/tx/photoblog/1509/06/c5/12227263_12227263_1441518066203_mthumb.jpg",
        },
        {
          src:
            "http://img.pconline.com.cn/images/upload/upc/tx/photoblog/1509/06/c5/12227263_12227263_1441518066203_mthumb.jpg",
        },
        {
          src:
            "http://img.pconline.com.cn/images/upload/upc/tx/photoblog/1509/06/c5/12227263_12227263_1441518066203_mthumb.jpg",
        },
      ];
      var aDiv = document.getElementsByTagName("div");
      var aImg = document.getElementsByTagName("img");
      function bind() {
        var frg = document.createDocumentFragment();
        for (var i = 0; i < data.length; i++) {
          var div = document.createElement("div");
          div.innerhtml = "<img realImg='" + data[i].src + "' alt=''/>";
          frg.appendChild(div);
        }
        document.body.appendChild(frg);
        frg = null;
      }
      bind();
      window.onscroll = function () {
        var scrollBottom =
          document.documentElement.scrollTop + document.documentElement.clientHeight;
        for (var i = 0; i < aDiv.length; i++) {
          var thisImg = aImg[i];
          var thisDiv = aDiv[i];
          var imgPosition = thisDiv.offsetTop + thisDiv.offsetHeight;
          if (imgPosition - 200 < scrollBottom) {
            if (thisImg.isLoad) continue;
            thisImg.src = thisImg.getAttribute("realImg");
            thisImg.isLoad = true;
          }
        }
      };
      /* 
        当相关服务关闭时,登录页仍然可见,相关图片地址的配置如下
        $timeout(function () {
          var thisImg = new Image();
          thisImg.src = $scope.r_g_company.logoPage.logo.src; 
          thisImg.onerror= function() {
            $scope.r_g_company.logoPage.logo.src = $scope.r_g_company.logoPage.logo.srcCY
          }
        },1000);
        $.get('/app_v3/oem/info?' +new Date().getTime()).then(function (res) {
          $scope.r_g_company.logoPage.logo.src = '/audit-html/static/img/cy_oem/logo.png';
        }).catch(function(){
          $scope.r_g_company.logoPage.logo.src = '/audit-html/static/img/cy_oem/logo.png';
          $scope.r_g_company.logoPage.logo.srcCY = '/audit-html/static/img/cy_tech/logo.png';
        });
      */
    </script>
  </html>
 
十三、canvas绘图
1、五角星和国旗
  <!doctype html>
  <html>
    <head>
      <meta charset="UTF-8">
      <title>五角星与国旗</title>
      <style>
        .container {
          display: flex;
          justify-content: space-around;
          align-items: flex-start;
          margin: 20px;
        }
        .canvas-wrapper {
          text-align: center;
          margin: 0 20px;
        }
        h1 {
          text-align: center;
          margin-bottom: 10px;
        }
      </style>
    </head>
    <body>
      <div class="container">
        <div class="canvas-wrapper">
          <h1>五角星</h1>
          <canvas id="canvas" width="450" height="450"></canvas>
        </div>
        <!-- canvas标签,默认宽300px; 高150px; 非常重要!!! -->
        <div class="canvas-wrapper">
          <h1>国旗(半成品)</h1>
          <canvas id="flag" width="600" height="400"></canvas>
        </div>
      </div>
    </body>
  </html>
  <script>
    // 绘制单独的五角星
    var canvas = document.getElementById("canvas");
    var context1 = canvas.getContext("2d");
    context1.beginPath();
    context1.rotate(18*Math.PI/180);
    for (i = 0; i < 5; i++) {
      context1.lineTo(
        Math.cos((i * 72+36) / 180 * Math.PI) * 200 + 250,
        Math.sin((i * 72+36) / 180 * Math.PI) * 200 + 150);
      context1.lineTo(
        Math.cos((i * 72+72) / 180 * Math.PI) * 75 + 250,
        Math.sin((i * 72+72) / 180 * Math.PI) * 75 + 150);
    }
    context1.fillStyle = "#ff0000";
    context1.fill();
    context1.closePath();
    // 绘制国旗
    const flagCanvas = document.getElementById('flag');
    const ctx = flagCanvas.getContext('2d');
    // 绘制红色背景
    ctx.fillStyle = '#de2910';
    ctx.fillRect(0, 0, 600, 400);
    // 大五角星中心坐标
    const bigStarX = 120;
    const bigStarY = 120;
    // 绘制大五角星
    drawStar(ctx, bigStarX, bigStarY, 60, '#ffde00', 0);
    // 绘制四个小五角星(修正后的位置和角度)
    const smallStars = [
      { x: 180, y: 60, angle: Math.atan2(bigStarY - 60, bigStarX - 180) },
      { x: 210, y: 90, angle: Math.atan2(bigStarY - 90, bigStarX - 210) },
      { x: 210, y: 150, angle: Math.atan2(bigStarY - 150, bigStarX - 210) },
      { x: 180, y: 180, angle: Math.atan2(bigStarY - 180, bigStarX - 180) }
    ];
    smallStars.forEach(star => {
      drawStar(ctx, star.x, star.y, 20, '#ffde00', star.angle);
    });
    // 绘制五角星函数
    function drawStar(ctx, cx, cy, radius, color, angle) {
      ctx.save();
      ctx.fillStyle = color;
      ctx.translate(cx, cy);
      ctx.rotate(angle);
      ctx.beginPath();
      for (let i = 0; i < 5; i++) {
        const outerAngle = (i * 2 * Math.PI / 5) - Math.PI / 2;
        const innerAngle = ((i + 0.5) * 2 * Math.PI / 5) - Math.PI / 2;
        ctx.lineTo(
          Math.cos(outerAngle) * radius,
          Math.sin(outerAngle) * radius
        );
        ctx.lineTo(
          Math.cos(innerAngle) * radius * 0.382,
          Math.sin(innerAngle) * radius * 0.382
        );
      }
      ctx.closePath();
      ctx.fill();
      ctx.restore();
    }
  </script>
2、党徽与国旗
  <!doctype html>
  <html>
    <head>
      <meta charset="UTF-8">
      <title>党徽与抗战时期的国旗</title>
      <style>
        .container {
          display: flex;
          justify-content: center;
          align-items: center;
          margin: 20px;
        }
        .canvas-container {
          margin: 10px;
        }
      </style>
    </head>
    <body>
      <div class="container">
        <div class="canvas-container">
          <canvas id="canvas" width="450" height="450"></canvas>
        </div>
        <!-- canvas标签,默认宽300px; 高150px; 非常重要!!! -->
        <div class="canvas-container">
          <canvas id="mainCanvas" width="900" height="600"></canvas>
        </div>
      </div>
    </body>
  </html>
  <script>
    // 以下,党徽绘制代码
    var canvas = document.getElementById("canvas");
    var context = canvas.getContext("2d");
    // 1、绘制深蓝色圆
    context.beginPath();
    context.arc(215, 215, 200, 0, 2 * Math.PI);
    context.fillStyle = "#030d48";
    context.strokeStyle = "grey";
    context.lineWidth = "10";
    context.stroke();
    context.fill();
    context.closePath();
    // 2、绘制12道光芒
    context.beginPath();
    for (var i = 0; i < 12; i++) {
      context.lineTo(
        Math.cos((i * 30) / 180 * Math.PI) * 200 + 215,
        Math.sin((i * 30) / 180 * Math.PI) * 200 + 215
      );
      context.lineTo(
        Math.cos((i * 30 + 15) / 180 * Math.PI) * 105 + 215,
        Math.sin((i * 30 + 15) / 180 * Math.PI) * 105 + 215
      );
    }
    context.fillStyle = "#ffffff";
    context.fill();
    context.closePath();
    // 3、绘制中心圆
    context.beginPath();
    context.arc(215, 215, 105, 0, 2 * Math.PI);
    context.strokeStyle = "#030d48";
    context.lineWidth = "10";
    context.stroke();
    context.closePath();
    // 以下,抗战时期国旗绘制代码
    const canvas2 = document.getElementById("mainCanvas");
    const ctx = canvas2.getContext("2d");
    // 1、绘制红色背景
    ctx.fillStyle = '#ff0000';
    ctx.fillRect(0, 0, canvas2.width, canvas2.height);
    // 2、绘制深蓝色背景
    ctx.beginPath();
    ctx.rect(0, 0, 450, 300);
    ctx.fillStyle = "#030d48";
    ctx.lineWidth = 5;
    ctx.fill();
    // 3、绘制12道光芒
    ctx.beginPath();
    for (let i = 0; i < 12; i++) {
      ctx.lineTo(
        Math.cos((i * 30) * Math.PI / 180) * 100 + 225, 
        Math.sin((i * 30) * Math.PI / 180) * 100 + 150
      );
      ctx.lineTo(
        Math.cos((i * 30 + 15) * Math.PI / 180) * 52.5 + 225, 
        Math.sin((i * 30 + 15) * Math.PI / 180) * 52.5 + 150
      );
    }
    ctx.fillStyle = "#ffffff";
    ctx.fill();
    // 4、绘制中心圆
    ctx.beginPath();
    ctx.arc(225, 150, 52.5, 0, Math.PI * 2);
    ctx.strokeStyle = "#030d48";
    ctx.lineWidth = 5;
    ctx.stroke();
  </script>
3、时钟
  这是用HTML5的canvas制作的一个钟表,包含表盘、时针、分针、秒针及它们的运动;另外还添加了自动读时间的功能
  <!doctype html>
  <html>
    <head>
      <style>
        body {
          background-color: #f0f8ff;
          display: flex;
          justify-content: center;
          height: 100vh;
          margin: 0;
          font-family: Arial, sans-serif;
          padding-top: 50px; /* 下移50像素 */
        }
        #clock-container {
          text-align: center;
        }
        #clock {
          display: block;
          margin: 0 auto;
        }
        .time-display {
          color: #00796b;
          font-size: 20px;
          margin-bottom: 15px;
          font-family: 'Microsoft YaHei', sans-serif;
        }
      </style>
    </head>
    <body>
      <div id="clock-container">
        <div class="time-display" id="time-display">北京时间: 加载中...</div>
        <!-- canvas标签,默认宽300px; 高150px; 非常重要!!! -->
        <canvas id="clock" width="500" height="500">
          您的浏览器不支持canvas标签,无法看到时钟
        </canvas>
      </div>
    </body>
  </html>
  <script>
    var clock = document.getElementById('clock'); //获取canvas元素
    var context = clock.getContext('2d'); //获取2D绘图上下文
    var timeDisplay = document.getElementById('time-display'); //获取时间显示元素
    function drawClock() { //定义绘制时钟的函数
      //以下,更新时钟上面的时间显示
      function tow(n) {//定义数字补零函数
        return n >= 0 && n < 10 ? '0' + n : '' + n; //小于10的数字补前导零
      }
      var now = new Date(); //获取当前时间对象
      var second = now.getSeconds(); //获取秒数
      var minute = now.getMinutes(); //获取分钟数
      var hour = now.getHours(); //获取小时数
      var date = now.getDate(); //获取日期
      var month = now.getMonth() + 1; //获取月份(需+1)
      var year = now.getFullYear(); //获取年份
      var text = ''; //初始化时间文本
      text += "北京时间: " + tow(year) + "年" + tow(month) + "月" + tow(date) + "日 "; //拼接日期
      text += tow(now.getHours()) + ":" + tow(minute) + ":" + tow(second); //拼接时分秒
      timeDisplay.textContent = text; //更新时间显示内容
      //以上,更新时钟上面的时间显示
      //以下,清空画布
      context.clearRect(0, 0, 500, 500); //清空画布
      //以下,绘制时钟背景渐变
      var gradient = context.createRadialGradient(250, 270, 0, 250, 270, 220); //创建径向渐变
      gradient.addColorStop(0, '#e0f7fa'); //添加渐变起始颜色
      gradient.addColorStop(1, '#80deea'); //添加渐变结束颜色
      context.fillStyle = gradient; //设置填充样式为渐变
      //以下,制作时钟外圈
      context.beginPath(); //开始路径绘制
      context.arc(250, 270, 220, 0, Math.PI * 2); //绘制背景圆形
      context.lineWidth = 15; //设置线条宽度
      context.strokeStyle = '#888888'; //设置线条颜色(浅灰)
      context.fill(); //填充背景
      context.beginPath(); //开始路径绘制
      context.arc(250, 270, 200, 0, Math.PI * 2); //绘制外圆
      context.stroke(); //绘制外圆边框
      context.closePath(); //结束路径
      //以下,制作时钟内圈
      context.beginPath(); //开始路径绘制
      context.lineWidth = 5; //设置线条宽度
      context.strokeStyle = '#333333'; //设置线条颜色(深灰)
      context.arc(250, 270, 190, 0, Math.PI * 2); //绘制内圆
      context.stroke(); //绘制内圆边框
      context.closePath(); //结束路径
      //以下,小时刻度制作
      for (i = 0; i < 12; i++) { //循环12次绘制小时刻度
        context.save(); //保存当前绘图状态
        context.lineWidth = 8; //设置线条宽度
        context.strokeStyle = "#00796b"; //设置线条颜色
        context.translate(250, 270); //平移坐标系到圆心
        context.rotate(i * 30 * Math.PI / 180); //旋转到对应小时角度
        context.beginPath(); //开始路径绘制
        context.moveTo(0, -170); //移动到刻度起点
        context.lineTo(0, -190); //绘制刻度线
        context.stroke(); //绘制刻度
        context.closePath(); //结束路径
        context.restore(); //恢复绘图状态
      };
      //以下,分钟刻度制作
      for (i = 0; i < 60; i++) { //循环60次绘制分钟刻度
        context.save(); //保存当前绘图状态
        context.lineWidth = 3; //设置线条宽度
        context.strokeStyle = "#00897b"; //设置线条颜色
        context.translate(250, 270); //平移坐标系到圆心
        context.rotate(i * 6 * Math.PI / 180); //旋转到对应分钟角度
        context.beginPath(); //开始路径绘制
        context.moveTo(0, -180); //移动到刻度起点
        context.lineTo(0, -190); //绘制刻度线
        context.stroke(); //绘制刻度
        context.closePath(); //结束路径
        context.restore(); //恢复绘图状态
      };
      //以下,钟面上表示小时的数字
      for (var i = 1; i < 13; i++) {//循环13次绘制小时数字
        context.save(); //保存当前绘图状态
        //以下,计算小时数字的位置
        var deg = i * Math.PI / 6; //计算对应角度
        context.translate(250, 270); //平移坐标系到圆心
        var x = Math.sin(deg); //计算x坐标
        var y = -Math.cos(deg); //计算y坐标
        //以下,绘制小时数字的圆形背景
        context.beginPath(); //开始路径绘制
        context.arc(x * 155, y * 155, 20, 0, Math.PI * 2); //绘制数字背景圆
        context.fillStyle = '#00796b'; //设置填充颜色
        context.fill(); //填充背景圆
        context.strokeStyle = '#004d40'; //设置边框颜色
        context.lineWidth = 2; //设置边框宽度
        context.stroke(); //绘制边框
        //以下,绘制小时数字
        context.fillStyle = "white"; //设置文本颜色
        context.font = "bold 20px Arial"; //设置字体样式
        context.textAlign = "center"; //设置文本居中
        context.textBaseline = "middle"; //设置文本垂直居中
        context.fillText(i, x * 155, y * 155); //绘制数字文本
        context.restore(); //恢复绘图状态
      };
      //以下,中心装饰点
      context.beginPath(); //开始路径绘制
      context.arc(250, 270, 10, 0, Math.PI * 2); //绘制中心圆
      context.fillStyle = 'green'; //设置填充颜色
      context.fill(); //填充中心圆
      context.strokeStyle = 'yellow'; //设置边框颜色
      context.lineWidth = 3; //设置边框宽度
      context.stroke(); //绘制边框
      //以下,时针制作
      context.save(); //保存当前绘图状态
      context.lineWidth = 8; //设置线条宽度
      context.strokeStyle = "#333"; //设置线条颜色
      context.lineCap = "round"; //设置线条端点样式
      context.translate(250, 270); //平移坐标系到圆心
      context.rotate(hour * 30 * Math.PI / 180); //旋转到对应小时角度
      context.beginPath(); //开始路径绘制
      context.moveTo(0, 15); //移动到时针起点
      context.lineTo(0, -90); //绘制时针
      context.stroke(); //绘制时针
      context.closePath(); //结束路径
      context.restore(); //恢复绘图状态
      //以下,分针制作
      context.save(); //保存当前绘图状态
      context.lineWidth = 5; //设置线条宽度
      context.strokeStyle = "#555"; //设置线条颜色
      context.lineCap = "round"; //设置线条端点样式
      context.translate(250, 270); //平移坐标系到圆心
      context.rotate(minute * 6 * Math.PI / 180); //旋转到对应分钟角度
      context.beginPath(); //开始路径绘制
      context.moveTo(0, 15); //移动到分针起点
      context.lineTo(0, -130); //绘制分针
      context.stroke(); //绘制分针
      context.closePath(); //结束路径
      context.restore(); //恢复绘图状态
      //以下,秒针制作
      context.save(); //保存当前绘图状态
      context.strokeStyle = "#e53935"; //设置线条颜色
      context.fillStyle = "#e53935"; //设置填充颜色
      context.lineWidth = 2; //设置线条宽度
      context.lineCap = "round"; //设置线条端点样式
      context.translate(250, 270); //平移坐标系到圆心
      context.rotate(second * 6 * Math.PI / 180); //旋转到对应秒数角度
      context.beginPath(); //开始路径绘制
      context.moveTo(0, 20); //移动到秒针起点
      context.lineTo(0, -160); //绘制秒针
      context.stroke(); //绘制秒针
      context.closePath(); //结束路径
      //以下,秒针尾部装饰
      context.beginPath(); //开始路径绘制
      context.arc(0, 0, 5, 0, Math.PI * 2); //绘制尾部装饰圆
      context.fill(); //填充装饰圆
      context.stroke(); //绘制装饰圆边框
      //以下,秒针头部装饰
      context.beginPath(); //开始路径绘制
      context.arc(0, -150, 8, 0, Math.PI * 2); //绘制头部装饰圆
      context.fill(); //填充装饰圆
      context.stroke(); //绘制装饰圆边框
      context.restore(); //恢复绘图状态
    }
    drawClock(); //首次绘制时钟
    setInterval(drawClock, 1000); //每秒更新一次时钟
  </script>
 
十四、弹窗
1、弹窗-系统内置
  <!DOCTYPE html>
  <html>
    <head>
      <meta charset="utf-8">
    </head>
    <body style="font-size: 30px;"> 
      <pre>     
        alert:
          1、弹-窗有确定按钮,阻塞线程,
          2、alert后面的代码,点击确认后才执行
        confirm:
          1、弹-窗有两个按钮(确定和取消),阻塞线程,
          2、confirm后面的代码,点击按钮后才执行,
          3、点击确定返回true,点击取消返回false
        prompt:
          1、弹-窗有一个输入框、两个按钮(确定和取消),阻塞线程
          2、prompt后面的代码,点击按钮后才执行,
          3、点击确定返回输入内容,为空返回空字符串,点击取消返回null
      </pre>
      <input type="button" onclick="fnAlert()" value="显示alert框" />
      <input type="button" onclick="fnConfirm()" value="显示confirm框" />
      <input type="button" onclick="fnPrompt()" value="显示prompt框" />
    </body>
  </html>
  <script>
    function fnAlert(){
      var text = alert("你好,我是一个alert框!");
      console.log( text );
    }
    function fnConfirm(){
      var text = confirm("你好,我是一个confirm框!");
      console.log( text );
    }
    function fnPrompt(){
      var text = prompt("你好,我是一个prompt框!","这是默认输入!");
      console.log( text );
    }
  </script>
2、弹窗-手写(可拖拽和缩放)
  <!DOCTYPE html>
  <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>弹窗-可拖拽和缩放</title>
      <style>
        * {
          padding: 0;
          margin: 0;
        }
        html,body {
          height: 100%;
        }
        .mask {
          position: fixed;
          width: 100%;
          height: 100%;
          background: #bbb;
        }
        #alert {
          position: absolute;
          background: #ddd;
          margin: auto;
          width: 600px;
          height: 800px;
          left: 50%;
          top: 50%;
          transform: translate(-50%, -50%);
          user-select: none;
          resize: both;
          overflow: auto;
        }
        .innerBox {
          padding: 0 30px;
        }
        p {
          line-height: 40px;
        }
        .title {
          height: 50px;
          line-height: 50px;
          background: gray;
          padding-left: 30px;
          cursor: pointer;
        }
        .submit{
          text-align: center;
          background: gray;
          display: block;
          margin: 0 auto;
          width:100px;
          height:30px;
          line-height: 30px;
        }
      </style>
    </head>
    <body>
      <div class="mask">
        <div id="alert">
          <p id="alert-title-id" class="title"> 本弹窗特征(鼠标置于此处,方可拖动): </p>
          <div class="innerBox">
            <p> 1、标题区可拖拽 </p>
            <p> 2、内容区可以是任意高度、宽度 </p>
            <p> 3、初始位置居中,由下面css决定 </p>
            <div style="padding-left:30px;">
              <p> left: 50%; </p>
              <p> top: 50%;</p>
              <p> transform: translate(-50%, -50%);</p>
            </div>
            <p> 4、弹-窗可缩放,由下面css决定 </p>
            <div style="padding-left:30px;">
              <p> resize: both; </p>
              <p> overflow: auto;</p>
            </div>
            <p> 5、提交按钮和文字居中,由下面css决定 </p>
            <div style="padding-left:30px;">
              <p> text-align:center; width:100px; background:gray;</p>
              <p> display:block; height:30px; line-height:30px; margin:0 auto;</p>
            </div>
            <p> 6、你使用时,在关闭弹-窗之前,用上面3处css代码重置弹-窗的位置,否则,下次使用弹-窗时,弹-窗将出现在上次关闭时的地方。 </p>
            <p> 7、弹-窗向任何方向(上下左右)拖拽,当消失3/4时,停止移动。 </p>
            <p> 8、拖拽弹-窗的右下方,可以实现缩放。 </p>
          </div>
          <div style="padding-top:30px;">
            <span class="submit">提交</span>
          </div>
        </div>
      </div>
    </body>
  </html>
  <script>
    var oDiv = document.getElementById("alert");
    oDiv.onmousedown = down;
    function processThis(fn, currentThis) {
      return function (event) {
        fn.call(currentThis, event); //”先触发,后执行“与”先执行,后触发“
      };
    }
    function down(event) {
      event = event || window.event;
      if (event.target.id != "alert-title-id") return;
      this.initOffsetLeft = this.offsetLeft;
      this.initOffsetTop = this.offsetTop;
      this.initClientX = event.clientX;
      this.initClientY = event.clientY;
      this.maxOffsetWidth =
        (document.documentElement.clientWidth || document.body.clientWidth) -
        this.offsetWidth;
      this.maxOffsetHeight =
        (document.documentElement.clientHeight || document.body.clientHeight) -
        this.offsetHeight;
      if (this.setCapture) {
        this.setCapture();
        this.onmousemove = processThis(move, this);
        this.onmouseup = processThis(up, this);
      } else {
        document.onmousemove = processThis(move, this);
        document.onmouseup = processThis(up, this);
      }
    }
    function move(event) {
      var currentLeft = this.initOffsetLeft + (event.clientX - this.initClientX);
      var currentTop = this.initOffsetTop + (event.clientY - this.initClientY);
      //以下都是边界值的判断;弹窗向任何方向(上下左右)拖拽,当消失3/4时,停止移动。
      if (currentLeft > this.maxOffsetWidth + this.clientWidth / 0.8) {
        currentLeft = this.maxOffsetWidth + this.clientWidth / 0.8;
      } else if (currentLeft < -this.clientWidth / 4) {
        currentLeft = -this.clientWidth / 4;
      }
      if (currentTop > this.maxOffsetHeight + this.clientHeight / 0.8) {
        currentTop = this.maxOffsetHeight + this.clientHeight / 0.8;
      } else if (currentTop < 300) {
        //-this.clientHeight / 4
        currentTop = 300; //-this.clientHeight / 4
      }
      //以上都是边界值的判断;弹-窗向任何方向(上下左右)拖-拽,当消失3/4时,停止移动。
      //以下都是边界值的判断;弹-窗向任何方向(上下左右)拖-拽,当消失1/2时,停止移动。
      /* if (currentLeft > this.maxOffsetWidth + this.clientWidth) {
          currentLeft = this.maxOffsetWidth + this.clientWidth;
        } else if (currentLeft < -this.clientWidth / 64) {
          currentLeft = -this.clientWidth / 64;
        }
        if (currentTop > this.maxOffsetHeight + this.clientHeight) {
          currentTop = this.maxOffsetHeight + this.clientHeight;
        } else if (currentTop < -this.clientHeight / 64) {
          currentTop = -this.clientHeight / 64;
        } */
      //以上都是边界值的判断;弹-窗向任何方向(上下左右)拖-拽,当消失1/2时,停止移动。
      //以下都是边界值的判断;弹-窗向任何方向(上下左右)拖-拽,触边时,停止移动。
      /* if (currentLeft > this.maxOffsetWidth + this.clientWidth / 2) {
          currentLeft = this.maxOffsetWidth + this.clientWidth / 2;
        } else if (currentLeft < this.clientWidth / 2) {
          currentLeft = this.clientWidth / 2;
        }
        if (currentTop > this.maxOffsetHeight + this.clientHeight / 2) {
          currentTop = this.maxOffsetHeight + this.clientHeight / 2;
        } else if (currentTop < this.clientHeight / 2) {
          currentTop = this.clientHeight / 2;
        } */
      //以上都是边界值的判断;弹-窗向任何方向(上下左右)拖-拽,触边时,停止移动。
      this.style.left = currentLeft + "px";
      this.style.top = currentTop + "px";
      console.log(this.style.left);
      console.log(this.style.top);
    }
    function up() {
      if (this.releaseCapture) {
        this.releaseCapture();
        this.onmousemove = null;
        this.onmouseup = null;
      } else {
        document.onmousemove = null;
        document.onmouseup = null;
      }
    }
  </script>
  
十五、表格排序 
  说明,这里的表格排序,包含按姓名、年龄、分数、性别等汉字和数字的排序。用纯原生JavaScript代码实现,同时还实现了隔行变色。
  <!DOCTYPE html>
  <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title></title>
      <style>
        #table {
          width: 600px;
          border: 3px solid darkgreen;
          margin: 20px auto;
          text-align: center;
        }
        #table tr {
          height: 40px;
          line-height: 40px;
        }
        .bg0 {
          background: mediumvioletred;
        }
        .bg1 {
          background: greenyellow;
        }
        .bg2 {
          background: yellow;
        }
        .cursor {
          cursor: pointer
        }
      </style>
    </head>
    <body>
      <table id="table" class="table">
        <thead>
          <tr class="bg2">
            <th class="cursor">姓名</th>
            <th class="cursor">年龄</th>
            <th class="cursor">分数</th>
            <th class="cursor">性别</th>
          </tr>
        </thead>
        <tbody>
        </tbody>
      </table>
    </body>
  </html>
  <script>
    var table = document.getElementById("table");
    var th = table.tHead.rows[0].cells;
    var body = table.tBodies[0];
    var row = body.rows;
    console.log(row); 
    console.log('上面,展开前是空数组,这是代码执行到此的结果'); 
    console.log('上面,展开后有6项数据,这是页面渲染完毕后的结果'); 
    var data = [
      { name: "赵老大", age: 45, score: 60, sex: 0 },
      { name: "钱老二", age: 24, score: 67, sex: 1 },
      { name: "孙老三", age: 38, score: 79, sex: 1 },
      { name: "李老四", age: 30, score: 80, sex: 0 },
      { name: "周老五", age: 65, score: 56, sex: 1 },
      { name: "吴老六", age: 26, score: 26, sex: 0 },
    ];
    //绑定原始数据
    bind();
    function bind() {
      var frg = document.createDocumentFragment();
      for (var i = 0; i < data.length; i++) {
        var cur = data[i];
        var tr = document.createElement("tr");
        for (var attr in cur) {
          if (attr === "sex") {
            cur[attr] = cur[attr] === 0 ? "男" : "女";
          }
          var td = document.createElement("td");
          td.innerHTML = cur[attr];
          tr.appendChild(td);
        }
        frg.appendChild(tr);
      }
      body.appendChild(frg); //2、
      frg = null;
    }
    //实现隔行变色
    changeColor();
    function changeColor() {
      for (var i = 0; i < row.length; i++) {
        row[i].className = "bg" + (i % 2);
      }
    }
    //绑定点击事件
    for (var i = 0; i < th.length; i++) {
      if (th[i].className === "cursor") {
        th[i].flag = -1;
        th[i].index = i;
        th[i].onclick = function () {
          sortArray.call(this, this.index);
        };
      }
    }
    //类数组转化为数组
    function makeArray(arg) {
      var ary = [];
      try {
        ary = Array.prototype.slice.call(arg);
      } catch (e) {
        for (var i = 0; i < arg.length; i++) {
          ary.push(arg[i]);
        }
      }
      return ary;
    }
    //点击事件中的排序
    function sortArray(n) {
      var that = this;
      for (var i = 0; i < th.length; i++) {
        th[i].flag = i === n ? (that.flag *= -1) : -1;
      }
      var ary = makeArray(row);
      ary.sort(function (rowBefore, rowBehind) {
        var rowInnerBefore = rowBefore.cells[n].innerHTML;
        var rowInnerBehind = rowBehind.cells[n].innerHTML;
        return rowInnerBefore.localeCompare(rowInnerBehind);
      });
      var frg = null;
      for (i = 0; i < ary.length; i++) {
        frg = ary[i];
        body.appendChild(frg);
      }
      changeColor();
    }
  </script>

十六、拖拽
1、拖拽-普通
  <!DOCTYPE html>
  <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>拖拽-普通</title>
      <style>
        *{
          margin:0;
          padding:0;
        }
        div{
          position: absolute;
          left:0;
          top:0;
          width: 100px;
          height: 100px;
          background: red;
        }
      </style>
    </head>
    <body>
      <div id="div"></div>
    </body>
  </html>
  <script>
    var oDiv = document.getElementById("div");
    oDiv.onmousedown = down;
    function processThis(fn, obj) {
      return function (e) {
        fn.call(obj, e);
      };
    }
    function down(event) {
      event = event || window.event;
      this.offsetLeftPass = this.offsetLeft;
      this.offsetTopPass = this.offsetTop;
      this.eventClientX = event.clientX;
      this.eventClientY = event.clientY;
      if (this.setCapture) {
        this.setCapture();
        this.onmousemove = processThis(move, this);
        this.onmouseup = processThis(up, this);
      } else {
        document.onmousemove = processThis(move, this);
        document.onmouseup = processThis(up, this);
      }
    }
    function move(event) {
      event = event || window.event;
      this.style.left =
        this.offsetLeftPass + (event.clientX - this.eventClientX) + "px";
      //this.offsetLeftPass:移动前offsetLeft值;(event.clientX-this.eventClientX):鼠标横向移动的距离,即盒子横向移动的距离
      this.style.top =
        this.offsetTopPass + (event.clientY - this.eventClientY) + "px";
      //this.offsetTopPass:移动前offsetTop值;(event.clientX-this.eventClientX):鼠标纵向移动的距离,即盒子纵向移动的距离
    }
    function up() {
      if (this.releaseCapture) {
        this.releaseCapture();
        this.onmousemove = null;
        this.onmouseup = null;
      } else {
        document.onmousemove = null;
        document.onmouseup = null;
      }
    }
  </script>
2、拖拽-zTree
  <!DOCTYPE html>
  <html>
    <head>
      <title>拖拽-zTree</title>
      <meta http-equiv="content-type" content="text/html; charset=UTF-8">
      <link rel="stylesheet" href="https://cdn.bootcss.com/zTree.v3/3.5.33/css/zTreeStyle/zTreeStyle.min.css">
      <script type="text/javascript" src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
      <script type="text/javascript" src="https://cdn.bootcss.com/zTree.v3/3.5.33/js/jquery.ztree.core.min.js"></script>
      <script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/zTree.v3/3.5.29/js/jquery.ztree.excheck.js"></script>
      <script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/zTree.v3/3.5.29/js/jquery.ztree.exedit.js"></script>
      <script type="text/javascript">
        var setting = {
          edit: {
            enable: true,
            showRemoveBtn: false,
            showRenameBtn: false
          },
          data: {
            simpleData: {
              enable: true
            }
          },
          callback: {
            beforeDrag: beforeDrag,
            beforeDrop: beforeDrop
          }
        };
        var zNodes =[
          { id:1, pId:0, name:"随意拖-拽 1", open:true},
          { id:11, pId:1, name:"随意拖-拽 1-1"},
          { id:12, pId:1, name:"随意拖-拽 1-2", open:true},
          { id:121, pId:12, name:"随意拖-拽 1-2-1"},
          { id:122, pId:12, name:"随意拖-拽 1-2-2"},
          { id:123, pId:12, name:"随意拖-拽 1-2-3"},
          { id:13, pId:1, name:"禁止拖-拽 1-3", open:true, drag:false},
          { id:131, pId:13, name:"禁止拖-拽 1-3-1", drag:false},
          { id:132, pId:13, name:"禁止拖-拽 1-3-2", drag:false},
          { id:133, pId:13, name:"随意拖-拽 1-3-3"},
          { id:2, pId:0, name:"随意拖-拽 2", open:true},
          { id:21, pId:2, name:"随意拖-拽 2-1"},
          { id:22, pId:2, name:"禁止拖-拽到我身上 2-2", open:true, drop:false},
          { id:221, pId:22, name:"随意拖-拽 2-2-1"},
          { id:222, pId:22, name:"随意拖-拽 2-2-2"},
          { id:223, pId:22, name:"随意拖-拽 2-2-3"},
          { id:23, pId:2, name:"随意拖-拽 2-3"}
        ];
        function beforeDrag(treeId, treeNodes) {
          for (var i=0,l=treeNodes.length; i<l; i++) {
            if (treeNodes[i].drag === false) {
              return false;
            }
          }
          return true;
        }
        function beforeDrop(treeId, treeNodes, targetNode, moveType) {
          return targetNode ? targetNode.drop !== false : true;
        }
        function setCheck() {
          var zTree = $.fn.zTree.getZTreeObj("treeDemo"),
          isCopy = $("#copy").attr("checked"),
          isMove = $("#move").attr("checked"),
          prev = $("#prev").attr("checked"),
          inner = $("#inner").attr("checked"),
          next = $("#next").attr("checked");
          zTree.setting.edit.drag.isCopy = isCopy;
          zTree.setting.edit.drag.isMove = isMove;
          showCode(1, ['setting.edit.drag.isCopy = ' + isCopy, 'setting.edit.drag.isMove = ' + isMove]);
          zTree.setting.edit.drag.prev = prev;
          zTree.setting.edit.drag.inner = inner;
          zTree.setting.edit.drag.next = next;
          showCode(2, ['setting.edit.drag.prev = ' + prev, 'setting.edit.drag.inner = ' + inner, 'setting.edit.drag.next = ' + next]);
        }
        function showCode(id, str) {
          var code = $("#code" + id);
          code.empty();
          for (var i=0, l=str.length; i<l; i++) {
            code.append("<li>"+str[i]+"</li>");
          }
        }
        $(document).ready(function(){
          $.fn.zTree.init($("#treeDemo"), setting, zNodes);
          setCheck();
          $("#copy").bind("change", setCheck);
          $("#move").bind("change", setCheck);
          $("#prev").bind("change", setCheck);
          $("#inner").bind("change", setCheck);
          $("#next").bind("change", setCheck);
        });
      </script>
    </head>
    <body>
      <p>参考文档1,https://treejs.cn/v3/main.php#_zTreeInfo</p>
      <p>参考文档2,https://www.bootcdn.cn/zTree.v3/3.5.29</p>
      <div class="content_wrap">
        <div class="zTreeDemoBackground left">
          <ul id="treeDemo" class="ztree"></ul>
        </div>
        <div class="right">
          <ul class="info">
            <li class="title"><h2>1、setting 配置信息说明</h2>
              <ul class="list">
              <li>此 Demo 仅从功能上演示实现拖拽的基本方法和配置参数</li>
              <li class="highlight_red">1)、使用拖-拽功能,必须设置 setting.edit 中的各个属性,详细请参见 API 文档中的相关内容</li>
              <li class="highlight_red">2)、使用拖-拽功能的事件回调函数,必须设置 setting.callback.beforeDrag / onDrag / beforeDrop / onDrop 等属性,详细请参见 API 文档中的相关内容</li>
              <li><p>基本拖-拽设置:<br/>
                  <input type="checkbox" id="copy" class="checkbox first" checked /><span>允许复制</span>
                  <input type="checkbox" id="move" class="checkbox " checked /><span>允许移动</span><br/>
                  <ul id="code1" class="log" style="height:42px;"></ul></p>
              </li>
              <li><p>拖-拽相对位置设置:<br/>
                  <input type="checkbox" id="prev" class="checkbox first" checked /><span>prev</span>
                  <input type="checkbox" id="inner" class="checkbox " checked /><span>inner</span>
                  <input type="checkbox" id="next" class="checkbox " checked /><span>next</span><br/>
                  <ul id="code2" class="log" style="height:65px;"></ul></p>
              </li>
              </ul>
            </li>
            <li class="title"><h2>2、treeNode节点数据说明</h2>
              <ul class="list">
              <li>对节点数据没有特殊要求,用户可以根据自己的需求添加自定义属性</li>
              </ul>
            </li>
          </ul>
        </div>
      </div>
    </body>
  </html>

  

posted @ 2019-06-03 14:04  WEB前端工程师_钱成  阅读(4142)  评论(0)    收藏  举报