网站建站套餐,发布网站免费空间,企业网站建设的方法有哪些,平面设计和电商设计的区别想要获取笔记的可以点击下面链接获取 JavaScript超详细的学习笔记#xff0c;点击我获取
一#xff0c;JavaScript详细笔记
1#xff0c;基础知识
1-1 基础知识
// 1#xff0c;标识符命名规则#xff1a;第一个字母必须是字母#xff0c;下划线或一个美元符号。不能…想要获取笔记的可以点击下面链接获取 JavaScript超详细的学习笔记点击我获取
一JavaScript详细笔记
1基础知识
1-1 基础知识
// 1标识符命名规则第一个字母必须是字母下划线或一个美元符号。不能数字开头通常不用汉字
// 2空格忽略所以可以在代码中随意使用空格和换行
// 3注释 // 单行注释 /* 注释内容 */ 多行注释
// 4直接量literal 12 1.2 “hello wold” true 等等一般赋给变量或者用在表达式里面
// 5严格模式 顶部添加 “use strict”
// 6语句以一个分号结束如果省略分号则由解析器确定语句结尾1-2 变量
function show(){var msgaini;
}
show();
console.log(msg)// 函数内用 var 定义的变量是局部变量只能在函数内使用function show(){msgaini;
}
show();
console.log(msg)
// 不用 var而直接赋值就是全局变量可以在函数外边使用注虽然省略了 var 可以定义全局变量但不推荐使用这种做法因为正在局部作用域中的全局变量很难维护。
1-3 数据类型
// 1js 不区分浮点数和整数// 2除了十进制整数还可以用八进制和十六进制来表示16 进制前面加 0x;8 进制前面必须有 0或者 0o计算或输出的时候会换算成 10 进制输出严格模式下 8进制无效ES6 页不能使用的// 3极大数或极小数可以用科学计数法 e 如 3e103^10/3e-103^(-10) 加号可省略默认情况下会将那些小数点后面带有 6 个零以上的浮点数转换为以 e 表示的数值console.log(0.1 0.2); 不等于 0.3而是等于 0.30000000000000004 // 所以浮点数加减不准确4数据范围由于内存限制ESmascrip 并不能保存任意的数值其能够表示的最小值保存在 Number.MIN_Value 中在绝大多数浏览器中这个值是5e-324;表示最大值保存在 Number.MAX_Value 中值为1.7976931348623157e308
console.log(Number.MAX_VALUE); 1.7976931348623157e308
console.log(Number.MIN_VALUE); 5e-324
console.log(Number.MAX_SAFE_INTEGER); 9007199254740991 // 安全整数值
console.log(Number.MIN_SAFE_INTEGER); -90071992547409915想确定一个数是不是有穷的可以使用 isFinite()函数这个函数会判断参数是否位于最小值与最大值之间
var a10; // 返回为 TRUE
console.log(isFinite(a));6访问 Number.POSITIVE_INFINITY 和 Number_NEGATIVE_INFINITY 也可以得到正负infinity 值既这两个属性中分别保存着 infinity 和-infinity
console.log(Number.POSITIVE_INFINITY); infinity
console.log(Number.NEGATIVE_INFINITY); -infinity7,NaN 非数值not a number用于返回一个本来要返回数值却未返回数值的情况
注除以 0 的结果是 infinite不是 NaN NaN 与任何值不相等包括其本身任何涉及 NaN 的计算都会返回 NaN
console.log(3/a); NaN
console.log(NaNNaN); flase8isNaN()函数 接受一个参数判断这个参数可以是任何类型其判断改参数是否“不是数值”这个函数首先尝试把这个参数转换成数值某些不是数值的值会直接转换成数值比如字符串“10”或者 Boolean而任何不能被转换为数值的值都会导致这个函数返回 TRUE
console.log(isNaN(number)); // 是非数字 所以 TRUE
console.log(isNaN(10)); // 是数字 所以 false
console.log(isNaN(10)); // 能转换成数字 所以 false
console.log(isNaN(NaN)); // 不是数字 所以 true
console.log(isNaN(false));// 能转换成数字 所以 false9string 类型 一般使用单引号 如 userName’aini’; 空格也是字符串
10转义字符 \n 换行 \t 制表符 \b 退格 \r 回车 \f 进纸 \ 斜杠 \’ 单引号 \” 双引号 \nn 以 16 进制代码表示的一个字符 \unnnn 以 16 进制代码 nnnn 表示的一个 Unicode
var src zero\nnetwork // 输出到页面时不会换行因为 HTML 代码换行时页面中不
document.write(src);// 换行只是多了空格// 书写字符串直接量可以拆分多行每行必须加上反斜杠\结束11布尔类型
var a 10, b 15;
console.log(a b);
if(a b){console.log(相等);
}else{console.log(不相等);
}12Undefined 未定义的此类型只有一个值既特殊的 undefined在使用 var 声明变量但未对其初始化
13Null 此类型也只有一个值既特殊的 null 值空值从逻辑上看。Null 值表示一个空对象指针而这也正是使用 typeof 操作检测 null 时会返回 object 的原因
var car null;
console.log(car); // null
console.log(typeof car); // object// 注实际上undefined 值是派生自 null 值得因此 ECMA-262 规定对他们相等性测试要返回 TRUE如console.log(null undefined); // TRUE1-4 复杂数据类型
1也称为复杂的数据类型就是一组数据和功能的集合
var person {};
person.nameaini;
person.age18;
person.say function(){
console.log(my name is aini);console.log(person); // {name: aini, age: 18, say: ƒ}2常见的引用数据类型
var arr [1, 3, 4, 5];
console.log(arr); // (4) [1, 3, 4, 5]
function show(){};
console.log(show); // ƒ show(){}
var date new Date();// Mon Sep 12 2022 14:04:42 GMT0800 (中国标准时间)
console.log(date);3Object 对象
1Object 对象时多有对象的基础因此多有对象都具有这些基本的属性和方法
2toSrting(): 返回对象的字符串表示 valueOf():返回对象的字符串数值或布尔值表示通常与 toString()返回值相同
var o new Object();
var arr new Array(1,2,3,4);
var fun new Function(a,b);
console.log(o); // {}
console.log(arr); // (4) [1, 2, 3, 4]
console.log(fun); // ƒ anonymous(a) {b}
console.log(o.toString()); // [object Object]
console.log(arr.toString()); // 1,2,3,4console.log(fun.toString()); // function anonymous(a) {b}
console.log(o.valueOf()); // {}
console.log(arr.valueOf()); // (4) [1, 2, 3, 4]
console.log(fun.valueOf());// ƒ anonymous(a) {b}通常这两个方法是可以改写的如
var o new Object();
o.valueOf function(){return this is a object;
}
o.toString function(){return this is my name;
}
console.log(o.valueOf()); // this is a object
console.log(o.toString());// this is my name4,可变类型和不可变类型
1原始值是不可变类型引用数据复杂数据类型属于可变类型
var arr [1,2];
var arr1 arr;
arr1.push(3);
console.log(arr);// Arr 和 arr1 指向同一个堆所以改变一个的值另一个也随之改变var arr [1,2];
var arr1 arr;
arr1 [1,2,3] // (重新创建了 arr1并覆盖了之前的数据)
console.log(arr);// [1,2]1-5 数据类型转换
1因为数据之间是同类型的运算如果是不同类型的运算需要将其转换为同一类型后再进行运算
2类型分两类强制类型转换自动类型转换
3自动类型转换根据运算的语义环境会自动进行类型转换
var a 2,b 1;
console.log(a-b); // 1;
b true;
console.log(a-b); // 1;// 注最经典的用法在 if 语句条件中var car new Object();
if(car){ //加入为真存在执行花括号里的语句console.log(汽车); //汽车
}// 自动转换注意点1” “ 空字符串转成数字 02[] 空数组转换数字为 03Function() 能转换成字符串”function” 转换为数字为 NaN ;1-5-1 强制类型转换
// 1尽管 JavaScript 可以自动进行类型转换但有时仍需要显式转换或者有时为了使代码变得更加清晰易读使用 string(),number(),Boolean();// 2转换为数值Number(); parseInt();parseFloat(); number() 可以用于任何类型另外两个则专门用于把字符串转成数值这三个函数对于同样的输入有不同的返回结果// 3number()函数转换规则如下1Boolean 1,0;2数字 直接返回该数字3undefind NaN;4null 0
// 如果是字符串则遵循以下规则1”123” 123;2“01235” 1235; // (去掉最前面的 0)3“1.1” 1.1 //浮点型4“01.1” 1.1 // 浮点型5“0xff” 255; // number()能识别出 16 进制6“” 0;7“123abc” // NaN;var value {};
console.log(Number(value)); // NaNvar value {};
value {valueOf:function(){return 18}};
console.log(Number(value)); // 18var value {};
value {valueOf:function(){return true}};
console.log(Number(value));// 4有一些操作符-的操作与 Number()函数相同会将操作数转换为数字
var x w;
console.log(typeof x); // number (加了正负号以后把数据类型自动改了)var x w;
console.log(typeof x); //number (加了正负号以后把数据类型自动改了)
console.log(x); // w
console.log(x); // NaN1-5-2 parseInt() 函数
// 1number 函数在转换字符串时比较复杂而且不够合理并且只能基于十进制进行转换且不能出现非法的尾随字符因此在处理整数的时候通常用的是parseInt()和 parseFloat();// 2二者都是全局函数不属于任何对象的方法console.log(parseInt( 8a:)); // 8;
console.log(parseInt(22.5)); // 22;
console.log(parseInt(wang1)); // NaN;
console.log(parseInt()); //NaNvar value ;
console.log(Number(value)); //0
console.log(parseInt(value)); //NaN// 3parseInt() 能识别 16 进制识别不了字符串的 8 进制console.log(parseInt(0xff)); //255;
console.log(parseInt(0xafwangwei)); // 175(自动忽略除了 16 进制以外的部分)
console.log(parseInt(070)); //56;(不用字符串表示可以识别出 8 进制并转化)
console.log(parseInt(075)); //75 (字符串里的八进制无法识别会忽略 0 在转换为整数)// 4parseInt()识别八进制时第 3 版可以识别但第 5 版识别不了为解决这个问题可以为 parseInt()指定第二个参数转换基数console.log(parseInt(0xff,16)); // 255 把 16 进制转换为 10 进制
console.log(parseInt(077,8)); // 63 把 8 进制转换为 10 进制
console.log(parseInt(100101,2)); // 37 把 2 进制转换为 10 进制
console.log(parseInt(054,6)); // 34 把 6 进制转换为 10 进制// 在指定了进制以后可以把前面的 0X 或者 0 去掉了console.log(parseInt(ff,16));
console.log(parseInt(77,8));
console.log(parseInt(100101,2));
console.log(parseInt(54,6));1-5-3 parseFloat() 函数
// 1parseFloat() 只能识别十进制16 进制会始终转换成 0console.log(parseFloat(123abc)); // 123;
console.log(parseFloat(123.56abc)); // 123.56;
console.log(parseFloat(0xaa)); // 0;
console.log(parseFloat(22.5)); //22.5;
console.log(parseFloat(0909.5)); //909.5;
console.log(parseFloat(3.125e6)); //3125000;
console.log(parseFloat(0700)); //700;7转换为字符串
// 1要把值转换成字符串有两种方式第一种是使用几乎每个值都有的 toString()方法该方法会返回相应值得字符串var age 18;
var ageString age.toString();
var found true;
console.log(found); // ”True” 实际上调用了 found.toString()或 found.valueOf()// 注数值布尔值对象和字符串值都有 toString()方法但 null 和 undefined值没有这个方法// 2. 可以为 toString()制定一个可选的基数radix的参数前提是对象是数字类型默认情况下toString()方法以十进制格式返回数值的字符串表示通过设置基数可以输出其他进制的字符串var age 18;
console.log(age.toString(2)); // 10010;
console.log(age.toString(8)); // 22
console.log(age.toString(16)); // 12// 3第二种方法是 String()函数这个函数可以把任意类型的数值转换为字符串包括 null 和 undefined
console.log(String(null)); // null
console.log(String(undefined)); // undefined// 4如果要把某个值转换为字符串可以使用加号操作符把他与一个空字符串拼接
var str 10 ;
console.log(typeof str)1-5-4 Boolean()类型
// 1使用 Boolean()会返回一个 Boolean 值但返回 TRUE 或者 false取决于要转换值得数据类型及其实际值
// 2一元“”运算符也将其操作数转换为布尔值并取反这是在代码中进行这种类型转换的常用办法var str w;
console.log(!!str); //相当于 boolean(str)1-6 检测类型
1-6-1 typeof
// 1由于松散的数据类型可以用 typeof 方法来检测数据类型返回结果都是字符串会返回一下值
Undefined; boolean; string; number; object; null;console.log(typeof null); // object 比较特殊把他看成是空对象1-6-2 instanceof
// Typeof 在检测引用类型的数据时用处不大想要确认一个对象具体是什么类型的对象就需要 instanceof 操作符var str w;
var o new Object();
console.log(o instanceof Object);// true
console.log(o instanceof String); // false
console.log(str instanceof Number);// false// 相当于判断返回值布尔类型的console.log(typeof null); // object;
console.log(null instanceof Object); //false 注意区分说明根据规定所有引用类型的值都是 object 的实例因此在检测一个引用类型值和 Object 构造函数时instanceof 操作符始终返回 TRUE
var color new Array();
console.log(typeof color); // object;
console.log(color instanceof Object); //true;
console.log(color instanceof Array); //true;// 使用 instanceof 操作符在检测基本类型的值会返回 false因为基本类型不是对象1-7 表达式
// 1”wangwei”; “name”; 1.23; var; name; name; 都算一个表达式
// 2简单表达式原始表达式比如一些常量变量 复杂表达式有简单表达式组合一起的表达式1-8 运算操作符
// 1算数运算符 - * / %取余数 --;
// 2比较运算符 !不等 全等 !不全等;
// 3条件运算符? :
// 4逻辑运算符和 ||或 ! 非;
// 5位操作符~ 左移位 右移位 无符号右移 按位与 ^按位异或|按位或
// 6字符元算符 ;
// 7赋值运算符 - * / % ^ | ;
// 8其他操作符delete typeof void instanceof in测试属性是否存在,逗号忽略第一个操作数返回第二个操作数。1-8-1 运算符优先级 1-8-2 操作数及个数
// 1运算符所操作的数据就是操作数可以位于运算的左右两边或者一遍
// 2运算符可以根据其操作数的个数进行分类一元操作数如“”二元操作数如“*”三元操作数如“?”;1-8-3 操作数类型和结果类型
有一些运算符可以作用于任何数据类型但在真是场景中一般都是有确定的数据类型并且大部分运算符都会计算并返回一个特定类型的值
1-8-4 算数运算符
console.log(12); //12 (加法当中字符串中的数字不会自动类型转换)
console.log(1{}); //1Oobject
var sum 1 2;
console.log(sum); // 12
console.log(2 3); // 5
console.log(Number(3)2) // 5
console.log(true true); // 2
console.log(1 true); // 2
console.log(1 null); // 1
console.log(1 undefined); // NaN1考虑到加法的结合性既运算结果依赖于运算的顺序
console.log(1 2 wang); //3wang
console.log(1 wang 2); //1wang2
console.log(wang 2 1); //wang21 (都是从左右的顺序计算)2连接字符串对于 null 和 undefined则分别调用 String()函数并取得字符串”null”和”undefined”;
console.log(wang null); //wangnull
console.log(wang undefined); // wangweiundefined
console.log(wang String(null)); //wangnull
console.log(wang String(undefined)); // wangweiundefined3减法如果一个操作符是字符串布尔值null 或 undefined则先在后台调用 Number()进行转换在进行转换运算
console.log(8 - true); // 7
console.log(8 - true); // NaN 注意“TRUE”引号引起来就是字符串
console.log(8 - Number(true));
console.log(8 - null); // 8
console.log(8 - {}); // NaN
console.log(8 - 3); // 5
console.log(8 - undefined); // NaN
console.log(Number(true)); // NaN4如果一个操作数是对象数值布尔值则调用他们的 toString()或 valueOf()方法取得相应的字符串值在进行计算
console.log(8 - {toString(){return 2}}); // 65其他运算
console.log(5 - true); // 4
console.log(NaN - 1); // NaN
console.log((5 - )); // 5
console.log(5 - 2); // 3
console.log(5 - null); // 5
console.log(-0 0); // 0
console.log(-0 - 0); // -0
console.log(Infinity-Infinity); //NaN
console.log(-Infinity - Infinity); //-Infinity6乘除操作符如果操作数是非数值会执行自动类型转换既后台用 Number()函数转换空字符串被当做 0 其余遵循以下规则
// 1如果计算结果超出范围 返回 Infinity -Infinity
// 2有一个操作数是 NaN 返回 NaN
// 3Infinity 与 0 相乘 返回 NaN
// 4Infinity 与非零相乘 返回 Infinity -Infinity
// 5Infinity 与 Infinity 相乘 返回 Infinity
// 60 被 0 除 返回 NaN
// 7非零的有限数被 0 除 返回 Infinity -Infinity
// 8如果 Infinity 被任何非 0 除 返回 Infinity -Infinityconsole.log(5 / 0); //Infinity
console.log(-5 / 0); // -Infinity
console.log(0 / 0); //Nan7取模
console.log(10 % 3); // 1
console.log(6.3 % 2.1); //2.09999999999999996
// 注:小数取模会出问题8一元操作符 自增 – 自减
// 1分为前置 a 后置 a
// 2前置操作时变量的值都是语句被求值之前完成的先完成变量本身自增自减在计算整个语句var age 18;
var anotherage --age 2;
console.log(age); //先算 --age 再算 anotherage 17
console.log(anotherage); // 19// 3后置操作时先算整个语句再考虑变量本身的自增自减var age 18;
var anotherage age-- 2;
console.log(age); //先算 another 再算 age-- 17
console.log(anotherage); // 20var num1 2;
var num2 20;
var num3 num1-- num2;
console.log(num3) //先算 num3 再算 num1-- 22
console.log(num1); // 1// 4包含数字的字符串会进行自动转换 不会进行字符串的拼接var x 1;
console.log(x); //2 也把 x 的数据类型改变了 x2
console.log(x 5); // xx5 7var x 1;
console.log(x 5); // 进行字符串拼接 “15”
console.log(x); //2 也把 x 的数据类型改变了 x25在应用于对象时先调用对象的 valueof 方法如果为 NaN则再调用toString 方法再应用
var s1 2, s2 z , b false , f 1.1 ;
o {
valueOf:function(){
return -1;
}
}
console.log(s1); // 2 同时 s1 变成 3
console.log(s1); // 3
console.log(s2); //NaN 同时把数据类型也改了
console.log(s2); //NaN
console.log(b); //0 同时 b 变成 1
console.log(b);
console.log(f--); //1.1 同时 f f-1
console.log(f);
console.log(o--); // -1 同时 o 变成 -2
console.log(o); //-26一元加和减操作符可以把操作数转换成数字或 NaN而对象先调用valueOf 和 toString 方法
var s1 2, s2 z , b false , f 1.1 ;
o {valueOf:function(){return -1;}
}
console.log(s1); // 2
console.log(s2); //NaN
console.log(b); //0
console.log(f); //1.1
console.log(o); -11-9 关系比较运算符
1-9-1
// 1关系运算符用于测试两个值的关系根据他们的关系返回 TRUE 或 false通常应用在 ifwhile 等控制语句中
// 2如果两个操作数都是字符串则比较他们的大小
// 3如果一个是数值则将另一个转换为数值再比较
// 4如果一个操作数是对象则调用 valueOf()或 toString()方法得到结果再进行比较
// 5如果是布尔值将其转换为数值再进行比较
// 6Infinity 比任何数字都大除了其本身-Infinity 比任何数都小
// 7任何操作数与 NaN 进行比较结果都是 false
// 8被认定为 false 的值undefinednullNaN” ”,0,falsevar o {};
console.log(o);
o {valueOf:function(){return 3}};
console.log(3 2);
console.log(3 5);
console.log(a b);
console.log(3 2);
console.log(o 2);
console.log(a 5);
console.log(a A);
console.log(NaN ! a); //true
console.log(NaN 3); //false
console.log( NaN NaN); //false// 9大小写转换的方法
console.log(abcdef.toUpperCase());
console.log(ABCDEF.toLowerCase());1-9-2 相等和不相等 !
1这两个操作数都会先转换操作数通常称为强制转换然后再比较
console.log(2 2);
console.log(2 3);
console.log(w w);
console.log(2 2); // true
console.log(true 1);
console.log(2 ! 3);Null 和 undefined 是相等的
console.log(null undefined); //true1-9-3 全等和不全等 !
不进行类型转换
console.log( 19 19); //false
console.log(0 -0); //true
var o1 {};
var o2 o1;
console.log(o1 o2); //truevar x NaN;
console.log(x ! x); //true 只有 x 等于 NaN 时才成立Null 和 undefined 是相等但不是全等
console.log(null undefined); //false
console.log(null undefined); //true1-9-4 条件运算符
var num1 10, num2 20
var max num1num2 ? num1: num2;
console.log(max); // 求绝对值基本语法
// 表达式? 返回值 1返回值 2
// 如果表达式为真则返回 返回值 1 若表达式为假。则返回 返回值 2var x -5;
console.log(x 0 ? x : -x); //求绝对值var a 10, b 15;
console.log(a b ? true:false);判断某个变量有没有定义
var a,b;
a aini;
console.log(a? a:未定义); // aini
console.log(b? b:未定义); //未定义1-10 位操作符
// 1计算值负数正数绝对值二进制取反 加一 如求 -18 先求 18 的二进制 10010取反以后是 01101 加一以后 01110 所以-18 二进制就等于 01110再加一个符号位就是 101110
// 2按位非 ~ 按位与 按位或| 按位异或^
// 3按位非 返回数字的反码var num 25; // 25 11001 -25 00110 00001 00111
var num1 ~num; //反码 00110 所以反码是 00110 00111 - 00001 -25 - 1
console.log(num1); // -26 所以反码是 00110 00111 - 00001 -25 - 1 -264按位与有两个操作符相同位数对齐比较 全 1 出 1有 0 出 0;
var sum 25 3; // 11001
console.log(sum); // 00011 00001 15按位或有两个操作符相同位数对齐比较 有 1 出 1全 0 出 0;
var sum 25 | 15; // 11001
console.log(sum); // 01111 11111 316按位异或相同返回 0不同返回 1
var sum 25 ^ 15; // 11001
console.log(sum); // 01111 10110 227c ab 左移c a 的二进制数左移 b 位后的结果也等于 c a * 2^b;
var sum 3 3; // 11 左移 3 位就是 11000 24
console.log(sum); // 248c ab 右移(有符号的右移正数补 0可以忽略负数补 1需要注意,无符号右移只会用 0 来填补)c a 的二进制数右移移 b 位后的结果也等于 c a / 2^b;
var sum 64 3; // 1000000 右移 3 位就是 0001000 8
console.log(sum); // 81-11 赋值运算符
1赋值运算符有很低的优先级
var a,b 0;
console.log((a b) 10); //因为 优先级很低一般需要用括号括起来来提高计算的优先级// 赋值运算符的结合性是从右到左
var j k i 0; // i0,ki, jk 由于 k,i 不是用 var 声明所以属于全局白变量2如果等号前面添加了其他操作符就可以完成复合赋值操作
-*/,%,,,,,^,|
num 10;
num10; //num num 10
console.log(num);// 可用于字符串
var str aini;
str AINI;
console.log(str);// 有时候两者有区别指向的不是同一个元素会出现问题
var date [1,2,3,4];
var i 1;
// date[i]*2; // date[1] date[1]*24 [1,4,3,4]
date[i] fate[i] *2 // [1,6,3,4] date[1] -i2 -date[2] 3*26 最终
date[1]date[2]*26
console.log(date);1-12 其他运算符
1-12-1 typeof
两种语法 typeof value / typeofvalue
console.log(typeof 1);
console.log(typeof true);
console.log(typeof undefined);
console.log(typeof NaN);
console.log(typeof null);
console.log(typeof aini);
console.log(typeof function(){});1-12-2 instanceof
不能判断基本数据类型
var num 1;
console.log(num instanceof Number); // false 不能判断基本数据类型
var num1 new Number(1); // 基本包装类型
console.log(typeof num1); // object
console.log(num1 instanceof Number); //true
console.log(num1 instanceof Object);var str new String(aini);
console.log(typeof str); // object
console.log(str instanceof String); // true
console.log(str instanceof Object); // true
var colors new Array();
console.log(typeof colors);
console.log(colors instanceof Array);
console.log(colors instanceof Object);1-12-3 in 运算符
in 运算符希望它的操作数是一个字符或者可以转换为字符右操作数是一个对象如果该对象拥有一个名为左操作符的属性名则表示返回 TRUE
var point {x:1,y:1};
console.log(1 in point); // false
console.log(x in point); // true
console.log(z in point); // false
console.log(toString in point); //True 所有对象都继承了 Object 的 toString 方法
var date [1,2,3];
console.log(0 in date); // 能自动转换
console.log(1 in date); // 判断索引
console.log(3 in date); // false1-12-4 delete 运算符
能删除对象的方法和属性
var point {x:1,y:1};
delete point.x;
console.log(point); // {y: 1}
var date [1,2,3];
delete date[1];
console.log(date); // (3) [1, 空, 3]1-12-5 逗号运算符
1一次性声明多个变量需要逗号隔开
2逗号运算符在一条语句中执行多个操作在用于赋值的时候逗号操作符总会返回表达式中的最后一项
var a (1,3,5,7,9);
console.log(a); // 93其首先计算左操作数再计算右操作数最后返回右操作数的值
var num (i 2 , j 3, k ij);
console.log(num); //52流程控制
2-1 前言
1表达式语句
// 1具有副作用的表达式是js中最简单的语句如赋值语句str “zero” “network”;
// 2递增递减运算也是一种表达式
// 3Delete 运算符非作用是删除一个对象的属性所以他也是语句而不是表达式
// 4函数调用是表达式语句中的另一个大类3 2; //表达式
a 2; // 语句
alert(wangwei); //语句
console.log(x Math.cos(60)); //语句2 复合语句和空语句
复合语句和空语句可以把多条语句联合在一起形成一条复合语句使用花括号将多条语句括起来也称为代码块
{
var x Math.PI;
var cs Math.cos(x);
console.log(cos(PI) cs);
}// 注语句块结尾不需要分号空语句允许包含 0 条语句的语句其实用分号表示Js 解释器在执行空语句时不会执行任何动作但某些情况下空语句还是很有用的
var a [];
for(var i 0 ;i 10;a[i]i);
console.log(a);3声明语句 var function 4顺序结构 ES 程序就是一些列可执行语句的集合默认情况下JS 解析器依照语句的编写顺序依次执行这是程序最基本的结构 5流程控制结构 流程就是代码执行顺序流程控制就是改变语句的默认执行顺序使其按一定的方式进行 1条件(condition) 语句 2循环语句loop 3跳转语句jump 如 breakreturnthrow 语句
2-2 条件语句
2-2-1 if 条件
2-1-1 形式一
if(condition) statement;
var a 10;
if(a 10) console.log(a is 10);
if(wangwei) console.log(aini is a good person); //只要是个真值就行ES 语法规定if 关键字和带圆括号的表达式之后必须跟随一条语句但只能是一条语句可以分行写也可以写在一起如果有多条可以使用语句块
var a 10;
if(a ! 10){console.log(a);console.log(a 10);
}注一般使用 Xnull 或 Xundefined 或简写X 来判断一个变量是否有效
var x;
if(!x) x 12; //如果 x 没有值的话
console.log(x);2-1-2 x形式二
if else 语句 ifconditionstatement1else tatement2
多条语句加花括号 同?:一样
var x 10;
var y 15;
if(x 10){console.log(x 10)
}else{console.log(x ! 10)
}if(y10) console.log(y 10)
else console.log(y ! 10);2-1-3 形式三
if-else if 语句 多条件语句
var grade 41;
if (grade80 grade100)
console.log(良好);
else if(grade60)
console.log(及格);
else if(grade40)
console.log(不及格);
else
console.log(0);2-1-4 if 语句嵌套
var a 30;
if(a 0 a10){
if(a0 a5){console.log(a 在 5 之内);
}else{console.log(a 在 6 到 10 之内);
}
}else if(a 10 a20){if(a10 a15){console.log(a 在 11 到 15 之内);}else{console.log(a 在 16 到 20 之内);
}
}else{console.log(a 大于 20);
}2-2-2 Switch 语句
// 语法
Switch(expression){Case value 1:statement ; break; case value 2: statement;break;default :statement;}var n 2;
switch(n){
case 1:
console.log(1);
break;
case 2:
console.log(2);
break;
case 3:
console.log(3);
break;
case 4:
console.log(4);
break;
default:
console.log(other);// 本质来讲case只是指明了程序运行的起点只要找到复合的case语句就开始进行直到程序结束
// 每一个case语句里加break不然找到了第一个匹配的值以后就会继续往下算直到程序结束
// Default 是当所有case不匹配时才运行一般放到最后面var n 2;switch(true){case n 0:console.log(大于0);break;case n 0:console.log(等于0);break;case n 0:console.log(小于0);break;}// 如果把所有可能性已经囊括了就不需要写default
// Case后具体的一个值运算返回常量变量
// Case语句在比较值是用的是全等操作符因此不会进行类型转换所以字符串”10”不等var n 1;switch(n){case 1:case 2:console.log(2);break;case 3:console.log(3);case 4:console.log(4);break;}如果在函数中使用Switch可以使用return代替break两者都用于终止Switch语句也会防止一个case语句块执行完后继续执行下一个case语句块
function convert(x){switch(typeof x){case number:return x.toString(16);case string:return x ;default:return other; }}console.log(convert(18));console.log(convert(aini));console.log(convert(true));// Return会返回值并直接退出函数var str world;switch(hello world){case str:console.log(str);break;case world:console.log(world);break;case hello str:console.log(hello str);break;default:console.log(other);}switch(true){case wangwei: //这是真值但不是布尔的TRUE也不会进行自动转换所以不成立console.log(wangwei);break;case 1:console.log(1);break;
case 3 2: //返回的结果是 TRUE 所以会运行case 3 2的情况console.log(true);break;default:console.log(other);}var str 10;switch(str){case 10: console.log(这是数字10);braak;case 10:console.log(这是字符串10);break;}// Case语句在比较值是用的是全等操作符因此不会进行类型转换所以字符串”10”不等于数字10Case 后面建议使用常量避免使用副作用的表达式
3循环控制
3-1 while 循环
1属于前测试循环语句循环之前判断条件是否成立
2语法 whileexpression{statement}
var count 0;while(count 10){console.log(count);count;};var count 0;var sum 0;while(count 100){sum countcount;};console.log(sum);// while语句里的分号建议加上var i 18;while(true){console.log(i)if(i 18){break;}}
// break语句能结束while循环跳出循环有时候为了避免死循环可以使用3-2 Do while 循环
1后测试循环先执行一遍循环再判断条件是否满足所以循环至少执行一次
var i 0;do{i2;}while(i10)console.log(i);// 也可以这样用改变条件的表达式直接写在var i 0;do{console.log(i);}while((i2) 10);// 这样可以先改变条件再进行循环相当于前测试循环function printArray(arr){var len arr.length,i0;if(len 0){console.log(Emprty Array)}else{do{console.log(arr[i]);}while(i len);}}var arr[beijing,nanjing,xinjiang,gaungdong];printArray(arr);bodydiv idmydiv/divscriptvar imgArr [xm/1.webp,xm/2.webp,xm/3.webp,xm/4.webp,xm/5.webp,xm/6.webp,xm/7.webp,xm/8.webp,];var mydiv document.getElementById(mydiv);var i 0;while(i imgArr.length){var img document.createElement(img);img.src imgArr[i];mydiv.appendChild(img);i;}/script
/body
3-3 for 循环
1前测试循环语句已知循环次数情况下使用for循环比较合适
2语法 for(初始循环变量;test条件表达式 ;增量表达式){statement}
3初始循环变量只是循环开始时执行一次
for(var i 0; i 10; i){console.log(i);}
// 跟上面的for循环等价var i 0;while(i 10){console.log(i);i;}var count 0;while(count,count 10){console.log(count);}// 注不能把初始化变量放到while括号里面但是可以放不影响增量的其他变量
// 从左往右计算返回逗号运算符最右侧的代码var result 1,i 1;while(i 8){result*i;console.log(i 的阶乘是: result);i;};简化上面的while循环 var result 1,i 1;while(result*i, i,i 8){console.log(i 的阶乘是: result);};var result 1;for(var i 1; i 8; i){result*i;console.log(i 的阶乘是: result);}var count 10,i;for(i0;icount;i)console.log(i);
// i可以在外部定义for循环里面就不用定var count 10,i0;for(;icount;i)console.log(i);
// For循环中初始化变量条件表达式增量表达式都是可选的但是分号不能省略var count 10,i0;for(;;i)if(icount){console.log(i);}else{break;}// 这是一个死循环var count 10,i0;for(;;)console.log(i);把初始化放到外部把条件表达式以及更新表达式放到内部条件不满足使用break跳出循环
var count 10,i0;for(;icount;){console.log(i);i;} // 所以可以把初始化和增量放到外部可以有多个初始化变量条件表达式增量表达式需要用逗号运算符
var sum 0;for(var i0,j1;i10;i,j){sum i*j;console.log(i , j);}console.log(sum);For循环的执行顺序
for(var i0,jconsole.log(A);console.log(B),i10;console.log(C), i){console.log(aini);}// 顺序
i0,jconsole.log(A) console.log(B),i10 console.log(aini) console.log(C), i console.log(B),i10 console.log(aini) console.log(C), i 总之初始变量语句只运行一次增量表达式在statement以后再执行
for(var i0,j1;i10;i,j){document.write(i);if(j%40){document.write(/br);}}3-4 for-in 循环
1for in 是精准的迭代语句
2语法 forvariable in object{statement}
for(var proname in window){console.log(proname);}
// window是一个内置对象for in可以把内置对象的所有属性方法事件枚举出来var arr[];for(var i0;i10;i){arr[i]i*10;}for(var j0;jarr.length;j){console.log(arr[j]);}for(var k in arr){console.log(数组中的第 k 个元素是 arr[k]);}// 把数组中的元素枚举出来,可以用for循环也可以用for-in语句
// For-in来枚举数组索引var str hello world;for(var a in str){console.log(a 字符 str[a]);}// 字符串也可以像数组一样枚举出来var person {name:aini,age:18,walk:function(){console.log(aini can walk) }}for(var p in person){console.log(p : person[p]);}// 可以把对象的属性和属性值枚举出来
// 注P是属性person[p]才是属性值数组里面也是同理索引和值ESMAScript对象的属性没有顺序因此用for-in循环出来的属性名的顺序不可预测如果迭代的对象时null或undefinedES5之前会抛出错误在ES5里面不会报错误也不执行如果object表达式等于一个原始值这个原始值会转换为与之对应的包装对象
var num 18;for(var n in num){console.log(n);}var str hello world;for(var a in str){ //这里使用了new String(str) 把原始类型转换为基本包装类型console.log(a);}ar person {name:aini,age:18,walk:function(){console.log(aini can walk) }}var a [],i0;for(a[i] in person);console.log(a);// A[i]在这里既可以当变量也可以自增同时对自己赋值For- in并不会遍历所有的属性和方法只会枚举可枚举的属性和方法由js核心语言内置的属性和方法就是不可枚举的比如toString(),valueOf();
各种循环可以任意嵌套
3-5 跳转
1可以从一个位置跳转到另一个位置
2标签语句任何语句如果在前面加上标识符和冒号都可以构成标签语句以便将来使用
语法 labelstatement var flag true;one: if(flag){console.log(flag);}start:for(var i0; i10;i){console.log(i);}two:{var num 18;numconsole.log(num);} // 一般与forwhile语句配合使用既在循环体内使用break和continue退出或跳转到标签外二者是唯一可以使用标签的语句
// 标签可以跟变量函数名同名
// 标签可以嵌套
// 嵌套时标签名不允许跟标签名重复one: for(var i 0;i5; i){two: for(var j 0; j5;j){console.log(ij);}}// 嵌套里的标签不允许// 单独使用break语句可以立即退出最内层的循环或者Switch语句break语句只能用在循环或者Switch中
// reak与标签语句配合使用退出这个循环的同时跳转到标签处结尾处可以用于跳出多层循环的var num 0;outermost:for(var i 0;i 10;i){for(var j 0; j 10;j){if(j5){break outermost;}console.log(i , j);num;}}console.log(num);var num 0;outermost:for(var i 0;i 10;i){inner: for(var j 0; j 10;j){if(j5){break;}console.log(i , j);num;}for(var k 0;k5;k){if(k2){break inner;}}}console.log(num);var sumarr [[1,2,3],[2,a,4],[4,5,6]],sum 0;compute_sum:if(sumarr){for(var i0;isumarr.length;i){var row sumarr[i];if(!Array.isArray(row)){break compute_sum;}for(var y 0; y row.length; y){var cell row[y]if(isNaN(cell)){break compute_sum;}sumcell;}}console.log(全部执行完毕);}console.log(sum);3-6 continue 语句
// Continue语句与break语句非常相似区别在于他不在退出循环而是结束本次循环继续下次循环其与break用法基本一致
// Continue只能用在循环体当中for(var i 0;i5; i){if(i 3) continue;console.log(i);}
// 遇到3结束本次循环开始下次var date [1,2,undefined,4,NaN,8,9,null]; sum 0,error 0;for(var i 0; idate.length;i){if(!date[i]){error1;continue;} sumdate[i];}console.log(sum);console.log(error);// Continue可以与标签配合使用就是跳出外侧循环继续进行外侧循环的下一次循环3-7 return 语句
// Return语句只能在函数中使用既函数调用后的返回值
// 如果没有return语句则程序返回的结果就是undefined
// Return默认返回值也是undefinedfunction display_object(o){if(!o) return ;for(p in o){console.log(p);} return;}var person {name:aini,age:18,city:xinjiang}console.log( display_object(person)) ;3-8 with 语句
// 为了简写代码
Withobject{statement}var person {name:aini,age:18,city:xinjiagn}console.log(person.name);console.log(person.age);console.log(person.city);// 可以这样简写代码with(person){console.log(name);console.log(age);console.log(city);}3-9 Debugger语句
// 什么也不做用于调试不怎么重要会在debugger语句的地方产生一个断点3-10 “use strict” 语句
// 让js代码在严格模式下执行4函数
4-1 定义函数
4-1-1 第一种方式
// 第一种方法函数声明式关键字function定义4-1-2 第二种方式
// 第二种方法function实际上也是对象所以可以使用function对象的构造函数来创建一个函数
// Var 变量名 new Function(形参1形参2代码体)var date var num prompt(input);console.log(num);
var date1 alert(aini);
var date2 var pfname:aini,age:19};console.log(p.name);
var fun new Function(date2);
fun();var scope global;|
function func(){var scope local;var myFun new Function(return scope;);console.log(myFun());
}
func();
// 注只能认全局变量在函数中的局部变量识别不了所以上述例子返回“global”内部的“local”识别不了
// 注1参数代码体必须使用引号引起来代码体放在最后一个引号里语句和语句之间分号引起来
// 2如果没有参数则省略只有代码体的引号4-1-3 第三种方式
可以直接把定义函数保存在变量中该变量名实际上就是函数
// Var 变量名 function参数1参数2{表达式语句}var mysum function(num1,num2){return num1num2;
console.log(mysum(10,20));
由于函数名仅仅是函数的指针所以可以把函数名赋值给多个变量这么说一个函数可以有多个名字
script
function sum(num1,num2){return num1 num2;
}
mysum sum;
anothersom sum;
console.log(mysum(10,20));
console.log(anothersom(30,50));
/script
把函数名赋值的时候不能带括号带括号的话立马就执行了
script
var sayworld;
var condition false;
if(condition){sayworld function(){console.log(aini)};
}else{sayworld Function()fconsole.1og(AINI)};
}
sayworld();
/script
函数声明和函数表达式的区别
// 解释器在执行环境中加载数据时会率先读取函数声明并使其在执行任何代码之前可用可以访问至于表达式则必须等解析器执行到它所在的代码行才会真正被解析sayHi();
function sayHi(){console.log(hi);
}// Function函数声明的时候访问可以放在函数之前也可以放在函数后面sayHi();
var sayhi function(){console.log(Hi);// 通过表达式定义的函数访问必须在定义之后不然会出错4-1-4 定义函数的三种方式
// 1函数声明
// 2利用Function类构造函数
// 3通过函数表达式构造函数// 函数的调用可以在代码中调用也可以在事件中调用还可以通过链接调用script
function sum(a,b){return ab;
}
console.log(sum(10,20));
var sum1 function(a,b){return a b;
}
console.log(sum1(20,30));
var fun new Function(a,b,return ab);
console.log(fun(50,100));
/script// 1如果调用函数时输入的实参的个数大于形参的个数则忽略多余的实参
// 2实参的个数小于形参则没有接受传递值得参数赋予undefined// Return返回值只要执行带return就会返回并退出后面的代码永远不会执行return只能用在函数当中// Return 也可以不带任何返回值在这种情况下函数就会停止运行并返回undefined// 函数中不用return也会返回undefined4-1-5 一些例子 5JavaScript 引用类型
// 本地对象独立于宿主环境的由ECMAscript实现提供的对象如objectFunctionArray等
// 内置对象由ECMAscript实现提供的独立于宿主环境的所有对象如Global和Math本质上是静态的就不需要创建对象
// 宿主对象所有非本地对象都是宿主对象既由scmascript实现的宿主黄江提供的对象如多有BOM和DOM对象丢失宿主对象5-1 object 类型
5-1-1 创建
// 创建object实例两种方法1var person new Object();
2Var person {}; (属性名可以是字符串用引号引起来属性名中的空格无法识别)
3数值属性名会自动转换成字符串但是用方法不同ar myo {}; // 是一个空对象只包含默认的属性和方法
Var myo new Object(); // 两者可以等同为对象添加方法 // 注字面量对象花括号里面用逗号
// 类创建对象里面的类里面使用分号5-1-2 对象属性
// Object对象属性是动态的// 两种访问方式1使用点访问如person.name;2使用方括号表示法属性应以字符串的方式放在括号中// 注如果属性名中包含会导致语法错误的字符或者属性名使用的是关键字或者保留字就可以使用方括号访问法5-1-3 动态添加属性和方法 5-1-4 Object类及对象的属性和方法
4-1 constructor 属性
对创建对象构造函数的引用如 4-2 prototype 属性
对对象的原型的引用 4-3 obj.hasOwnProperty(property)
判断该对象是否具有某个属性 4-4 Object.prototype.isPrototypeOf(object)
判断该对象是否为另一个对象的原型 // 就是判断obj是不是由object创建出来的// 结果是TRUE因为p就是由person创造出来的4-5 Obj.propertyIsEnumerable(property)
判断给定的属性是否可以用for in 枚举 4-6 obj.toString()
返回对象的原始字符串表示
4-7 obj.valueOf() :
返回最适合该对象的原始值
5-2 其他引用类型
1数组 var arr new Array(1,2,3,4,5);
2字符串 var str new String(“aini”);
3真则表达式 var pattern new RegExp(“w” , “i”);5-3 对象的废除
// 一个对象不使用应该废除释放内存JS自带垃圾回收程序
// 强制销毁object null这个对象就不存在了
// 必须把所有对象的所有引用都设为null对象才会被清除
// Delete操作符删除不掉对象但是可以删除对象的属性对象的引用5-4 基本引用类型和引用类型的值
// 对象的比较并非值得比较即使两个对象包含相同的属性和相同的值也并不相等// 对象的比较是引用的比较当且劲当他们引用同一个对象时他们才相等5-5 引用类型的复制 判断两个对象是否有相同的属性值 6JS基本包装类型
// ES提供了三个基本的引用类型Booleanj ,Number , String;
// 但是同时还具有与各自的基本类型相应的特殊行为
// 实际上每当读取一个基本类型的值得时候后台就会创建一个对应的基本包装类型的对象让我们用一些方法来操作这些数据// 后台默认给s1创建了一个String对象是临时的一个对象我们是看不见的再调用了他的substring()方法调用完了以后再把这个临时对象销毁
// 后台默认给s1创建了一个引用类型并调用了他的substring()方法赋给s2;基本包装类型的实例调用typeof 会返回object而且所有包装类型的对象转换为布尔值类型都是TRUE Object构造函数仅接受一个参数其会根据传入的值得类型返回相应包装类型的实例 ES在必要时会将包装类型转换成原数值如 实际上比较的是他们的值基本包装类型用valueOf方法返回他的值再进行比较所以两者相等
注null和undefined没有包装对象访问他们的属性会造成一个类型错误
6-1 Boolean 类型
// 1Boolean类型是与布尔值对应的引用类型Boolean 对象表示两个值true 或 false。
// 2创建Boolean对象var oBoolnew Boolean(true); // 传入true或false值
// 注如果逻辑对象无初始值或者其值为 0、-0、null、、false、undefined 或者 NaN那么对象的值为 false否则其值为 true// 不判断对象的值而是判断这个对象存不存在所以返回TRUE// 总结理解基本类型的布尔值与Boolean对象之间的区别非常重要建议不要使用最好使用Boolean原始值
// 可以使用Boolean(参数)进行数据类型转换;
6-2 Number 类型
// 1Number 对象是原始数值的包装对象。在必要时JavaScript 会自动地在原始数据和对象之间转换
// 2可以用构造函数 Number() 明确地创建一个 Number 对象6-2-1 对象属性
// 对象属性1MAX_VALUE: // 表示最大的数静态属性
2MIN_VALUE: // 表示最小的数静态属性
3NaN: // 非数字值静态属性
4NEGITAVE_INFINITY:// 负无穷大溢出时返回该值静态属性
5POSITIVE_INFINITY:// 正无穷大溢出时返回该值静态属性一般用于判断表达式
// 注Number.NaN 是一个特殊值说明某些算术运算如求负数的平方根的结果不是数字。
// 方法 parseInt() 和 parseFloat() 在不能解析指定的字符串时就返回这个值。6-2-2 对象方法
Number类或对象方法
1parseInt() 和 parseFloat()方法静态方法
2isNaN()// 来判断是否为数字静态方法
3isFinite// 是否为有限静态方法
4isInteger// 是否为整形静态方法
5toString()方法: // 如:NumberObject.toString(radix)radix可选规定表示数字的基数使 2 ~ 36 之间的整数若省略该参数则使用基数 10。6tolocaleString() :// 把一个Number对象转换为本地格式的字符串
7toFixed() 方法// 把 Number 四舍五入为指定小数位数的数字类型是字符串参数规定了小数的位数是 0 ~ 20 之间的值有些实现可以支持更大的数值范围。如果省略了该参数将用 0 代替8toExponential()方法 // 可把对象的值转换成指数计数法。如:NumberObject.toExponential(num) num规定指数计数法中的小数位数是 0 ~ 20 之间的值// 用指数计数法表示而且还要保留指定的小数位数9toPrecision() // 可在对象的值超出指定位数时将其转换为指数计数法其可能会返回固定大小格式也可能返回指数格式具体规则是看哪种格式最合适如:NumberObject.toPrecision(num) num规定必须被转换为指数计数法的最小位数该参数是 1 ~ 21// 就是小数点后或者整数位加起来就等于指定的位数若不满足可以用科学计数法来凑6-3 String 对象
String类型是字符串对象包装类型用于处理文本字符串。语法var str new String(str); 如 // 上面两个是对象
// 下面是强制类型转换的字符串6-3-1 字符串属性
1-1 length属性
返回字符串中的字符个数 如: str.length 汉字也是一个字符
6-3-2 字符串操作方法
2-1 charAt() charCodeAt()
都接受一个参数是基于0的字符位置其中charAt() 方法以单字符串的形式返回给定位置的那个字符 如果想得到字符编码而不是字符时使用charCodeAt()如 也可以用方括号来访问字符串向数组一样 2-2 concat()
用于将一或多个字符串拼接起来返回拼接得到的新字符串如 concat()方法可以接受多个任意参数即可以通过它可以拼接任意多个字符串如 2-3 slice(x,y)
// x位置开始截取到y结束不包含y位置 2-4 substr(x,y)
// x是截取开始的位置y是截取长度substr()将负的第一个参数加上字符串的长度而将负数的 第二个参数转换为0 2-5 substring(x,y)
// x位置开始截取到y结束不包含y位置// 如果只设置一个参数则从开始位置截取到末尾substring()方法会把所有负值参数都转换为0 // 同时这些方法的值可以是负值其中slice()会将传入的负值与字符串的长度相加substr()将负的第一个参数加上字符串的长度而将负数的第二个参数转换为0substring()方法会把所有负值参数都转换为0如2-5-1 文字滚动效果 6-3-3 字符串位置方法
3-1 indexOf() 和 lastIndexOf()
// 这两个方法都是从一个字符串中搜索给定的子字符串然后返回子字符串的位置如果没有找到子字符串则返回-1// lastIndexOf从后面往前找两个方法只返回第一个出现的位置// 可以设置第二个参数表示从什么位置开始查找在使用第二个参数的情况下可以循环调用两个办法找到所有匹配的字符串 3-2 trim()
trim()方法.// 删除前置及后缀的所有空格然后返回结果还有两个非标准的trimLeft()和trimRight()方法 // 分别用于删除字符串开头和末尾的空格3-3 大小写转换
toLocaleLowerCase() // 把字符串转换为小写
toLocaleUpperCase() // 把字符串转换为大写
toLowerCase() // 把字符串转换为小写
toUpperCase() // 把字符串转换为大写// 其中toLowerCase()和toUpperCase()是最常用的方法6-3-4 字符串匹配方法
4-1 match()
本质上与RegExp的exec()方法相同其只接受一个参数要么是一个正则表达式要么是一个RegExp对象如 // 说明match()返回了一个数组其第一项是与整个模式匹配的字符串之后的每一项如果有保存着与正则表达式中的捕获组匹配的字符串4-2 search()
接受的参数与match()一样该方法返回字符串中第一个匹配项的索引如果没有找到匹配项则返回-1如 4-3 replace()
replace()方法// 替换字符串接受两个参数第一个参数是一个查找的字符串或者是正则第二个参数是要替换的字符串或者函数
// 注意如果第一个参数是字符串则只会替换第一个找到的字符串想替换所有的话第一个参数可以写个正则// 想要替换全部就加上g(全局标志)不然只会替换第一个如果没匹配上替换不了就会输出原有的值4-4 split
split()方法// 基于指定的分隔符将一个字符串分割成多个字符串并把结果放在一个数组中分隔符可以是字符串也可以是正则其可以接受可选的第二个参数用于指定数组的大小第二个参数可以指定长度 也可以是正则表达式
6-2-5 其他方法
5-1 ocaleCompare()
localeCompare() // 用本地特定的顺序来比较两个字符串默认返回下列值中的一个-1、0、1如5-2 fromCharCode()
fromCharCode()方法 // String构造函数本身还有一个静态方法fromCharCode()这个方法的任务是接受一或多个字符编码然后将它们转换成一个字符串从本质上看这个方法与实例方法charCodeAt()执行的是相反的操作把asccII码转换成对应的字母或数字
5-3 HTML方法
// 早期的Web浏览器可以使用Javascript动态格式化HTML其扩展了字符串的标准实现了一些专门用于简化常见HTML格式化任务的方法但是尽量不要使用这些方法因为它们创建的标记通常无法表达语义anchor(name) // 创建 HTML 锚输出如a name”name”string/a
big() // 用大号字体显示字符串如bigstring/big
small() // 使用小字号来显示字符串如smallstring/small
blink() // 显示闪动字符串
bold() // 使用粗体显示字符串如bstring/b
fontcolor(color) // 使用指定的颜色来显示字符串如font color”colorstring/font
fontsize(size) // 使用指定的尺寸来显示字符串如font size”size”string/font
italics() // 使用斜体显示字符串如istring/i
link(url) // 将字符串显示为链接如a href”url”string/a
fixed() // 以打字机文本显示字符串如ttstring/tt
sup() // 把字符串显示为上标如supstring/sup
sub() // 把字符串显示为下标如substring/sub
strike() // 使用删除线来显示字符串如strikestring/strike7Global 对象
// 全局对象及其属性在任何地方都可以直接使用
// Global对象是对象不是类所以没有构造函数不能new实例化
// 注所有全局变量也都是全局对象的属性这个对象是不存在的换句话说不属于任何其他对象的属性和方法最终都是它的属性和方法 因此它没有名字Global不是全局对象的名字只是我们这样称呼它 // isNaN就是全局对象的函数7-1 全局属性
// 如undefined、NaN以及Infinity都是Global属性
// ECMAScript5禁止给undefined、NaN和Infinity赋值这样做即使在非严格模式下也会导致错误。7-2 全局对象
Mathjson 7-3 全局函数
// isNaN(), isFinite(), parseInt(), parseFloat()等
// isNaN()用来确定一个值是否为NaN而Number.isNaN()确定传递的值是否为NaN和其类型是Number它是原始的全局isNaN的强大的版本// Number.isFinite()和全局的isFinite()相比不会强制将一个非数值的参数转换成数值这就意味着只有数值类型的值且是有穷的才返回true// 只有为Number的值且是有限的时候Numebr.isFinite()会返回true。而isFinite()在参数经过Number转换后再判断是否是有限的。正无穷、负无穷和NaN都不是有限数字。// Number.isInteger()用来判断给定的参数是否为整数只有是Number类型的值并且是整数才会返回trueInfinity和NaN都不是整数。7-4 全局构造函数
// 全局对象还定义了一些属性用来引用Javascript预定义的所有其他对象这些对象大部分是原生引用类型的构造函数Object、Number、String、Boolean、Array、Date、Function、RegExp、Error、EvalError、RangeError、ReferenceError、SyntaxError、TypeError、URIError// 根据JavaScript的运行环境在JS中存在两种全局对象JS全局对象和window全局对象// 当Javascript放在特定宿主环境时全局对象通常具有与该特定环境相关的额外属性这些额外属性并不是ES标准规定的而是由宿主环境实现的如在客户端Javascript中全局对象是Window对象表示运行JS的Web浏览器窗口或在nodejs中Global指的就是global对象// 全局中的this由于全局对象位于作用域链的最顶部所以在全局环境中关键字this指的就是全局对象如果在是浏览器中this指的就是window对象This 就相当于全局对象的代理人在浏览器环境中就是window
在ES中全局对象的预定义属性都是不可枚举的因此使用for/in来只能列出所有隐式或显式声明的全局变量如 7-5 全局变量
所有的全局变量都全局对象的属性
声明的4种方法:
// 1.直接在全局作用域中用var 声明的变量就是全局变量此种方式声明的变量具有不可配置的属 性不能使用delete操作符把变量删除。// 2.window.变量,这种声明的变量也是全局变量但这种变量跟上面用var 声明的变量有点不一样这种方式声明的全局变量是可配置的因此能用delete操作符把变量删除。// 3.隐式声明全局变量就是不使用var声明直接进行赋值的变量在不严格模式中相当于window.变量这种方式但在严格模式下会报错。// 4.在html中给标签指定一个id属性也相当于给Window对象添加了一个id的属性在javascript中可直接通过标签的id访问该标签或者window[id]。7-6 Global对象的方法
7-6-1 字符编码方法
1-1 Escape() 函数
// escape() 函数可对字符串进行编码这样就可以在所有的计算机上读取该字符串(其中某些字符被替换成了十六进制的转义序列 )// 注:该方法不会对 ASCII 字母和数字进行编码也不会对下面这些 ASCII 标点符号进行编码- _ . *var str aini is a good boy
var a_,.艾尼!-;
console.log(escape(str));
console.log(escape(a));// 转换成以%开头的16进制码
// 如 空格→%20 艾尼 → %u827e %u5c3c1-2 unescape() 函数
unescape() 函数
// unescape() 函数可对通过 escape() 编码的字符串进行解码。// 注:通过找到形式为 %xx 和 %uxxxx 的字符序列x 表示十六进制的数字用 Unicode 字符 \u00xx 和 \uxxxx 替换这样的字符序列进行解码。console.log(unescape(aini%20is%20a%20good%20boy));解码方式
// 把% 抓换成 \ ,这样以后浏览器就能识别\uxxxx的Unicode编码了console.log(\u827e\u5c3c); // 艾尼7-6-2 URL编码方法
// Global对象的encodeURI()和encodeURIComponent()可把字符串作为 URIUniform Resource Identifiers通用资源标识符进行编码以便发送给浏览器// 有效的URI中不能包含某些字符如空格这两个编码方法用特殊的UTF-8编码替换所有无效的字符从而让浏览器能够接受和理解。// 注两个方法不会对 ASCII 字母和数字进行编码也不会对这些 ASCII 标点符号进行编码 - _ . ! ~ * ‘ ( )
encodeURI()也不会对在URI中具有特殊含义的符号;/?:$,#进行转义但encodeURIComponent()会// 其中encodeURI()主要用于整个URI如
http://www.zeronetwork.cn/my case.html而encodeURIComponent()主要用于对URI中的某一段如my case.html进行编码// 它们的主要区别在于encodeURI()不会对本身属于URI的特殊字符进行编码如冒号、正斜杠、问号和井号而encodeURIComponent()则会对它发现的任何非标准字符进行编码2-1 encodeURI() 和 encodeURIComponent()
encodeURI()函数 和 encodeURIComponent() 函数var strzero!ne work零-_!~(*#?;
console.log(encodeURI(str));var strzero!ne work零-_!~(*#?;
console.log(encodeURIComponent(str));// encodeURIComponent() 函数 编码范围广一点用的时候仅仅对URL里面某一部分进行编码var url1https://www.zeronetwork.cn/case.html?user%20nameage18#one;
console.log(encodeURI(url1));
console.log(encodeURIComponent(url1));// 所以encodeURICompenent不适合对整个URL进行编码
// 注一般来说使用encodeURIComponent()的场景比使用encodeURI()要多因为在实践中更常见的是对查询字符串参数而不是对基础URI进行编码2-2 decodeURI() 和 decodeURIComponent()
decodeURI() 和 decodeURIComponent()
// 其中decodeURI() 只能对 encodeURI() 编码过的 URI 进行解码
// 如可将%20替换成一个空格但不会对%23作任何处理因为%23代表井号而井号不是使用encodeURI()替换的
// 同样的decodeURIComponent()能够解码encodeuricomponent()编码的所有字符即它可以解var url1https://www.zeronetwork.cn/case.html?user nameage18#one;
console.log(encodeURI(url1));
console.log(encodeURIComponent(url1));console.log(decodeURI(https://www.zeronetwork.cn/case.html?user%20nameage18#one));
console.log(decodeURIComponent(https%3A%2F%2Fwww.zeronetwork.cn%2Fcase.html%3Fuser%20name%26age%3D18%23one));// 注:URI方法代替了已经被ECMA-262第3版废弃的escape和unescape()方法因为URI方法能对所有Unicode编码,而原来的方法只能对ASCII符号正确编码因此在实际场景中一定要使用URI方便不要再使用escape()和unescape()方法onsole.log(escape(http://www.zeronetwork.cn/index.html?id101username艾尼));
console.log(escape(?!()#%));console.log(encodeURI(http://www.zeronetwork.cn/index.html?id101username艾尼));
console.log(encodeURI(?!()#%));console.log(encodeURIComponent(http://www.zeronetwork.cn/index.html?id101username艾尼));
console.log(encodeURIComponent(?!()#%));7-7 eval 函数
//只接受一个字符串参数是正常的JS或者ES代码
// 可以动态执行JS代码function swapImg(){var img eval(document.getElementById(myimg))myimg.srcimages/02.jpg;
}
var myImgdocument.getElementById(myimg);
myImg.onmouseoverswapImg;同样的也可以在eval()中定义一个函数在该调用的外部代码中引用这个函数如
eval(var msghello world;);alert(msg);// 注在eval()中创建的任何变量或函数都不会被提升因为在解析代码的时候它们被包含在一个字符串中它们只在eval()执行的时候被创建console.log(str); // undefined
var str aini like dilnur;// 虽然变量声明在后访问在前但是str会被提升但是他的值不会被提升所以str存在返回undefinedshow();function show(){console.log(show);
}// 这里函数也提升了eval(console.log(str));
straini like dilnur;// 会报错str未定义说明str没有被提升var sglobal;
function func(){eval(var s local——;) // 局部的 在外边不能够访问window.eval(var s window.local;) //全局
}
func();
console.log(s); // window.localvar x y global;
function f(){var x local;eval(xchanged;) //改变的是局部x的值console.log(x);
}function g(){var y local;window.eval(ychanged;); //改变的是全局x的值console.log(y)
}
f(); //返回函数局部变量 x 的值
g(); //返回局部变量 y 的值由于没改变 所以返回 local
console.log(x); //返回全局变量 x 的值
console.log(y); //返回全局变量 y 的 值所以说window.eval在函数内能改变全局变量的值
// 函数内可以访问到全局变量函数外不能访问函数内的局部变量当函数内函数外声明同一个变量时函数内优先访问函数内的局部变量Window.eval()在函数内优先访问全局变量
eval() 当在函数内部作用域中eval执行的是局部如果调用window.eval则在全局空间执行
// 注在严格模式下ES5对eval()的行为施加了更多的限制在外部访问不到eval()中创建的任何变量或函数同时为eval()赋值也会导致错误不是严格模式下
var x 18;
eval(var y20;console.log(y));
console.log(y); // 外部也能访问在严格模式下
use strict
var x 18;
eval(var y20;console.log(y));
console.log(y); // 外部不能访问7-8 window对象
// ECMAScript虽然没有指出如何直接访问Global对象但Web浏览器都是将这个全局对象作为window对象的一部分加以实现的因此在全局作用域中声明的所有变量和函数就都成为了window对象的属性var colorred;
function sayColor(){console.log(window.color); //就可以当window的属性用
}
sayColor();// 注JavaScript中的window对象除了扮演ECMAScript规定的Global对象的角色外还承担了很多别的任务即window对象下的一些方法和属性都是js原生提供的但在浏览器环境中大量的功能都是由宿主对象完成的另外一种取得Global对象的方法var globalfunction(){return this;
}(); // 相当于global();// 定义的同时函数也运行了// 说明分析以上代码创建了一个立即调用的函数表达式返回this在没有给函数明确指定this值的情况下this值等于Global对象var globalfunction(){return this;
}(); // 相当于global();var straini like dilnur;
console.log(str);
console.log(window.str);
console.log(this.str);
console.log(global.str);在函数内访问外部变量的方法
var globalfunction(){return this;
}(); // 相当于global();var straini like dilnur;function show(){var straini;console.log(window.str);window.eval(str);console.log(global.str);console.log(this.str); //容易产生歧义
}
show();7-9 Math 对象
// Math对象是一个静态对象或者称为工具类不能使用new关键字创建对象,应直接使用”对象名.成员”的格式来访问其属性和方法,如:
var numMath.random();7-9-1 math对象的属性
E// 自然对数的底数即常数e.约等于2.718
LN10// 代表10的自然对数,约等于2.302
LN2// 代表2的自然对象,约等于0.693
LOG2E// 代表以2为底E的对数
LOG10E// 代表以10为底E的对数
PI// 代表II的值,约等于3.1415926
SQRT1_2// 代表2的平方根分之一约等于0.707
SQRT2// 代表2的平方根,约等于1.414console.log(Math.PI);
console.log(Math.E);
console.log(Math.SQRT1_2);
console.log(Math.abs(-5));7-9-2 math对象的方法
2-1 min()和max()
// 用于确定一组数值中的最小值和最大值这两个方法都可以接收任意多个数值参数var max Math.max(3,25,45,78,96,54,100);
var min Math.min(87,45,12,59,87);
console.log(max);
console.log(min);注数组中不能用所以需要借助apply()方法如
var arr[1,25,84,75,95,42,35,68];
var maxMath.max.apply(Math,arr);
console.log(max);2-2 ceil()
// 向上取整console.log(Math.ceil(1.1)); // 2console.log(Math.ceil(-1.5)); // -12-3 floor()
// 向下取整
console.log(Math.floor(1.1)); // 12-4 round()
// .5的时候round返回比它大的整数所以负数有点不一样
// 注在round里面使用负数时注意console.log(Math.round(-1.1));console.log(Math.round(-1.5)); //注意中间的console.log(Math.round(-1.6));2-5 Random()
Random()方法// 随机数
// 产生[0,1)之间的随机数console.log(Math.random());般与其他方法配合使用以求得一个在一个范围内的随机数
console.log(Math.floor(Math.random()*101)); // [1,10]总结公式
Math.floor(Math.random()*(max-min1)min)得到一个区间的整数随机数
function intRandom(min,max){return Math.floor(Math.random()*(max-min1)min);
}
console.log(intRandom(10,100));数组当中随机拿出一项
var colors[red,blue,green,black,purple];
var colorcolors[Math.floor(Math.random()*(colors.length))];
console.log(color);随机返回字符 var strABCDEFGHIJKLNOPQURSTUVWXYZabcdefghijklnopqurstuvwxyz0123456789;var a;
function getRandomStr(x){for(var i0;ix;i){var randomMath.floor(Math.random()*(str.length-1));a str.substring(random,random1);}return a;
}
console.log(getRandomStr(4));从中文里面随机取x个字符
// 首先确定汉字编码的范围
4E00 ----------------9FA5
// 16进制转换成10进制
19968 -------------40869function getRandomHan(len){var str;for(var i0;ilen;i){c(Math.floor(Math.random()*(40869-199681)19968)).toString(16);c \\u c;cc.replace(/\\/g,%);c unescape(c);c.replace(/%/g,\\) //还存在%的用\代替回去strc;}return str;
}
console.log(getRandomHan(4继续封装继续简化 function getRandomHan(len){var str;for(var i0;ilen;i){cgetRandom(19968,40869).toString(16);c decodeUnicode(c);strc;}return str;
}
console.log(getRandomHan(4))function getRandom(min,max){return Math.floor(Math.random()*(max-min1)min);
};
function decodeUnicode(str){str\\ustr;strstr.replace(/\\/g,%);strunescape(str);strstr.replace(/%/g,\\);return str
}老师课件上的封装
function getRandomHan(len){var str ;for(var i0;ilen; i){var c (Math.floor(Math.random() * (40869 - 19968) 19968)).toString(16);c \\u c;c c.replace(/\\/g,%);c unescape(c);c c.replace(/%/g,\\);str c;}return str;
}
console.log(getRandomHan(6));
// 拆分整合
function getRandom(min,max){ // 获取指定范围内的随机数return Math.floor(Math.random() * (min - max) max);
}
function decodeUnicode(str) { // 解码str \\ustr; //Unicode显示方式是\u4e00str str.replace(/\\/g, %);str unescape(str); //转换中文str str.replace(/%/g, \\); //将其他受影响的转换回原来return str;
}
function getRandomChinese(len){var str ;for(var i 0;ilen;i){str decodeUnicode(getRandom(0x4e00,0x9fa5).toString(16));}return str;
}
console.log(getRandomChinese(2));7-9-3 其他函数
abs(num)// 绝对值、
asin(x)// 反正弦值、
exp(num)// Math.E的num次幂、
atan(x)// 反正切值、
log(num)// num的自然对数、
atan2(y,x)// y/x的反正切值、
pow(num,power)// num的power次幂、
cos(x)// 余弦值、
sqrt(num)//平方根、
sin(x)// 正弦值、
acos(x)// 反余弦值、
tan(x)// 正切值8函数的形参与实参
( 形参 parameter 实参 argument )
8-1 形参
8-1-1 可选参数
可选参数
// 当实参比形参的个数要少时剩下的形参都将设置为undefined值为了让实参与形参保持较好的适应性有必要为可选形参设置一个合理的默认值如function show(name,msg){if(nameundefined) nameaini;if(msgundefined) msg18 years old;console.log(hello,name,message:msg);
}
show();
show(aili);
show(dilnur,22 years old);不用 if 可以使用||也是惯用手法
function show(name,msg){namename || aini;msg msg || 18 years old;console.log(hello,name,message:msg);
}
show();
show(aili);
show(dilnur,22 years old);// 将对象o中可枚举的属性追加到数组a中并返回数组a ,如果省略a则创建一个新数组并返回这个新数组function getProprtyNames(o,/* optional */a){ // 一般可以加个注释来指定一下不定参数if(a undefined) a[];// a a || [];for(var property in o){a.push(property);}return a;
}
var o{name:aini,age:18,sex:nan,friend:dilnur};
var resultgetProprtyNames(o);
console.log(result);
var arr[1,2,3];
console.log(getProprtyNames(o,arr));// 说明当设置可选参数时一定要把可选参数放到形参列表的最后因为不能省略第一个参数定参并传入第二个实参如果第一个参数定参不存在或不使用也需要显式的传入undefined真实的场景是传入一个无意义的占位符nullfunction show(a,b,/* optional*/c,d){}
show(1,2,3,4); //正常使用
show(1,null,2,3); // 使用null占位8-1-2 形参个数
//函数本身的length属性只读它代表函数的形参个数即函数期望传入的实参个数如
// 函数名.length 可以返回传入的形参的个数function show(a,b,/* optional*/c,d){console.log(show.length);
}
show(1,2,3,4);
show(1,null,2,3); 8-2 实参
8-2-1 实参对象arguments
// 传入的实参个数超过函数定义的形参个数时没有办法直接获得超出的实参的值但可以使用实参对象arguments来获取// 在函数体内arguments是指向实参对象有的引用其是一个类数组对象// 函数接收的始终都是这个数组而不关心数组中包含哪些参数// 即使没有传递参数这个数组也存在只不过是包含0个元素的数组function show(a,b){console.log(arguments);
}
show();
show(1,2,3,4); Arguments.length 可以返回实参个数
function sayHi(){console.log(传入了arguments.length个实参);console.log(hello,arguments[0],arguments[1]);
}
sayHi();
sayHi(aini,huan ying ni lai xue xi);可以使用arguments对象来验证所传递的参数是否符合函数要求如
//实际场景中如果对参数个数有要求需要验证
function func(x,y,z){//首先验证传入的个数是否正确if(arguments.length! func.length) throw new Error(需要func.length三个函数); //抛出异常错误;//在判断类型if(typeof arguments[0] ! number) console.log(实参类型是数字);zarguments[2]?y:0; return xyz;
}
console.log(func(1,2,w));// 在某些时候没有必要检查实参的个数因为JS的默认行为是可以满足需要的如省略的实参都是undefined多出的参数自动省略function check(args){var actualargs.length; //实参的真实个数var expectedargs.callee.length; //形参个数if(actual ! expected) throw new Error(参数个数不对);
}
function f(x,y,z){try{check(arguments);return xyz;}catch(e){console.log(e);}
};
console.log(f(1,2,3));
console.log(f(2,3));可以利用arguments对象让函数能够接收任意个数并分别实现适当的功能如
function doAdd(){if(arguments.length1) console.log(arguments[0]);else if(arguments.length2) console.log(arguments[0]arguments[1]);else if(arguments.length3) console.log(arguments[0] arguments[1]arguments[2]);
}
doAdd(10,20);
doAdd(5,10,20);
doAdd(10,35,12,45);// 根据实参的个数进行处理优化代码利用循环
function doAdd(){var sum 0;for(var i0;iarguments.length;i){sumarguments[i];}console.log(sum);
}
doAdd(10,20);
doAdd(1,2,3,4,5,6,7,8,9);继续优化
function doAdd(){var sum 0;for(var i0;iarguments.length;i){sum (arguments[i]?arguments[i]:0);}console.log(sum);
}
doAdd(10,20);
doAdd(1,2,3,4,5,aini,NaN,8,9);根据实参返回最大值
function max(){var maxNumber.NEGATIVE_INFINITY;for(var i0;iarguments.length;i){(arguments[i]max)?(maxarguments[i]):max;}return max;
}
var largestmax(1,45,32,189,245,12,36,985,421,65);
console.log(largest);// 类似于这种可以接收任意个数的实参这种函数也被称为“不定实参函数”这个术语来自C语言// 但真正的不定实参的个数不能为零其应用的场景是该函数包含固定个数的命名和必须的参数以及随后个数不定的可选实参即arguments对象可以与命名参数一起使用function doAdd(num1,num2){var resultparseInt(num1)parseInt(num2);if(arguments.length 2){for(var i2;iarguments.length;i){result parseInt(arguments[i]);}}
return result;
}
console.log(doAdd(20,30,40,50,60,30));arguments并不是真正的数组它是一个对象其数组元素是函数实参的别名arguments的值永远与对应命名参数的值保持同步
function func(num1){console.log(num1); // 10console.log(arguments[0]); //10arguments[0]1;console.log(num1); // 1console.log(arguments[0]) //1 ;
}
func(10);// 注在严格模式下重写arguments的值会失效但不会导致语法错误
// Javascript是一门弱类型语言所以函数中的形参并没有类型声明并且在传入参数前也未做任何类型检查虽然JS可以进行数据类型的自动转换但在某些时候类型不同不会导致语法错误但在程序运行时有可能导致错误函数还是希望得到一个类型明确的参数值此时应当对所传入的参数进行类型的检查如function sum(arr){if(Array.isArray(arr)){var total0;for(var i0;iarr.length;i){var elementarr[i]if(element null) continue;if(isFinite(element)) totalelement;else throw new Error(数组元素必须是有限数);}return total;}else throw new Error(函数的参数必须是数组);
}
console.log(sum([1,2,3,4,5,6]));扩大累加的数据类型
function flexsum(a){var total0;for(var i0;iarguments.length;i){var elementarguments[i],n;if(element null) continue;if(Array.isArray(element)){nflexsum.apply(this.element);}else if(typeof element function){nNumber(element());}else nNumber(element);if(isNaN(n)) throw new Error(无法把element转换为数字);totaln;}
return total;
}
console.log(flexsum(1,2,3));
console.log(flexsum(1,,3));
console.log(flexsum(1,[7,8],3));
console.log(flexsum(function(){return 18;}));没有重载
unction func(){console.log(func函数);
}
function func(num){console.log(年龄为num);
}
function func(name,age){console.log(名字为name年龄为age);}
func();
func(18);
func(aini,18);// 虽然定义了三个一样的函数但是最后定义的把前面定义的两个覆盖掉// ECMAScript函数不能像传统意义上那样实现重载而在其他语言中可以为一个函数编写两个定义只要这两个定义的签名参数的类型和数量不同即可ECMAScript函数没有签名因为其参数是由包含零或多个值的数组来表示的而没有函数签名真正的重载是不可能做到的var addNum function(num){return num100};
var addNum function(num){return num300};函数表达式定义同名的函数时下面的变量就把前面的变量覆盖了所以也不存在重载
8-2-2 模拟重载
可以通过检查传入函数中参数的类型和数量并作出不同的反应可以模拟方法的重载
function overrideFunc(){var arglenarguments.length;if(arglen0) console.log(wu canshu de fangfa);else if(arglen1) console.log(传入的参数为arguments[0]);else if(arglen2) {console.log(传入了2个参数);if(!isNaN(arguments[0] !isNaN(arguments[1]))){console.log(和为(arguments[0]arguments[1]));}else console.log(不是数字:arguments[0]arguments[1]);}else console.log(未知其他处理);
}
overrideFunc();
overrideFunc(零点程序员);
overrideFunc(3,4);
overrideFunc(3,a,33);8-2-3 callee和caller属性
// callee属性指向当前正在执行的函数在某些时候非常有用如在一个匿名函数中使用递归但在严格模式下会抛出错误所以尽量避免使用callee属性可以为函数明确定义一个名称//数的阶乘
function factorial(x){if(x1) return 1;return x* factorial(x-1);
}
console.log(factorial(10));//数的阶乘
function factorial(x){if(x1) return 1;// return x* factorial(x-1);return x*arguments.callee(x-1) //这样就不会出错了
}
var myFactorial factorial;
factorial null;
console.log(myFactorial(10));8-2-4 匿名函数中使用
//数的阶乘
function create(){return function(n){if(n1) return 1;return n*arguments.callee(n-1);};
}
console.log(create()(5));
// 由于内函数没有名字所以只能用arguments.callee来指向他caller是非标准的但大多数浏览器都实现了这个属性它指向调用当前函数的函数在ES5中被移弃了其始终会返回undefined不会报错
8-3 参数的传递方式
// 基本数据类型值传递
// 引用数据类型引用传递地址传递本质上还是按值传递function setname(obj){obj.nameaini;
}
var person new Object();
setname(person);
console.log(person.name); //aini// obj和person内存对应同一个地址function setname(obj){obj.nameaini;obj new Object();obj.name dilnur;console.log(obj.name);
}
var person new Object();
setname(person); //dilnur
console.log(person.name); //aini8-4 函数回调和匿名函数
8-4-1 函数回调
// 回调 将一个函数对象a 传递给另一个函数对象 b让后者在适当的时候执行 a。这就是所谓的“回调”// 适当的时候:当这个外部函数在一定条件下就会调用参数指定的函数此函数就是回调函数。如:function funcA(){console.log(this is func A);
}
function funcB(func){func();
}
funcB(funcA); // this is a func A input typetext idscoreinput typebutton value检测 οnclicktest()brp idmyp/p
script
function test(){var mypdocument.getElementById(myp);var str;var score document.getElementById(score).value;if(score0) str分数不能为负else if(score 0) str此考生没有参加考试else if(score0 score60) str不及格else if(score60 score 70) str及格else if(score70 score90) str良好else if(score90 score100) str优秀else str无效分数;myp.innerHTMLstr;
}也可以这么样写 var str;function fun(score){if(score0) str分数不能为负else if(score 0) str此考生没有参加考试else if(score0 score60) str不及格else if(score60 score 70) str及格else if(score70 score90) str良好else if(score90 score100) str优秀else str无效分数;}
function test(){var mypdocument.getElementById(myp);var score document.getElementById(score).value;fun(score);myp.innerHTMLstr;}继续改进用回调函数
bodyinput typetext idscoreinput typebutton value检测 οnclicktest()brp idmyp/p
scriptvar str;function fun(score,callback){if(score0 || score100) str分数无效else if(score 0) str此考生没有参加考试else callback();}
function test(){var mypdocument.getElementById(myp);var score document.getElementById(score).value;fun(score,function(){ //匿名函数if(score0 score60) str不及格else if(score60 score 70) str及格else if(score70 score90) str良好else if(score90 score100) str优秀});myp.innerHTMLstr;
}数组排序 function sortArr(arr,fun){if(!Array.isArray(arr) || !(fun instanceof Function)){throw new Error(参数类型不准确);}else{for(n in arr){for(m in arr){if(fun(arr[n],arr[m])){var tmparr[n];arr[n]arr[m];arr[m]tmp;}}}}}function compare(num1,num2){return num1num2; }try{var arr[45,12,68,95,115,65,32,25,12,78,35];sortArr(arr,compare);console.log(arr)}catch(e){console.log(e);}8-4-2 匿名函数
// 匿名函数即为没有名字的函数也称为拉姆达函数匿名函数功能强大用途很广// 一般将匿名函数作为参数传入另一个函数或者从一个函数中返回另一个函数这是一种极为有用的技术// 函数也可以作为值来使用也就是说可以将函数赋值给变量、存储在对象的属性或数组元素中也可以当作一个参数进行传递function func(x){return x*x}
var s func;
console.log(func(4));
console.log(s(4));同样可以将函数赋值给对象的属性此时应该称为方法 var o {name:aini,say:function(x){return x*x;}
}
console.log(o.say(5));或者
var o {}
function say(x){return x*x;
};
o.saysay;
console.log(o.say(5));或者
这里用的就是匿名函数
var o {}
o.sayfunction(x){return x*x;}
console.log(o.say(5));赋值给数组元素此时的函数可以不需要名字即是匿名函数
var arr [function(x){return x*x},18];
console.log(arr[0](arr[1]));函数作为参数传递 function myFun(someFun,someArg){return someFun(someArg)}function add(num){return num10;}var result myFun(add,20);console.log(result);还可以这样用
//定义一些简单函数
function add(x,y){return xy};
function subtract(x,y){return x-y};
function multiply(x,y){return x*y};
function devide(x,y){return x/y};
function operate(operator,num1,num2){return operator(num1,num2);
}
var result operate(add,operate(multiply,4,5),operate(add,2,3));
console.log(result);第二种用法
// 使用函数直接量定义在一个对象直接量中
var operators {add: function(x,y){return x y;},subtract: function(x,y){return x - y;},multiply: function(x,y){return x * y;},divide: function(x,y){return x / y;},pow: Math.pow // 使用内置的Math对象的pow方法
};
// operate函数接受一个名字作为运算符在operators对象中查找这个运算符
// 然后将它作用于所提供的操作数
function operate(operation, num1, num2){if(typeof operators[operation] function)return operators[operation](num1, num2);else throw unknown operator;
}
// 调用并计算
var result operate(add, hello, operate(add, , wangwei));
console.log(result); // hello wangwei
result operate(pow, 10,2);
console.log(result); // 100// 当函数作为值的一个典型的应用就是在Array.sort()方法中使用该方法用来对数组元素进行排序因为排序的规则有很多如基于数值大小、字母表顺序、日期大小、从小到大等所以sort()方法接受一个自定义函数作为参数该自定义函数用来处理具体的排序操作其原理是对于任意两个值都返回一个值以指定它们在在排序后的数组中的先后顺序如function compare(x,y){return x - y;
}
var arr [1,88,3,5,12,18,67];
console.log(arr.sort(compare));9数组
// 数组是值的有序集合每个值叫做一个元素而每个元素在数组中都有一个位置以数字表示称为索引或下标// JavaScript数组是无类型的数组元素可以是任意类型即使同一个数组内不同的元素也可以有不同的类型
// JS数组的索引是基于零的32位整数第一个元素的索引为0
// ES数组是动态的即它的大小是可以动态调整的可以随着数据的添加与删除会自动增长或缩减9-1 创建数组
9-1-1 第一种
使用构造函数Array()创建
var arr new Array(); // 空数组
var arr new Array(3); // 指定长度
var arr new Array(wangwei,wujing,jingguo); //显示指定元素// 1.如果预先知道数组要保存的项目数量可以为构造函数传递该数量这个数量实际上就是该数组length属性实际上是预分配了一个数组空间但数组中没有存储值甚至数组的索引都未定义
// 2.直接传递项即参数就是数组的元素
// 3.如果需要创建只有一个值的数组并且这个值是数字只能把该数字使用字符串的形式当这一个值如果是非整数时会抛出异常var arr new Array(18.5); //抛出异常也可以省略new操作符实际上是进行了数据类型转换如
var arr Array(); // 空数组
console.log(arr);
var colors Array(3); // 指定长度
console.log(colors);
var names Array(wangwei,wujing,jingguo); //显示指定元素
console.log(names);9-1-2 第二种
使用数组字面量直接量创建
var empty [];
var num [2,3,4,5];// 这是创建数组最简单的方法即使用方括号[ ]将数组元素用逗号分隔开
// 当为[ ]空数组时与new Array()等价
// 数组直接量中的值不一定是常量也可以是任意的表达式var year 2022;
var years [year,year1,year2];
console.log(years);还可以包含对象或其他数组
var objArr [{name:wangwei,age:18},[1,2],[3,{x:4,y:5}]];如果省略数组中某个值省略的元素的值都是undefined值
var count [1,,3];
console.log(count[1]); // undefined;允许有可选的结尾逗号但会省略最后的元素
count [1,2,]; // 2
console.log(count);
count [,,,]; // 3
console.log(count);// 不建议使用某些浏览器会解析为3项数组因为它们对数组的实现不一致
// 总结在实际场景中使用数组字面量要比构造函数简单、灵活9-2 数组的内存分布
// 数组在内存中用一串连续的区域来存放一些值在 JavaScript 中数组是哈希映射在内存中不是连续的它可以通过多种数据结构实现其中一种是链表因此如果使用大数组需要的时间很长// 在ES6中引入了类型化数组JavaScript 引擎已经在为同种数据类型的数组分配连续的存储空间了如ArrayBuffer其使用的就是一块连续的内存空间9-3 数组的读写
要读取或设置数组元素的值时使用“[ ]”运算符并提供相应的基于0的数字索引下标
var citys new Array(蚌埠,宿州,南京);
var c1 citys[0]; // 读取第一个元素
citys[0] 怀远; // 写第一个元素在越界访问不存在的数据元素时会返回undefined
如果设置某个值的索引超过了数组现有的项数数组会自动增加到该索引值加1的长度 var colors[red,blue];
colors[2]green; // 明确添加了一个新元素
var i 3;
colors[i] purple;
colors[i1] yellow;注意跳过某个索引添加值时跳过的自动变成empty
var colors[red,blue];
colors[3] purple;
console.log(colors);
console.log(colors.length); // 4// 数组的最大索引为4294967294(2的32次方-2)// 数组是对象的特殊形式使用方括号访问数组元素就像用方括号访问对象的属性一样
// 其会将指定的数字索引值转换成字符串并将其作为属性名来使用var n [];
n[0] one; n[“0”]”one”
n[1] two; n[“1”]”two” 等同
console.log(n[1]);
var p {};
p[0] one; // 将0转换为”0”
p[1] two;
console.log(p[1]);
var o {0:wangwei,1:wujing,2:huiyuan};
o[1] lili;
console.log(o[1]); // lili// 所有的数组都是对象所以可为其创建任意名字的属性此时这个任意名字如果不是非负整数它就只能当作常规的对象属性而非数组的索引var arr [1,2];
arr[-2] -2的值;
arr[name] aini;
console.log(arr);// 如果凑巧使用了是非负整数的字符串它就当作数组索引而非对象属性当使用一个浮点数和一个整数相等时情况也是一样的var arr [1,2];
arr[2] 333; 把二当做数组的索引因为字符串2能转换成数字var arr [1,2];
arr[-1.23] true; // 创建了一个名为“-1.23”的属性
arr[1000] 18; // 数组的第1001个元素
arr[1.000] 10; // 等同 arr[1]
console.log(arr)// 事实上数组索引仅仅是对象属性名的一个特殊类型这意味着ES数组没有“越界”错误的概念即试图查询任何对象中不存在的属性时不会报错只是得到undefined值 // 所有的索引都是属性名但只有在2的32次方之内的整数属性名才是索引超出的索引只能是属性名所以超出4294967295的都属属性数组索引最大长度为4294967295arr[4294967294] a;
arr[4294967294] a;
console.log(arr);
console.log(arr.length);
arr[4294967295] b;
console.log(arr);
console.log(arr.length);
arr[4294967296] c;
console.log(arr);
console.log(arr.length);通常数组的实现是经过优化的用数字索引来访问数组元素一般来说比访问常规的对象属性要快很多所以尽量使用数组传递数据
9-4 length 属性
数组对象的length长度属性返回或指定数组元素的个数
var colors[red,blue,green,skey,gray];
console.log(colors.length);
colors.length 3;
console.log(colors.length);
console.log(colors);// 说明即多于length的元素被删除了var names[wangwei];
names.length 4;
console.log(names); // 添加了三个空的向// length属性设置为大于数组项数的值则新增的每一项都会取得undefined值从本质上讲并不会向数组中添加新的元素它只是在数组尾部创建一个空的区域其实就是后面要讲的稀疏数组利用length属性可以方便的在数组末尾添加新项
var colors[red,blue,green];
colors[colors.length]black;
colors[colors.length]brown;
console.log(colors);// 数组的最大索引为4294967294(2的32次方-2)即可以包含4292967295个元素当索引小于此值并且不为非负时数组会自动更新其length属性超出这个范围length不会更新在ES中可以用Object.defineProperty()让数组的length属性变成只读的
9-5 稀疏数组
Sparse arrays 稀疏数组ES中的数组是稀疏的即数组的元素的索引不一定要连续它们之间可以有空缺
var arr new Array(5); // 数组没有元素但length是5
arr []; // 空数组length为0
arr [1,,4]; //
arr[1000] 0; // 赋值添加了一个元素length为1001
arr [1,2,3,4,5];
delete arr[2]; // 使用delete也能生成稀疏数组
console.log(arr);如果数组是稀疏数组length值大于元素的个数否则该属性就是数组元素的个数即稀疏数组的length长度与个数是不一致的
遍历时会跳过这些空隙
var a1 [1,,,4];
for(i in a1)console.log(i);循环遍历时都能打印出来空数组就打印undefined
9-6 稠密密集数组
与稀疏相对应则为密集数组即元素中不存在空隙其实密集数组基本就是平时常见的正常数组
var spare new Array(3);
var dense Array.apply(null,spare); 可以产生值为undefined
console.log(spare); 的密集数组跟稀疏数组
console.log(dense); 有区别
console.log(spare[0]);
console.log(dense[0]);区别
var arr new Array(3);
arr[2] aini;
for (index in arr){console.log(index,arr[index]); // 2 aini
}var dense Array.apply(null,Array(3));
dense[2] dilnur;
for (index in dense){console.log(index,dense[index]); //都能打印
}// 说明稀疏数字里面的undefined和密集数组里面的undefined是两回事儿// 密集数组
console.time(one);
Array.apply(null,Array(1e5)).forEach(function(){});
console.timeEnd(one);
// 稀疏数组
console.time(two);
Array(1e5).forEach(function(){});
console.timeEnd(two);
// one: 8.589111328125ms
// two: 2.122314453125ms// 稀疏数组速度比密集数组处理速度快// 在实际应用中绝大部分的es数组都不是稀疏数组如果使用了稀疏数组可以像处理非稀疏数组一样处理只不过它们包含一些undefined值// 稀疏的数组通常在实现上比稠密的数组更慢、内存利用率更高在这样的数组中查找元素的时间与常规元素属性的查找时间一样长另外在通常的情况下我们想要直接声明一个数组并赋予其一些特定的初始值并且为了避免问题通常是希望申明为密集数组的
压缩稀疏数组可以通过filter()方法压缩其中的空隙因为filter会跳过空隙返回密集的数组 var a1 [1,,,4];
var a2 a1.filter(function(x){return true;});
for(i in a2)console.log(i);9-7 数组方法
ES在Array.prototype中定义了很多操作数组的方法
console.log(Array.prototype);9-7-1 数组转字符串
1-1 toString()valueOf()
所有对象都具有toLocaleString()、toString()及valueOf()方法
1valueOf() // 返回的是数组本身
2toString() // 方法将数组表示为字符串各个元素按顺序排列组合以逗号分隔的字符串返回。(通过对每项调用toString()方法,然后用逗号把它们连接在一起构成的)var arr [1,2,3,5];
console.log(arr);
console.log(arr.valueOf());
console.log(arr.toString());var colors[red,blue,[green,black]];
console.log(colors.valueOf()); // [red, blue, Array(2)]
console.log(colors.toString()); // red,blue,green,blank
console.log(colors); // [red, blue, Array(2)]// 以上console.log()换成alert由于alert要接收字符串参数所以它会在后台调用toString()由此会得到与直接调用 toString()方法相同的结果// 这里的toString()方法与不使用参数的join()方法返回的字符串是一样的// toLocaleString()是toString()方法的本地化版本一般情况下会返回与toString()和valueOf()方法相同的值但也不总是如此其会使用本地化的分隔符当调用数组的toLocaleString()方法时后台会为每一项调用 toLocaleString()方法而不是tostring()方法var p1 {toLocaleString:function(){return aini},toString:function(){return ai}
}
p2 {toLocaleString:function(){return dilnur},toString:function(){return norah}
}var person [p1,p2];
console.log(person);
console.log(person.toString());
console.log(person.toLocaleString());1-2 join()
2join()方法
// 将数组中所有元素使用不同的分隔符转化为字符串分隔符号由用户指定var colors [red,green,blue,balck];
console.log(colors.join());
console.log(colors.join(,));
console.log(colors.join(-));
console.log(colors.join(|));
console.log(colors.join( ));
var arrStr new Array(10);
console.log(arrStr.join(-));如果不指定分隔符或者传入undefined则使用默认的逗号分隔
var colors [red,green,blue,balck];
console.log(colors.join(undefined));
console.log(colors.join(undefined));
console.log(colors.join(null));
console.log(colors.join(null));// 注”undefined”,”null”是字符串如果直接是undefined则会用逗号分割null的话转换成”null”,再用”null”分割// join()方法是split()方法的逆向操作后者是将字符串分割成若干块来创建一个数组。// 注如果数组中的某一项的值是null或者undefined那么该值在以上所有方法中返回空字符串。字符串split()方法把字符串转换为数组
var str red,blue,green; // 字符串
var aStrstr.split(,); // 数组
var aStrstr.split(); // 单个字符数组关于字符串split方法的几个注意点
// 1如果指定错误的分隔符则把整个字符串当做数组的一个 元素返回var str red,blue,green;
var aStrstr.split(-);
console.log(aStr);// 2如果什么也不传就会把字符串里面的每一个字符拿出来当做数组的一个元素var str red,blue,green;
var aStrstr.split();
console.log(aStr);// 3如果把空字符串声明为分隔符,split会返回字符串的每个字条符;一般用于需要逐个处理字符.9-7-2 队列方法
//1栈和队列是一种限制了插入和删除数据项操作的数据结构
//2栈数据结构的访问规则是LIFO先进后出
//3而队列数据结构的访问规则是FIFOFirst-In-First-Out先进先出
//4栈最近添加的项是最先删除的项栈中的插入和删除操作都只发生在一个位置,即栈顶部;把一个项添加到栈中,叫做推入栈从栈中删除一项,叫做从栈中弹出;
//5队列在列表的末端添加项从列表的前端移除项可以使用push()方法向数组末尾添加项使用shift()方法从数组前端移除项这两个方法模拟队列2-1 push()
在数组尾部添加一个或多个元素并返回更新后数组长度会修改原数组
var colors new Array();
var count colors.push(red,green);
console.log(colors);
console.log(count); // 2
count colors.push(black,blue);
console.log(colors);
console.log(count); //4// push()方法实际上同手工添加数据一样.
// 注pop()不带值也不会报错只返回数组长度2-2 pop()
与push()方法相反其会删除数组末尾的一个元素并将其返回会修改原数组
var colorsnew Array();
colors.push(red,green,blue);
var item colors.pop();
console.log(item); // blue
console.log(colors);2-3 unshift()
// (1) 在数组顶端插入元素一次可以插入单个或多个元素所有元素按顺序插入操作完成后返回新数组的长度会修改原数组其与push()类似
// 2unshift()方法将引发所有下标的改动
// 3在一次性插入多个参数时插入的元素的顺序和它们在参数列表中的顺序一致
// 4如果不计较元素插入的位置则推荐使用push方法因为unshift()方法可能会影响依靠下标才能准确进行的计算。var colors new Array();
colors.push(red,green,blue);
var newLen colors.unshift(black,gray);
newLen colors.unshift(r,[g,b]);
console.log(newLen);
console.log(colors);2-4 shift()
// shift()移除数组的第一个元素并将其返回该方法执行后数组剩下的元素向前移动下标索引号重新调整从0开始按顺序赋予所有元素会修改原数据其与pop方法类似var colors new Array();
colors.push(red,green,blue);
var shiftItem colors.shift();
console.log(shiftItem);
var item colors.shift();
console.log(item);
console.log(colors);调用push()方法和shift(),可以使Array对象具有队列一样的行为先进先出
var colorsnew Array();
var count colors.push(red,green,blue);
count colors.push(black);
var item colors.shift();
console.log(colors);使用unshift()和pop()方法可以从相反的方向来模拟队列即在数组的前端添加项在末端移除项
var colorsnew Array();
var count colors.push(red,green,blue);
count colors.unshift(black);
var item colors.pop();
console.log(colors);9-7-3 数组排序
排序算法选择排序和冒泡排序
3-1 选择排序
var arr [78,15,69,3,45,78,95,62,35,98,789,125,98,5,58];
for (var i0; iarr.length;i){var element for(var ji1; jarr.length;j){if(arr[j]arr[i]){element arr[i];arr[i] arr[j];arr[j]element;}}
};
console.log(arr);3-2 冒泡排序
var arr [78,15,69,3,45,74,785,125,654,12,35,64,05,45,78,126];
function compare(num1,num2){return num1num2?true:false;
}
function sort(arr){var len arr.length;for (var i0; ilen;i){var element for(var j0; jlen-i;j){if(compare(arr[j],arr[j1])){element arr[j];arr[j] arr[j1];arr[j1]element;}}
}return arr;
};
console.log(sort(arr));3-3 reverse()
reverse()方法将一个Array对象中所有元素的次序反转会修改原数组
var arr [aini,dilnur,xinjiang,shanghai];
console.log(arr.reverse());3-4 sort()
sort()方法
1sort()方法// 对数组元素进行排序后并返回默认将一个数组中的所有元素进行升序排序会改变原数组
2// 如果数组元素中undefined或null它们会被排列在数组的尾部var values [0,1,5,10,15];
values.sort();
console.log(values);
var arr new Array(banana,undefined,cherry,null,apple); 按照Unicode编码大小
arr.sort(); 进行排序
console.log(arr);实际上sort()方法是把元素转换成字符串进行排序的
// 1sort()方法可以接收一个比较函数以确定一个排序的规则
// 2比较函数接收两个参数如果第一个参数位于第二个之前则返回一个负数
// 3如果两个参数相等则返回0
// 4如果第一个参数位于第二个之后则返回一个正数function compare(value1,value2){if(value1value2){return -1;}else if(value1value2){return 1;}else{return 0;}
}
var values [0,1,5,10,15];
values.sort(compare);// 对于数值类型或者valueOf()方法会返回数值类型的对象类型可以使用一个更简单的比较函数这个函数只要用两个值互减即可function compare(value1,value2){return value1 - value2;
}注参数必须是数字才能这样用
// 说明由于该函数通过返回一个小于零、等于零或大于零的值来影响排序结果因此减法操作就可以适当地处理所有这些情况。3-5 随机排序
var arr [1,2,3,4,5,6,8,9];
var randomArr arr.sort(function(){return Math.random()-0.5;
}
);
console.log(arr);对于字符串排序主要是大小写的问题
var str [dilnur,AINI,aini,DILNUR];
console.log(str.sort());
str.sort(function(s,t){var s s.toLowerCase();var t t.toLowerCase();if(s t) return -1;if(s t) return 1;return 0;
});
console.log(str);另外有时需要按字符串长度进行排序此时还应该同时判断字符是否为双节字
var arr [阿卜杜艾尼a,aini-艾尼,dilnur_地,aini艾尼,dilnur地理];
function getDword(str){var len str.length;for(var i0;ilen;i){if(str.charCodeAt(i)255){len;}}return len;
}
arr.sort(function(s1,s2){return getDword(s1)-getDword(s2);
})
console.log(arr);// 汉字的长度就按双字节算3-6 对象排序
比较的主要是对象的valueof()值
var arr [o,{valueOf:function(){return 15}},{valueOf:function(){return 18}},{valueOf:function(){return 4}}
];
arr.sort(function(o1,o2){return o1 - o2});
console.log(arr);
for(var i in arr)console.log(arr[i].valueOf());或者按指定的对象属性进行排序
var wang {name:wangwei,sex:男,age:18};
var wu {name:wujing,sex:女,age:28};
var guo {name:jianguo,sex:男,age: 25};
var arr [wang,wu,guo];
var pArr arr.sort(function(p1,p2){return p1.age - p2.age;
});
console.log(pArr);9-7-4 数组操作
4-1 concat()
concat()方法:
// 1基于当前数组将多个元素连接一起成为新的数组并返回
// 2新数组中的元素按连接时的顺序排列// 具体来说这个方法会先创建当前数组一个副本然后将接收到的参数添加到这个副本的末尾最后返回新构建的数组。当需要合并多个数组时此方法比较方便var arr [1,2];
arr1 arr.concat();
arr2 arr.concat(3,4);
arr3 arr.concat([3,4,],[aini]); // 3,4,aini拿出来放到源数组里面 扁平化
arr4 arr.concat([3,4,[5,6,7]]);
console.log(arr);
console.log(arr1);
console.log(arr2);
console.log(arr3);
console.log(arr4);// 3concat()不会递归扁平化数组的数组也不会修改原来的数组
// 4当没有给concat()方法传递参数的情况下它只是复制当前数组并返回副本4-2 slice()
2slice()方法:
// 1能够基于当前数组中的一个或多个项创建一个新数组
// 2其接受一或两个参数即要返回项的起始和结束位置
// 3在只有start一个参数的情况下slice()方法返回从该位置开始到当前数组末尾的所有项
// 4如果两个参数该方法返回起始和结束位置之间的项但不包括结束位置的项
// 5其不会影响原有数组var colors [red,green,blue,yellow,purple];
var a colors.slice(2);
var b colors.slice(1,4);
console.log(a);
console.log(b);
var c colors.slice(1,-2); //从索引1 取到 倒数第二个
var d colors.slice(-4,-2); // 倒四 取到 倒二
console.log(c);
console.log(d);只能从左往右截像colors.slice(3,1) 就会返回空数组
4-3 splice()
3splice()方法:改变原数组
// 1可以删除、替换或插入数组元素是最强大的数组方法有很多种用法
// 2具体作用是从一个数组中移除一个或多个元素剩下的元素组成一个数组移除的元素组成另一个新数组并返回
// 3同时原数组可以在移除的开始位置处顺带插入一个或多个新元素达到修改替换数组元素的目的start// 必选项表示从数组中剪切的起始位置下标索引号。deleteCount// 必选项表示将从数组中切取的元素的个数。item可选项// 表示切取时插入原数组切入点开始处的一个或多个元素;3-1 删除
var colors [red,green,blue,yellow,purple];
var a colors.splice(1,3);
console.log(colors); // 返回原数组删除某些元素以后的数组
console.log(a); // 返回删除的项1删除// 可以删除任意数量的项只需要指定2个参数如splice(0,2);// 删除数组中前两项如果只指定1个参数则删除从该位置到结尾的元素
2插入// 可以向指定位置插入任意数量的项需要指定3个参数其中第二个参数为0如果要插入多个项可再传入第4第5等任意多个项如splice(2,0,”red”,”green”);
3替换// 可以向指定位置删除任意数量的项同时插入任意数量的项需要指定至少3个参数插入的项数不必与删除的项数相等如splice(2,1,”red”,”green”);splice()方法始终会返回一个数组该数组中包含从原始数组中删除的项如果没有删除任何项则返回一个空数组
3-2 插入
没必要把返回值赋给新变量
var colors [red,green,blue,yellow,purple];
var a colors.splice(1,0,grey,skey);
console.log(colors); // 返回插入以后的新的数组
console.log(a); // 返回空数组3-3 替换
替换的数量跟删除的数量不一定一致
var colors [red,green,blue,yellow,purple];
var a colors.splice(1,2,grey,skey);
console.log(colors); // 返回替换以后的新的数组
console.log(a); // 返回数组删除的项注splice会改变源数组但是splice本身只返回删除的项
var colors [red,green,blue,yellow,purple];
var a colors.splice(1,2,[wangwei],[aini]);
console.log(colors);
console.log(a); 不会扁平化区别于concat()方法
第一个参数可以接受负数如果为负数则指定的位置是数组长度加上该负值或者从后向前定位
var colors [red,green,blue,yellow,purple];
var a colors.splice(-3,2,aini,dilnur);
console.log(colors);
console.log(a);// (倒数第三个位置往后数两个)
// 在实际的场景中主要还是向数组的中部插入项
// delete运算符删除数组元素
// 通常使用delete运算符删除一个指定的元素与删除对象属性一样var colors[red,green,blue,yellow,purple];
console.log(colors.length);
delete colors[1];
console.log(colors);
console.log(colors.length);// delete删除不会修改数组的length属性如果从数组中删除一个元素该数组就变成稀疏数组
// 此删除并没有真正删除数组元素仅删除元素内容此删除与为其赋undefined值是类似的但有一些微妙的区别
// 删除数组元素有多种方式如设置length属性小于数据长度会删除数组尾部的元素9-7-5 数组位置操作
5-1 ndexOf()lastIndexOf()
// 1.两者会搜索整个数组中具有给定值的元素并返回要查找的元素在数组中的第一个索引位置或者在没找到返回-1
// 2.这两个方法都是接收两个参数要查找的项和可选的表示查找起点位置的索引其中indexOf()方法从数组的开头位置为0开始向后查找lastIndexOf方向则从数组的末尾开始向前查找。// 注在查找时使用是全等操作符var numbers [1,2,3,4,5,4,3,2,1];
console.log(numbers.indexOf(4)); // 3
console.log(numbers.lastIndexOf(4)); // 5
console.log(numbers.indexOf(4,4)); // 5 从索引4位置往后找“4”这个元素
console.log(numbers.lastIndexOf(4,4)) // 3 从索引4位置往前找|“4”这个元素var p {name:wangwei};
var p1 p;
var people [{name:wangwei},{name:aini}];
var people [p1,{name:aini}];
var morePeople [p];
console.log(people.indexOf(p)); // 0
console.log(morePeople.indexOf(p)); // 0// 4.第二个参数也可以是负数即为数组长度加上该负数的值为查找的索引开始位置或者从后往前定位var numbers [1,2,3,4,5,4,3,2,1];
console.log(numbers.indexOf(4,-5)); // 5封装一个函数查找所有给定值的索引并返回一个包含匹配索引的数组
var numbers [1,2,3,4,5,4,3,2,1,4,2,7,9,4,2,3,5];
function findIndex(arr,index){var len arr.length;var result [];var pos 0;while(poslen){pos arr.indexOf(index,pos);if(pos -1) break;result.push(pos);pos;}
return result;
}
console.log(findIndex(numbers,4));字符串也有indexOf()和lastIndexOf()方法它们和数组的方法功能类似
对于数组这些方法也可以重写本质上就是修改其prototype对象的属性方法如
var arr [1,2,3];
arr.push(4,5);
console.log(arr);
Array.prototype.push function(){for(var i0; iarguments.length; i)this[this.length] arguments[i] * arguments[i];
}
arr.push(6,7);
console.log(arr);9-7-6 遍历数组
// 所谓的数组的遍历就是通过索引挨个取出数组元素遍历目的是为了读取所有元素或者处理所有元素使用for循环是遍历数组最常见的方法var cities [beijing,nanjing,bengbu];
for(var i0;icities.length;i) console.log(cities[i]);
for(var a in cities) console.log(a,cities[a]);
var o {name:aini,age:18,sex:true,hobby:reading};
var keys Object.keys(o); // 把对象的属性放到数组里返回
console.log(keys);
var values [];
for(var i0,lenkeys.length;ilen;i){// values[i]o[keys[i]]values.push(o[keys[i]]);
}
console.log(values);// 在遍历稀疏数组或者需要排除null、undefined和不存在的元素时需要先检测var arr [1,,3,null,5,undefined,7];
for(var i0,lenarr.length;ilen;i){console.log(arr[i]); // 可以把所有元素都打印出来
}
console.log(----------------);
for(var i in arr){console.log(arr[i]); //会过滤掉empty元素
};
console.log(----------------);
for(var i in arr){if(!arr[i]) continue; //过滤掉null和undefinedconsole.log(arr[i]);
};
console.log(----------------);
for(var i in arr){if(arr[i] undefined) continue; //过滤掉undefinedconsole.log(arr[i]);
};
console.log(----------------);
for(var i in arr){if(arr[i] null) continue; //过滤掉nullconsole.log(arr[i]);
};
console.log(----------------);
for(var i0,lenarr.length;ilen;i){if(!(i in arr)){console.log(arr[i]); // 可以用for循环过滤空元素}
} ;// 注空元素的索引用in操作符判断时返回false用for循环时可以用来过滤掉稀疏数组里的空元素// 但是for/in能够枚举继承的属性名如添加到Array.prototype中的方法由于这个原因在数组上不应该使用for/in循环除非使用额外的检测方法来过滤不用的属性如Array.prototype.company zeronetwork;
var arr [1,,3,null,undefined,6];
arr.custom wangwei;
arr[-18.1]18;
for(var i 0;iarr.length;i){console.log(i,arr[i]);
} //for循环只遍历元素//用 for in把所有可枚举的属性枚举出来
for(var i in arr){console.log(i,arr[i]);
} //custom ,company,-18.1都打印出来
//hasOwnProperty()方法过滤
for(var i in arr){if(!arr.hasOwnProperty(i)){console.log(i,arr[i]); //只有 company zeronetwork;}
};
//跳过不是非负整数的i;
for(var i in arr){if(String(Math.floor(Math.abs(Number(i))))! i) continue;console.log(i,arr[i]);
}// 1ES允许for/in循环以不同的顺序遍历对象的属性
// 2通常数组元素的遍历实现是升序的但不能保证一定是这样的特别地如果数组同时拥有对象属性和数组元素返回的属性名很可能是按照创建的顺序而非数值的大小顺序
// 3如何处理这个问题的实现各不相同如果算法依赖于遍历的顺序那么最好不要使用for/in而使用常规的for循环。9-7-7 遍历数组的迭代方法 (1// ECMAScript5为数组定义了5个迭代方法
2// 这些方法的第一个参数接收一个函数并且对数组的每个元素调用一次该函数
3// 第二个参数是可选的其是运行该函数的作用域对象影响this的值
4// 或者说调用的函数被当作是第二个参数的方法
5// 如果是稀疏数组对不存在的元素不调用传递的函数
6// 这些方法不会修改原始数组
7// 参数函数使用三个参数数组元素、元素索引和数组对象本身通常只需要第一个参数可以忽略后两个参数
7// 参数函数可以修改原始数组
8// 根据使用的方法不同这个函数执行后的返回值可能也会不同7-1 forEach()
遍历数组为每个元素调用指定的函数该函数使用三个参数数组元素、元素索引和数组本身此方法没有返回值本质上与使用for循环迭代数组一样
var numbers [1,2,3,4,5,4,3,2,1];
numbers.forEach(function(item,index,array){console.log(item,index,array[index]);
});第一个参数// 数组元素
第二个参数// 数组索引
第三个元素// 数组对象本身// 为每个元素加1
var data [1,2,3,4];
data.forEach(function(v,i,a){a[i] v 1; //会改变原数组元素
});
console.log(data);如果只关心数组元素的值可以只使用一个参数额外的参数将被忽略
// 求和
var data [1,2,3,4];
var sum 0;
data.forEach(function(value){ //第一个参数数组元素相当于遍历多有元素sum value; //不会改变原数组元素
});
console.log(sum);1// forEach()方法无法在所有元素都被传递给调用的函数之前终止遍历即没有像for循环中使用的break语句
2// 如果要提前终止必须把forEach()方法放在try块中并能抛出一个异常function foreach(a,f,t){try{a.forEach(f, t);}catch(e){if(e foreach.break) return;else throw e;}
}
foreach.break new Error(StopIteration);7-2 every()some(
1// 最相似的是every()和some()它们都是数组的逻辑判定用于判定数组元素是否满足某个条件
2// 对every()传入的函数必须对每一项都返回true这个方法才返回true
3// 而some()是只要传入的函数对数组中的某一项返回true就会返回true否则返回falsevar numbers [1,2,3,4,5,4,3,2,1];
var everyResult numbers.every(function(item,index,array){return (item 2);
});
console.log(everyResult); // false
var someResult numbers.some(function(item,index,array){return (item 2);
});
console.log(someResult); // true4// 一旦every()和some()确定该返回什么值时它们就会停止遍历数组元素
5// some()在判定函数第一次返回true后就返回true但如果判定函数一直返回false它将会遍历整个数组every()恰好相反它在判定函数第一次返回false后就返回false但如果判定函数一直返回true它将会遍历整个数组。
6// 根据数学上的惯例在空数组上调用时every()返回truesome()返回false7-3 filter()方法
1// 返回的是数组元素是调用的数组的一个子集
2// 回调函数是用来逻辑判定的该函数返回true或false如果返回的是true或真值则该函数处理的数组元素就被添加到返回的子集数组中
(3// filter()方法会跳过稀疏数组中缺少的元素它的返回数组总是密集的var numbers [1,2,3,4,5,4,3,2,1];
var filterResult numbers.filter(function(item,index,array){return item2;
});
console.log(filterResult);
var evenResult numbers.filter(function(value,index){return index % 2 0; // [1, 3, 5, 3, 1] index是索引
});
console.log(evenResult);
// 压缩稀疏数组
var sparse [1,,,4];
var dense sparse.filter(function(){return true; //过滤稀疏元素
});
console.log(dense);
// 压缩并删除undefined和null元素
var sparse [1,null,3,undefined,,6];
var dense sparse.filter(function(v){return v ! undefined v ! null;
});
console.log(dense);注该方法返回的是符合条件的数组元素
7-4 map()
1// 将调用的数组的每个元素传递给回调函数并将调用的结果组成一个新数组返回
2// 其不会修改原始数组如果是稀疏数组返回的也是相同方式的稀疏数组即具有相同的长度、相同的缺失元素
var numbers [1,2,3,4,5,4,3,2,1];
var mapResult numbers.map(function(item,index,array){return item*2;
});
console.log(mapResult);
var a [1,null,3,undefined,5];
var b a.map(function(v){return v * v;
});
console.log(b);不做任何处理
var spare [1,,4,null,undefined,NaN,6];
var dense spare.map(function(v,i,a){});
console.log(dense);返回所有数组元素
var dense spare.map(function(v,i,a){return v;
});
console.log(dense);能处理的元素进项处理空元素返回空元素
var spare [1,,4,null,undefined,NaN,6];
var dense spare.map(function(v,i,a){return v*v; //进行数据类型转换由于null和undefined不能进行数据类型转换});
console.log(dense) //所以都返回NaN7-5 reduce()和reduceRight()
1// reduce()和reduceRight()这两个方法都会迭代数组的所有项然后构建一个最终返回的值
2// 其中reduce()方法从数组的第一项开始逐个遍历到最后而reduceRight则从数组的最后一项开始向前遍历到第一项
3// 这两个方法都接收两个参数调用的函数callbackfn和作为归并基础的初始值initialValue可选的
4// 这个函数接收4个参数前一个值、当前值、项的索引和数组对象其返回的任何值都会作为第一个参数自动传给下一项第一次迭代发生在数组的第二项上因此第一个参数是数组的第一项第二个参数就是数组的第二项使用reduce()方法可以执行求数组中所有值之和的操作如
// 求和
var values [1,2,3,4];
var sum values.reduce(function(prev,cur,index,array){return prev cur;
});
console.log(sum); // 10reduce()参数函数的参数也可以只使用两个参数如
// 求和
var values [1,2,3,4];
var sum values.reduce(function(prev,cur){return prev cur;
});
console.log(sum); // 10
// 求积
var product values.reduce(function(prev,cur){return prev * cur;
});
console.log(product); // 24
// 求最大值
var max values.reduce(function(prev,cur){return prev cur ? prev : cur;
});
console.log(max); // 4reduce()方法的第二个参数initialValue是回调函数的第一个参数previousValue的初始值如
// 把以上所有示例添加第二个参数如
var values [1,2,3,4];
var sum values.reduce(function(prev,cur){return prev cur;
},2); // 12意思就是pre不是第一项而是给pre赋一个值cur从第一项开始迭代 var values [1,2,3,4,5];
var sum values.reduceRight(function(prev,cur,index,array){console.log(prev,cur);return prev cur; //21
},6); //pre的初始值为6
console.log(sum); // 15
// 求2^(3^4)乘方操作的优先顺序是从右到左
var a [2,3,4];
var big a.reduceRight(function(accu, value){console.log(value);return Math.pow(value, accu);
});
console.log(big);// 在空数组上不带初始值参数的reduce()将导致类型异常如果数组只有一个元素并且没有指定初始值或者一个空数组并且指定了一个初始值该方法只是简单的返回那个值而不会调用回调函数var a [];
// var b1 a.reduce(function(x,y){return x y}); //no initial value 会报错
// var b2 a.reduce(function(x,y){return x x}); //也会报错
var b3 a.reduce(function(x,y){return x y},3); //只返回初始值console.log(b3); // 31// 使用reduce()还是reduceRight()主要取决于要从哪头开始遍历数组除此之外它们完全相同。
2// reduce()和reduceRight()是典型的函数式编程有些地方会把其回调函数称为化简函数这个化简函数的作用就是用某种方法把两个值组合或化简为一个值并返回化简后的值如以上的示例化简函数通过各种方法组合了两个值9-8 检测数组及类数组
9-8-1 检测数组
1.// 数组是具有自己特殊行为的对象因此确定某个对象是不是数组就很重要
2.// typeof只能检测其是object的类型而不能确定是否为Array类型
3// 对于一个页面或者一个全局作用域而言可以使用instanceof操作就可以得到检测结果如var arr [];
console.log(arr instanceof Array);
console.log(arr instanceof Object);
console.log(arr.constructor);
console.log(arr.constructor Array);注实际上就是判断它的原型
4. // 但instanceof与constructor有问题如果网页中包含多个框架那实际上就存在两个以上不同的全局执行环境分别具有自己的全局对象每个全局对象都有自己的Array构造函数因此一个框架窗口中的对象不可能是另一个框架窗口的实例
5.// 比如如果从一个框架向另一个框架传入一个数组那么传入的数组与在第二个框架中创建的数组分别具有各自不同的构造函数它们就不是同一类对象var frame document.createElement(iframe);
document.body.appendChild(frame);
var FrameArray window.frames[0].Array; // 获取框架全局环境中的Array构造函数
var fa new FrameArray(1,2,3); // 在框架全局环境中创建数组
console.log(fa instanceof Array); // false在当前环境中判断fa是不是数组
console.log(Array.isArray(fa)); // true
console.log(fa.constructor Array); // false6. // 解决方案使用Object.prototype上的原生toString()方法判断如
Object.prototype.toString.call(arr)
7.// 该方法返回一个[object NativeConstructorName]格式的字符串但该方法不能检测非原生构造函数的函数名因此自定义的构造函数都将返回[object Object]如var frame document.createElement(iframe);
document.body.appendChild(frame);
var FrameArray window.frames[0].Array; // 获取框架全局环境中的Array构造函数
var fa new FrameArray(1,2,3); // 在框架全局环境中创建数组
console.log(fa instanceof Array); // false在当前环境中判断fa是不是数组
console.log(Array.isArray(fa)); // true
console.log(fa.constructor Array); // false
console.log(Object.prototype.toString.call(fa));
console.log(Object.prototype.toString.call(fa) [object Array]);var d new Date();
console.log(Object.prototype.toString.call(d));console.log(Object.prototype.toString.call(p));// 所以说如果不是系统自己定义的而是我们自己定义的独享无法用
Object.prototype.toString.call()这个方法来判断因为都会返回[object object];bodyiframe srca.html namemyFrame/iframe
script
// 修改上例
console.log(Object.prototype.toString.call(fa) [object Array]); // truefunction Person(){}
var p new Person();
console.log(Object.prototype.toString.call(p)); // [object Object]
// 或者script
// a.html 中定义了 var aArr [1,2];
window.onload function(){var myFrame window.frames[myFrame];console.log(myFrame.aArr);console.log(myFrame.aArr instanceof Array); // falseconsole.log(Object.prototype.toString.call(myFrame.aArr) [object Array]); // true
}
/script根据封装一个判断类型的函数
function getType(target){var types {[object Object] : Object,[object Array] : 数组,[object Number] : Number,[object Boolean] : Boolean,[object String] : String,[object Date] : 日期,};if(target null) return null;if(typeof target object){var toStr Object.prototype.toString.call(target);return types[toStr];}else{return typeof target;}
}console.log(getType(18));
console.log(getType({}));
console.log(getType([]));
console.log(getType(new Boolean()));
console.log(getType(new Date));
console.log(getType([1,2,aini]));// 在ES5中新增了Array.isArray()静态方法该方法的作用是确定某个值是不是数组而不管它是在哪个全局环境中创建的如var colors [red,blue,yellow];
console.log(Array.isArray(colors));(万能的静态方法都能判断)
9-8-2 数组去重
1// 使用一个空对象作为临时数组不重复元素值的载体即把数组不重复元素值保存为空对象属性再把该属性名添加到一个返回的数组中
2// 最主要的原理对象的属性名不可能重复会覆盖如var arr [1,3,2,3,4,2,3,2,1,1,2,3,4];
function getUnique(arr){var obj[];var a [];for(var i0;iarr.length;i){if(!obj[arr[i]]){console.log(arr[i]);obj[arr[i]]aini;a.push(arr[i]);}}return a;
}
console.log(getUnique(arr));9-8-3 类数组对象
ES数组的有一些特性是其他对象所没有的
1// 当有新的元素添加到列表中时自动更新length属性
2// 设置length为一个较小值时将截断数组
3// 从Array.prototype中继承一些方法
4// 其类属性为Array
5// 这些特点让数组和常规的对象有明显的区别
6// 通常来说把一个拥有数值length属性和对应非负整数属性的对象看作一种“类数组”对象
7// 这些“类数组”对象不能直接调用数组方法或者其length属性也没有特殊的行为如字符串的length属性就是只读的但可以使用数据遍历的方法来遍历它们就像操作真正的数组一样// 字符串是一个类数组对象
// Arguments对象是一个类数组对象str zeronetwork;
str.length 5;
console.log(str.length); // 11字符串Length属性只读不可写
str zeronetwork;
console.log(str[0],str[1],str[2]); // z e t在客户端JS中一些DOM方法如 document.getElementsByTagName()也返回类数组对象如
var lis document.getElementsByTagName(li);
console.log(lis);
for(var i0;ilis.length;i){var li lis[i];li.onclick function(e){alert(this.innerText); //在对应的li标签鼠标单击时弹出里面内容}
}自定义一个类数组对象
如果让一个对象成为一个类数组对象主要的做法是利用属性名模拟数组元素索引动态的length属性甚至可以调用push等数组方法
添加length属性
var obj {0:aini,1:dinur,2:dil};
console.log(obj[0],obj[1],obj[2]);for(p in obj){console.log(p,obj[p]);
};
//添加length属性
var obj {0:aini,1:dinur,2:dil,length:3};
console.log(obj[0],obj[1],obj[2]);
console.log(obj.length);
//动态添加length属性
var o {};
var i 0;
while(i10){o[i] i*i;i;
};
o.lengthi;
//把o当做数组一样遍历
var sum 0;
for(var j0;jo.length;j){sumo[j];
}
console.log(sum);自定义一个类数组对象
//自定义类数组类及对象
function MyArray(){this.length arguments.length;for(var i0;ithis.length;i){this[i]arguments[i]}
};
var arr new MyArray(4,3,5,wangwei);
for (var i0; iarr.length;i){console.log(arr[i]);
};function YourSize(size){this.lengthsize;for(var i0;isize;i){this[i][i];}
};
var a new YourSize(3);
for(var x in a){this[i]i;console.log(x,a[x]);
};
console.log(a);添加push方法
//添加push方法同时更新length属性;
var obj {0:a,1:b,2:c,length:3,push:function(){this[this.length]arguments[0];this.lengtharguments.length;return this.length;
}}
console.log(obj.length);
console.log(obj.push(aini));添加push方法添加多个元素
//添加push方法同时更新length属性;
var obj {0:a,1:b,2:c,length:3,push:function(){for(var i0;iarguments.length;i){this[this.length]arguments[i];this.length}return this;
}}
console.log(obj.length);
console.log(obj.push(aini,dilnur,diana,norah));可间接应用Array的方法
//间接使用array的push方法
var obj {length:0,push:Array.prototype.push,splice:Array.prototype.splice};
console.log(obj.push(c,d)); //2
console.log(obj.length); // 29-8-4 检测类数组对象
封装一个函数用来检测类数组对象如
var o new String(aini);
// 判定 O 是否是一个类数组对象
// 字符串和函数都具有length属性但可以用typeof进行排除
// 客户端DOM文件节点也具有length属性需要用额外判断o.nodeType!3将其排除function isArrayLike(o){if(o // o 是非null或undefinedtypeof o object // 是对象isFinite(o.length) // 有限数o.length0 // 大于0o.length Math.floor(o.length) //length 属性是整数o.length 2^32 // )return true else return false;
};
console.log(isArrayLike(o));ES的数组方法为了能应用在类数组对象上而特意定义为通用的但是由于类数组没有继承自Array.prototype就不能直接调用某些数组方法只有间接的使用Function.call方法调用如
var s1 aini like ;
console.log(s1.concat( dilnur));var o {0:a,1:b,2:c,length:3};
o.length3;
console.log(Array.prototype.join.call(o,));
console.log(Array.prototype.slice.call(o,0));
console.log(Array.prototype.map.call(o,function(x){return x.toUpperCase();
}));注concat方法是特例可以直接用在字符串上面
作为数组的字符串
// 字符串的行为类似于只读的数组除了用charAt()方法来访问单个字符外还可以使用方括号ES为可索引的字符串定义这些特性主要为了更加简洁、可读和更高效的操作var s aini;
console.log(s.charAt(0)); // a
console.log(s[0]); // a字符串的这些类数组的行为可以使用数组方法
var str ainilikedilnur;
console.log(Array.prototype.join.call(str,,));
console.log(Array.prototype.filter.call(str,function(s){return s
}).join());字符串是不可变值所以当把它当作数组看待时它们是只读的故如push、sort、reverse、splice等数组方法会修改数组但在字符串上是无效的
9-8-5 二位数组和多维数组
// 二维或多维数组就是以数组作为数组元素的数组多维数组的应用场景还是比较多的比如一个班的学生成绩表但从本质上说ES并不支持真正的二维或多维数组
var a1 [1,2,3];
var a2 [4,5,6];
var a3 [7,8,9];
var arr [a1,a2,a3];
// 或者
var arr [[1,2,3],[4,5,6],[7,8,9,10,12]
];
var arr [[1,2,3],[wangwei,wujing],[零点,网络]
];
var arr new Array(new Array(1,2,3),[wangwei,wujing],new Array(零点,网络)
);
如果把二维数组当作另外一个数组的元素那就是三维数组了以此类推就形成多维数组
访问二维或多维数组只要多次使用[ ]操作符即可如
var arr [[1,2,3],[wangwei,wujing],[零点,网络]
];
console.log(arr[1][1]); // 访问
arr[3] new Array(one,two,three); // 添加
arr[2][2] 程序员;
console.log(arr);二维或多维数组的遍历同一维数组一致只不过使用了嵌套遍历
求最大值
var arr [[88,67,44,98],[56,78,99,56],[79,95,78,92]
];
var max arr[0][0];
for(var i0;iarr.length;i){for (var j0;jarr[i].length;j){arr[i][j]max?(maxarr[i][j]):max;}
};
console.log(max);九九乘法表
var table new Array(9);
for(var i0;itable.length;i) table[i]new Array(9);
for(var row0;rowtable.length;row){for(var col0;coltable[row].length;col){table[row][col]row*col;}
}
console.log(table[5][9]);function getTable(arr){var table document.createElement(table);for(var i1; iarr.length;i){var tr document.createElement(tr);for(var j1;jarr[i].length;j){var td document.createElement(td);td.innerTexti*jarr[i][j];tr.appendChild(td);}table.appendChild(tr);}document.body.appendChild(table);
}
getTable(table);10Date日期对象
GMT// 格林尼标准时
UTC// 格林尼治标准时间
时区// 共有24区东12区西12区一个时区一小时
计算机元年// 1970年1月1日 0:00:00 用于计时的开始Date使用的是UTC// 是所有时区的基准标准时间是1970年1月1日 00:00:00 开始经过的毫秒数保存日期10-1 Date对象的创建
d new Data() // 以当前日期和时间创建date对象
d new Date(0) // 以1970-1-1 00:00:00 的毫秒数创建date对象
d new Date(2020,7,18) // 就表示创建了一个2020年8月18号的日期对象new Date()里面直接传年份注意
// JS里的月份是 0~11 分别表示1~12月所以计算机里 0 表示1月1表示2月11就是12月
d new Date(2020,7,18) //得到的是2020年8月18日// ECMAScript提供了两个静态方法
Date.parse()和Date.UTC()10-2 Date.parse()
// 跟时区无关月份基于1
Date.parse()方法接受一个表示日期的字符串参数返回一个时间戳(毫秒数)
1// 日期字符串应该符合 RFC 2822 和 ISO 8061 这两个标准:
2// ISO 8601扩展格式 YYYY-MM-DDTHH:mm:ss:ssssZ如2020-05-25T00:00:00yyyy4位年份、MM月份、DD天、HH时、mm分、ss秒、ssss毫秒通常见的日期格式
1// mm/dd/yyyy 如: 3/21/2009即月/日/年
2// yyyy/mm/dd 如: 2009/3/21
3// mmmm dd,yyyy 如: Apr 21,2009即英文月名 日年即January 12,2010console.log(Date.parse(May 25,2020));
console.log(Date.parse(2018-07-22))
console.log(Date.parse(2018-07))
console.log(Date.parse(2018))
console.log(Date.parse(07/22/2018))
console.log(Date.parse(2018/07/22))
console.log(Date.parse(2018/7/22))
console.log(Date.parse(July 22, 2018))
console.log(Date.parse(July 22, 2018 07:22:13))
console.log(Date.parse(2018-07-22 07:22:13))
console.log(Date.parse(2018-07-22T07:22:13))// 注如果传入Date.parse()方法的字符串不能表示日期那么它会返回NaN根据parse()返回值创建Date对象
var d new Date(Date.parse(May 25, 2020));
console.log(d)// Mon May 25 2020 00:00:00 GMT0800 (中国标准时间)实际上如果直接将表示日期的字符串传递给Date构造函数也会在后台调用Date.parse()两者是等价的如
var d new Date(May 25, 2020);注意月份前面有0和没有0是不一样的中间连接符是‘-’的时候才会有区别其他都是GTM时间
var time Date.parse(2019-04-03); // 8区时间
var time1 Date.parse(2019-4-03); // 标准UTC时间
var d new Date(time);
var d1 new Date(time1);
console.log(d);
console.log(d1);注日期对象在不同浏览器实现的并不统一比如传入了超出范围的值
(负数可以直接取正前面的0可以省略
var d new Date(January 33,2020);
console.log(d) // Invalid Date// 在解析January 33,2020有些浏览器返回Invalid DateIE返回Sun Feb 02 2020把超出的时间往后自动推算// UNIX 时间戳的原因以秒seconds为单位。JavaScript 以毫秒milliseconds为单位记录时间。可在使用UNIX 时间戳去实例化Date 对象
var timestamp 1591866649;
var d new Date(timestamp * 1000);
console.log(d);10-3 Date.UTC()
Date.UTC方法//参数不用引号而且需要逗号隔开月份是基于0的语法
// date.UTC(year,month,[date,hrs,min,sec,ms])
必需参数// yearmonth// 其参数为日期中的年,月基于0,日,小时0到23,分,秒,毫秒其中年月必选如果没有提供日默认为1如果省略其他参数则统统默认为0// 至少应该是3个参数但是大多数 JavaScript 引擎都能解析 2 个或 1 个参数var d Date.UTC(2020);
var d1 Date.UTC(2020,6); // 毫秒数1593561600000
var d2 new Date(Date.UTC(2020,6));
var d3 new Date(Date.UTC(2020,6,6,17,55,55)); // 自动添加时区返回当地日期和时间
var d4 new Date(2020,6,10); //月份从0开始,6即是7月// 如果没有任何关于时区的信息会将日期视为 UTC 并自动执行到当前计算机时区的转换// 可以直接把UTC参数传递给Date()构造函数如var dnew Date(2020,6); // Wed Jul 01 2020 00:00:00 GMT0800
var d new Date(2020,6,6,17,55,55); // 即为GMT时间
console.log(d);// 当初始化一个 Date 对象时可以选择时区可以通过添加 HOURS 的格式或者通过一个被圆括号包裹的时区名来描述一个时区注兼容性有点问题
console.log(new Date(Jun 7,2020 13:51:01 0700));
console.log(new Date(Jun 7,2020 13:51:01 (CET))); // CET欧洲中部时间补充
var time2 Date.parse(2022-4-03)
console.log(new Date(time2))
var time3 Date.parse(2022-04-03)
console.log(new Date(time3))var time2 Date.parse(2022/4/03)
console.log(new Date(time2))
var time3 Date.parse(2022/04/03)
console.log(new Date(time3))var time2 Date.parse(2022,4,03)
console.log(new Date(time2))
var time3 Date.parse(2022,04,03)
console.log(new Date(time3))var time2 Date.parse(2022-4-03 10:56:36)
console.log(new Date(time2))
var time3 Date.parse(2022-04-03T10:56:36)
console.log(new Date(time3))10-3 总结
1// Date.parse() 当只有年月日时且连接符是 - 的时候月份前面有0和没有0是有区别的没有0则是UTC时间有0的话加8小时var time2 Date.parse(2022-4-03)
console.log(new Date(time2))
var time3 Date.parse(2022-04-03)
console.log(new Date(time3))2// 当连接符不是 - 的时候月份面前加不加0都一样都返回UTC时间
3// 当年月份后面加上 时间以后月份前面有没有有没有0也都一样都返回正确的 时间
4// 只有当 - 充当连接符而且月份前面有0是年月份跟时间之间才可以加T当使用其他连接符或者用 - 当连接符但是月份前面没有0 时就会报错var time2 Date.parse(2022-4-03 10:56:36)
var time3 Date.parse(2022-04-03T10:56:36)var time2 Date.parse(2022,4,03 10:35:56)
var time3 Date.parse(2022,04,03 10:35:56)10-4 Date 对象的方法
1.Date()// 返回当日的日期和时间。
2.getDate()// 从 Date 对象返回一个月中的某一天 (1 ~ 31)。
3.getDay()// 从 Date 对象返回一周中的某一天 (0 ~ 6)。
4.getMonth()// 从 Date 对象返回月份 (0 ~ 11)。
5.getFullYear()// 从 Date 对象以四位数字返回年份。
6.getYear()// 请使用 getFullYear() 方法代替。
7.getHours()// 返回 Date 对象的小时 (0 ~ 23)。
8.getMinutes()// 返回 Date 对象的分钟 (0 ~ 59)。
9.getSeconds()// 返回 Date 对象的秒数 (0 ~ 59)。
10.getMilliseconds()// 返回 Date 对象的毫秒(0 ~ 999)。
11.getTime()// 返回 1970 年 1 月 1 日至今的毫秒数与valueOf()返回值相同。
12.getTimezoneOffset()// 返回本地时间与格林威治标准时间 (GMT) 的分钟差。
13.getUTCDate()// 根据世界时从 Date 对象返回月中的一天 (1 ~ 31)。
14.getUTCDay()// 根据世界时从 Date 对象返回周中的一天 (0 ~ 6)。
15.getUTCMonth()// 根据世界时从 Date 对象返回月份 (0 ~ 11)。
16.getUTCFullYear()// 根据世界时从 Date 对象返回四位数的年份。
17.getUTCHours()// 根据世界时返回 Date 对象的小时 (0 ~ 23)。
18.getUTCMinutes()// 根据世界时返回 Date 对象的分钟 (0 ~ 59)。
19.getUTCSeconds()// 根据世界时返回 Date 对象的秒钟 (0 ~ 59)。
20.getUTCMilliseconds()// 根据世界时返回 Date 对象的毫秒(0 ~ 999)。
21.parse()// 返回1970年1月1日午夜到指定日期字符串的毫秒数。
22.setDate()// 设置 Date 对象中月的某一天 (1 ~ 31)。
23.setMonth()// 设置 Date 对象中月份 (0 ~ 11)。
24.setFullYear()// 设置 Date 对象中的年份四位数字。
25.setYear()// 请使用 setFullYear() 方法代替。
26.setHours()// 设置 Date 对象中的小时 (0 ~ 23)。
27.setMinutes()// 设置 Date 对象中的分钟 (0 ~ 59)。
28.setSeconds()// 设置 Date 对象中的秒钟 (0 ~ 59)。
29.setMilliseconds()// 设置 Date 对象中的毫秒 (0 ~ 999)。
30.setTime()// 以毫秒设置 Date 对象。
31.setUTCDate()// 根据世界时设置 Date 对象中月份的一天 (1 ~ 31)。
32.setUTCMonth()// 根据世界时设置 Date 对象中的月份 (0 ~ 11)。
33.setUTCFullYear()// 根据世界时设置 Date 对象中的年份四位数字。
34.setUTCHours()// 根据世界时设置 Date 对象中的小时 (0 ~ 23)。
35.setUTCMinutes()// 根据世界时设置 Date 对象中的分钟 (0 ~ 59)。
36.setUTCSeconds()// 根据世界时设置 Date 对象中的秒钟 (0 ~ 59)。
37.setUTCMilliseconds()// 根据世界时设置 Date 对象中的毫秒 (0 ~ 999)。
toSource()// 返回该对象的源代码。
toString()// 把 Date 对象转换为字符串。
toTimeString()// 把 Date 对象的时间部分转换为字符串。
toDateString()// 把 Date 对象的日期部分转换为字符串。
toGMTString()// 请使用 toUTCString() 方法代替。
toUTCString()// 根据世界时把 Date 对象转换为字符串。
toLocaleString()// 根据本地时间格式把 Date 对象转换为字符串。
toLocaleTimeString()// 根据本地时间格式把 Date 对象的时间部分转换为字符串。
toLocaleDateString()// 根据本地时间格式把 Date 对象的日期部分转换为字符串。
toISOString()// 返回对应的UTC时间的 ISO8601 写法如2012-12-31T16:00:00.000Z
toJSON()// 返回值同toISOString()
UTC()// 根据世界时返回 1970 年 1 月 1 日 到指定日期的毫秒数。
valueOf()// 返回 Date 对象的原始值。以上方法大概分为三种to方法、get方法和set方法。
10-4-1 to方法
1to方法-日期格式化方法
// date()类型还有一些专门用于将日期格式化为字符串的方法如toString()
toDateString()// 以特定于实现的格式显示星期几、月、日和年
toTimeString()// 以特定于实现的格式显示时、分、秒和时区
toLocaleDateString()// 以特定于地区的格式显示星期几、月、日和年
toLocaleTimeString()// 在特定于地区的格式显示 时、分、秒
toUTCString()// 以特定于实现的格式显示UTC日期
toISOString()// 返回ISO表示的日期
toGMTString()方法// 这是一个与toUTCString()等价的方法其存在的目的在于确保向后兼容不过ECMAScript推荐使用 toUTCString()方法继承的方法
与其他引用类型一样Date类型也重写了toLocaleString()、toString()和valueOf()方法但这些方法的返回值与其他类型中的方法不同。
valueOf()方法// 返回日期的毫秒数
toString()方法// 通常返回带有时区信息的日期和时间其中时间一般以军用时间(即小时从0到23)var d new Date();console.log(d); // 默认就是调用toString()方法
console.log(d.valueOf()); // 返回毫秒数
console.log(Date.parse(new Date())); //返回毫秒数
console.log(d.toString()); toLocaleString()//会按照与浏览器设置的地区相适应的格式返回日期和时间即时间格式中会包含AM或PM但不会包含时区信息var d new Date();
console.log(d.toLocaleString()); // 2022/12/17 13:16:37注真实场景中toString()和toLocaleString()没有什么用仅在调试代码时使用
至于valueOf()方法返回的是毫秒数因此可以方便的使用比较操作来比较日期如
var d1 new Date(2019,0,1);
var d2 new Date(2020,2,1);
console.log(d1);
console.log(d2);var d1 new Date(2019,0,1);
var d2 new Date(2020,2,1);
console.log(d2-d1); // 毫秒数相减
console.log(d2.valueOf()-d1.valueOf());相减默认调用的就是valueOf方法
默认调用toString()方法进行字符串拼接
var d1 new Date(2019,0,1);
var d2 new Date(2020,2,1);
console.log(d2d1); // 字符串拼接默认调用toString()方法注意日期比较的惯性思维如2019.1.1早于2020.2.1日但后者返回的毫秒数大。
10-4-2 get 方法
var d new Date();
console.log(d.getDate()); //18
console.log(d.getDay()); //4
console.log(d.getFullYear()); //2020
console.log(d.getMonth()); //5 (starts from 0)
console.log(d.getHours()); //17
console.log(d.getMinutes()); //30
console.log(d.getSeconds()) //13
console.log(d.getMilliseconds()); //765
console.log(d.getTime()) //159186842016010-4-3 set 方法
var d new Date();
d.setDate(6);
d.setFullYear(2022);
d.setMonth(4);
d.setHours(4);
d.setMinutes(4);
d.setSeconds(4);
d.setMilliseconds(123);
d.setTime(1598765678999);
console.log(d);// 注setDate 和 setMonth 从 0 开始编号
// 这些方法基本是跟getter方法一一对应的但是没有setDay方法因为星期几是计算出来的而不是设置的
set方法中的参数如果超出它的范围会进位称为冒泡如date.setHours(48)这也会将日期数变大var date new Date();
date.setFullYear(2022,1,18);
// date.setMonth(24);
date.setMonth(2,8);
date.setHours(16,18,28,208);
console.log(date.toLocaleString());// 如果参数是负数表示从上个月的最后一天开始减去相应的单位数
// 以上的方法都有一个相对应的 UTC set方法
获取当前时间戳:获取当前时间戳:
console.log(new Date().getTime());console.log(Date.now());Date.now()方法返回表示调用这个方法时的日期和时间的毫秒数其简化了Date.getTime()方法如
如果有些浏览器不支持Date.now()可以使用操作符获取Date对象的时间戳如
var start Date.now();
num 0;
for(var i0;i100000000;i){
num1;
num2;
} // 模拟其他处理代码
var stop Date.now();
var result stop-start;
console.log(result)
console.log(num)10-5 日期计算
var d1 new Date(2020-06-18);
var d2 new Date(2020-06-19);
console.log(d1 - d2); // -86400000
console.log(d1 d2); // 返回两个日期的字符串拼接
// 或
var d1 new Date(July 18,2020 14:10:18);
var d2 new Date(July 19,2020 14:10:18);
var diff d2.getTime() - d1.getTime();
console.log(diff);// getTime() 方法返回以毫秒计的数字所以需要将当日时刻计入如July 18, 2020 14:14:14 不等于July 18, 2020。在这种情况下可以使用 setHours(0, 0, 0, 0) 来重置当日时刻10-5-1 计算本年度还剩下多少天
function leftDays() {var today new Date();var endYear new Date(today.getFullYear(), 11, 31, 23, 59, 59, 999);var msPerDay 24 * 60 * 60 * 1000;return Math.round((endYear.getTime() - today.getTime()) / msPerDay);
}
console.log(leftDays());10-5-2 中文月份和日期
var d new Date();
var month d.getMonth();
var week d.getDay();
var monthArr [一月,二月,三月,四月,五月,六月,七月,八月,九月,十月,十一月,十二月];
var weekArr [星期一,星期二,星期三,星期四,星期五,星期六,星期天]
console.log(monthArr[month],weekArr[week-1])10-5-3 获取日期部分信息
var d new Date()
Date.prototype.dataPart function(part){if(!part) part d;else part part.toLowerCase();var monthArr [一月,二月,三月,四月,五月,六月,七月,八月,九月,十月,十一月,十二月];var weekArr [星期一,星期二,星期三,星期四,星期五,星期六,星期天];switch(part){case y:return this.getFullYear();break;case m:return monthArr[this.getMonth()];break;case d:return this.getDate();break;case h:return this.getHours();break;case m:return this.getMinutes();break;case s:return this.getSeconds();break;case w:return weekArr[this.getDay()];break;default:return this.getDate();}return this.getDate();
}
console.log(d.dataPart(d))
console.log(d.dataPart(s))
console.log(d.dataPart(h))
console.log(d.dataPart(y))// 在date对象原型中定义方法只要是用date对象创建的日期都能用这个方法10-5-4 还有多长时间退休
function retireDays(birthday,age){var d1 new Date(birthday).getFullYear();var d2 new Date().getFullYear();var old d2 - d1;console.log(现在你的年龄是 old,将于 (d1 age) 退休);if(age - old 0){console.log(还差(age - old)年退休)}else{console.log(你已经退休啦好好享受老年生活吧);}
}
retireDays(2020.6.6,60);10-5-5 网页时钟
function checkTime(time){if (time 10){time 0time}return time
}
function showTime(){var d new Date();var h checkTime(d.getHours());var m checkTime(d.getMinutes());var s checkTime(d.getSeconds());myP document.getElementById(myP);myP.innerHTML h : m :s;timer setTimeout(showTime(),1000);
}
showTime()10-5-6 倒计时
function getCountDown(d){var d1 new Date();var d2 new Date(d); // var diff d2 - d1; // 相差毫秒数var o {};if(diff 0){var day Math.floor(diff / 1000 / 60 / 60 / 24); // 剩下多少天var hour Math.floor(diff / 1000 / 60 / 60 % 24); // 剩下多少小时var minute Math.floor(diff / 1000 / 60 % 60); // 剩下多少分var second Math.floor(diff / 1000 % 60); // 剩下多少秒o.stop false;o.str 距离d 还剩下day天hour小时minute分second秒;}else{o.stop true;o.str 已时结束;}return o;
}
var timer setInterval(function(){var mydate document.getElementById(mydate);mydate.innerHTML getCountDown(2022.12.31 23:59:59).str;if(getCountDown(2022.12.31 23:59:59).stop) clearInterval(timer);
},1000);10-5-7 计算某个日期加上天数
function addDate(date,days){var d new Date(date);d.setDate(d.getDay() days);var month d.getMonth() 1;var day d.getDate();if(month 10)month 0 month;if(day 10)day 0 day;var value d.getFullYear() - month - day;return value;
}
console.log(addDate(2020-6-6,50));
console.log(addDate(2020-6-6,-6));10-5-8 判断闰年
四年一闰百年不闰四百年再闰
Date.prototype.isLeapYear function(){return (this.getFullYear() % 4 0 ((this.getFullYear() % 100 !0) || (this.getFullYear() % 400 0)));
}
var d new Date();
console.log(d.isLeapYear());
d.setFullYear(2019);
console.log(d.isLeapYear());10-5-9 计算两个日期相差的天数
function daysDiff(dateOne,dateTwo){var oneMonth dateOne.substring(5, dateOne.lastIndexOf(-));var oneDay dateOne.substring(dateOne.length,dateOne.lastIndexOf(-) 1);var oneYear dateOne.substring(0, dateOne.indexOf(-));var twoMonth dateTwo.substring(5, dateTwo.lastIndexOf(-));var twoDay dateTwo.substring(dateTwo.length, dateTwo.lastIndexOf(-) 1);var twoYear dateTwo.substring(0, dateTwo.indexOf(-));var diff ((Date.parse(oneMonth/oneDay/oneYear) - Date.parse(twoMonth/twoDay/twoYear)) / 86400000);return diff;
}
console.log(daysDiff(2020-6-6,2020-5-30));10-5-10 格式化输出
Date.prototype.format function(fmt){var o {M : this.getMonth() 1,d : this.getDate(),h : this.getHours(),m : this.getMinutes(),s : this.getSeconds(),q : Math.floor((this.getMonth() 3) / 3),S : this.getMilliseconds()};if(/(y)/.test(fmt)){fmt fmt.replace(RegExp.$1,(this.getFullYear() ).substr(4 - RegExp.$1.length));}for(var k in o){if(new RegExp(( k )).test(fmt)){fmt fmt.replace(RegExp.$1,RegExp.$1.length 1? o[k]: (00 o[k]).substr(( o[k]).length));}}return fmt;
};
var d new Date(2020,6,6,0,0,0);
console.log(d);
console.log(d.format(yyyy年MM月dd日)); // 2020年07月06日
console.log(d.format(yyyy年MM月d日 hh:mm:ss)); // 2020年07月6日 00:00:0010-5-11 时间控件
body
input typedate idmydate
input typedatetime idmydatetime
input typedatetime-local idmylocaldate
input typetime idmytime
input typebutton value提交 οnclickshow()
script
function show(){var mydate document.getElementById(mydate);var mydatetime document.getElementById(mydatetime);var mylocaldate document.getElementById(mylocaldate);var mytime document.getElementById(mytime);console.log(mydate.value);console.log(mydatetime.value);console.log(mylocaldate.value);console.log(mytime.value);
}
/script
/body10-5-12 制作日历
function getDays(y,m){var d new Date(y,m);d.setMonth(m1);d.setDate(0);return d.getDate();
}
function changeDay(target,d){var year d.getFullYear();var month d.getMonth();var date d.getDate();var week d.getDay();var days getDays(year,month); //一个月内有多少天var current new Date();currentyear current.getFullYear();currentmonth current.getMonth();currentday current.getDate();currentweek current.getDay();var daylist document.getElementById(daylist);for(var idaylist.children.length-1;i0;i--){daylist.removeChild(daylist.childNodes[0]);}var d1 dd1.setDate(1);var firstweek d1.getDay(); //获取当月1号对应星期几for(var i0;ifirstweek%7;i){var li document.createElement(li);daylist.appendChild(li)}for(var i1;idays;i){var li document.createElement(li);li.innerHTML i;if((icurrentday month currentmonth year currentyear) || (monthcurrentmonth yearcurrentyear) || (year currentyear)){li.className lightgray;}else if(i currentday month currentmonth year currentyear){li.className currentbox}else{li.className darkgray}daylist.appendChild(li)}document.getElementById(target-month).innerHTML month 1 月;document.getElementById(target-year).innerHTML year;
}
var d new Date();
changeDay(calender,d)
var prev document.getElementById(prev);
var next document.getElementById(next);
prev.addEventListener(click,function(){d.setMonth(d.getMonth()-1);changeDay(calender,d)
},false);
next.addEventListener(click,function(){d.setMonth(d.getMonth()1);changeDay(calender,d)
},false);10-6 Datejs 日期库
// 官网www.datejs.com10-6-1 返回特定的日期
console.log(Date.today());
console.log(Date.today().toString(yyyy-MM-d HH:m:s))
console.log(Date.today().next().friday().toString(yyyy-MM-d HH:m:s))
console.log(Date.today().last().friday().toString(yyyy-MM-d HH:m:s))
console.log(Date.last().week().toString(yyyy-MM-d HH:m:s))10-6-2 判断
console.log(Date.today().is().sunday());
console.log(Date.today().is().saturday());
console.log(Date.today().is().dec());
console.log(Date.today().is().weekday()); //判断是不是工作日10-6-3 返回加一天或减一天后的日期
可以是负数
console.log(Date.today().addDays(1));
console.log(Date.today().add(1).day());
console.log(Date.today().add(1).month());
console.log(Date.today().add(1).year());
console.log(Date.today().add(1).week());10-6-4 返回某个月的某个日期
console.log(Date.monday().toString(yyyy-MM-d HH:m:s));
console.log(Date.next().monday().toString(yyyy-MM-d HH:m:s));
console.log(Date.april().toString(yyyy-MM-d HH:m:s));
console.log(Date.today().first().monday().toString(yyyy-MM-d HH:m:s)); //本月第一个星期一
console.log(Date.today().second().monday().toString(yyyy-MM-d HH:m:s)); //本月第二个星期二
console.log(Date.today().final().sunday().toString(yyyy-MM-d HH:m:s)); //当前月的最后一个星期天
console.log(Date.april().final().monday().toString(yyyy-MM-d HH:m:s)); //返回四月的最后一个monday10-6-5 返回今天的某个时刻
console.log(Date.today().at(4:18pm).toString(yyyy-MM-d HH:m:s));10-6-6 根据对象构件日期
var t {hour:18,minute:30}
console.log(Date.today().at(t).toString(yyyy-MM-d HH:m:s));10-6-7 日期解析转换
console.log(Date.parse(t));
console.log(Date.parse(tomorrow));
console.log(Date.parse(next friday));
console.log(Date.parse(yesterday));
console.log(Date.parse(last monday));
console.log(Date.parse(July 8th, 2020))
console.log(Date.parse(July-08-2020))
console.log(Date.parse(July/08/2020))
console.log(Date.parse(2020 6 16))
console.log(Date.parse(2020.6.16))
console.log(Date.parse(6.16.2016))
console.log(Date.parse(16:30:30))
console.log(Date.parse(4:30:30 pm))console.log(Date.parse(t 5d)); //今天加上5天
console.log(Date.parse(t 5m));//今天加上五个月
console.log(Date.parse(t - 1m));//今天减去5个月
console.log(Date.parse()); //今天加上一天
console.log(Date.parse(-y)); //今天加上一天10-6-8 链式操作
/添加1个月零5天然后检查该日期是否为星期五
Date.today().add({ months: 1, days: 5 }).is().fri();
//输入日期然后移至下一个星期五减去一个月
Date.parse(10-July-2004).next().friday().add(-1).month();10-6-9 日期比较
Date.today().equals( Date.parse(today)); // true
Date.parse(last Tues).equals(Date.today()); // true|falseDate.equals(Date.today(), Date.parse(today)); // true|false
Date.compare(Date.today(), Date.parse(today)); // 1 greater, -1 less than, Date.today().compareTo(Date.parse(yesterday)); // 1 greater, -1 less than, 0 equal
Date.today().between(startDate, endDate); // true|false10-6-10 转换为字符串
注意该format参数对于该.toString()功能是可选的。如果未提供format.toString()则将调用本地JavaScript Date 函数。
s// 分钟介于0到59之间的秒数如0 to 59
ss// 如果需要分钟的秒数前导零如00 to 59
m// 每小时的分钟数介于0到59之间如0 or 59
mm// 每小时的分钟前导零如果需要如00 to 59
h// 1到12之间的一天中的小时如1 to 12
hh// 如果需要一天中的小时数前导零如01 to 12
H// 0-23之间的一天中的小时如0 to 23
HH// 如果需要一天中的小时数前导零如00 to 23
d// 每月的1到31之间的日期如1 to 31
dd// 如果需要的话该月的某天前导零。如01 to 31
ddd// 缩写的天名如Mon to Sun
dddd// 全日名称如Monday to Sunday
M// 一年中的1-12点之间的月份如1 to 12
MM// 一年中的前导零如果需要如01 to 12
MMM// 缩写的月份名称如Jan to Dec
MMMM// 完整的月份名称如January to December
yy// 将年份显示为两位数如99 or 07
yyyy// 显示完整的四位数年份如1999 or 2007
t// 显示AM / PM指示符的第一个字符如A or P
tt// 显示AM / PM指示符如AM or PM
S// 当日的序数后缀如st, nd, rd, or th自定义日期和时间格式说明符
d// shortDate格式模式如M/d/yyyy
D// longDate 格式模式如dddd, MMMM dd, yyyy
F// fullDateTime 格式模式如dddd, MMMM dd, yyyy h:mm:ss tt
m// monthDay 格式模式如MMMM dd
r// rfc1123 格式模式如ddd, dd MMM yyyy HH:mm:ss GMT
s// sortableDateTime 格式模式如yyyy-MM-ddTHH:mm:ss
t// shortTime 格式模式如h:mm tt
T// longTime 格式模式如h:mm:ss tt
u// universalSortableDateTime 格式模式如yyyy-MM-dd HH:mm:ssZ
y// yearMonth 格式模式如MMMM, yyyy分隔符
正斜杠、空格、- 连字号、逗号
console.log(Date.today().toString());
console.log(Date.today().toString(M/d/yyyy))
console.log(Date.today().toString(d));
console.log(Date.today().toString(MMMM dS,yyyy));new Date().toString(); //星期三2007年10月31日格林尼治标准时间0700太平洋夏令时间
new Date().toString(M/d/yyyy); //2007年10月31日Date.today().toString(d-MMM-yyyy); //2007年10月31日
new Date().toString(HH:mm); // 16:18Date.today().toString(MMMM dS, yyyy); // April 12th, 2008Date.today().toShortDateString();// 10/31/2007. 根据Date.CultureInfo.shortDatePattern特定于区域性
Date.today().toLongDateString();// Wednesday, October 31, 2007. 根据Date.CultureInfo.longDatePattern特定于区域性new Date().toShortTimeString();// 4:18 PM. 根据Date.CultureInfo.shortTimePattern特定于区域性
new Date().toLongTimeString();// 4:18:34 PM. 根据Date.CultureInfo.longTimePattern特定于区域性核心用法
/将日期设置为当前月份和年份的15号
//其他对象值包括year|month|day|hour|minute|second。
Date.today().set({ day: 15 });
Date.today().set({ year: 2007, month: 1, day: 20 });
//将Date添加2天。其他对象值包括 year|month|day|hour|minute|second.
Date.today().add({ days: 2 });
Date.today().add({ years: -1, months: 6, hours: 3 });
Date.today().addYears(1); //增加1年
Date.today().addMonths(-2); //相减2个月
Date.today().addWeeks(1); //增加1周
Date.today().addDays(4); //增加4天
Date.today().addHours(6); //增加6小时
Date.today().addMinutes(-30); //相减30分钟
Date.today().addSeconds(15); //增加15秒
Date.today().addMilliseconds(200); //增加200毫秒Date.today().moveToFirstDayOfMonth();//返回当前月份的第一天
Date.today().moveToLastDayOfMonth();//返回当前月份的最后一天new Date().clearTime(); //将时间设置为00:00一天的开始
Date.today().setTimeToNow();//将时间重置为当前时间与clearTime()的功能相反其他用法
Date.getMonthNumberFromName(March);// 2-特定于CultureInfo。static
Date.getDayNumberFromName(sat);// 6-特定于CultureInfo。静态
Date.isLeapYear(2008) // true|false. static
Date.getDaysInMonth(2007, 9) // 31 static
Date.today().getWeek();//返回一年中的第几周。根据年份Date 返回1到52 | 53
Date.today().setWeek(1); //将一年中的星期几设置为星期几
var test new Date(); // Do something... like run a test...
test.getElapsed(); //返回距现在的毫秒数
Date.today().isDaylightSavingTime();// true|false. 在夏令时之内
Date.today().hasDaylightSavingTime();// true|false. 是否遵守夏令时10-7 Momentjs日期库
// 官网https://momentjs.com/10-7-1 获取当前的时间
console.log(moment()); //返回一个对象console.log(moment()._d);
console.log(moment().format(yyyy-M-d)); //2022-12-6
console.log(moment(undefined).format(yyyy-M-d)); //2022-12-6
console.log(moment([]).format(yyyy-M-d))10-7-2 Format days
moment().format(MMMM Do YYYY, h:mm:ss a);
moment().format(dddd);
moment().format(MMM Do YY);
moment().format(YYYY [escaped] YYYY);
moment().format();10-7-3 Relative Time
moment(20111031, YYYYMMDD).fromNow();
moment(20120620, YYYYMMDD).fromNow();
moment().startOf(day).fromNow();
moment().endOf(day).fromNow();
moment().startOf(hour).fromNow();10-7-4 Calendar Time
console.log(moment().subtract(10, days).calendar());
console.log(moment().subtract(6, days).calendar());
console.log(moment().subtract(3, days).calendar());
console.log(moment().subtract(1, days).calendar());
console.log(moment().calendar());
console.log(moment().add(1, days).calendar());
console.log(moment().add(3, days).calendar());
console.log(moment().add(10, days).calendar());其余的去官网了解
11RegExp 正则
11-1 正则创建
11-1-1 直接量
var pattern /aini/;
var str aini is a good boy
console.log(pattern.test(str))11-1-2 使用RegExp构造函数
var pattern new RegExp(aini)
var str aini is a good boy
console.log(pattern.test(str))11-2 修饰符
1g :// 表示全局(global)模式即模式将被应用于所有字符串而非在发现第一个匹配项时立即停止
2i // 表示不区分大小写(case-insensitive)模式
3M:// 表示多行(multiline)即在到达一行文本末尾时还会继续查找下一行中是否存在匹配项一个正则表达式就是一个模式与上述 3 个标志的结合体不同组合产生不同结果如
var pattern1 /at/g; // 匹配字符串中所有 at 的实例
var pattern2 /[bc]at/i; // 匹配第一个 bat 或 cat不区分大小写
var pattern3 /.at/gi; // 匹配所有以 at 结尾的 3 个字符的组合不区分大小写另一种创建正则表达式的方式是使用 RegExp 构造函数它接收两个参数一个是要匹配的字符串模式另一个是可选的修饰符字符串如
var pattern1 new RegExp([bc]at,i);1// 也可以不使用 new 操作符其等同于 new RegExp()
2// 但如果 pattern 参数是一个正则表达式时有所不同它只是简单地返回 pattern而不会创建一
个新的 RegExp 对象但如果使用了不同的修饰符就会返回新的 RegExp 对象var re new RegExp(.at,g);
newre RegExp(re); // true
newre RegExp(re,i); // false 模式改变了就不是同一个正则了
console.log(re newre);RegExp构造函数最大的特点是可以动态的创建正则表达式这种情况往往用在没办法通过写死在正则直接量中
var arr [坏蛋,傻女人,妈的,脑子有病,神经病,疯子]var arrStr [你真是个坏蛋,你是傻女人,他妈的气死我了,你脑子有病,神经病啊你干嘛打我,狗疯子我要打死
你你妈的脑子真有病]
for(var i0;iarrStr.length;i){
for(var j0;jarr.length;j){
//var reg /arr[j]/g; // 不能用直接量,这是错误的
var reg new RegExp(arr[j],img);
arrStr[i] arrStr[i].replace(reg,*);
}
}
console.log(arrStr)11-3 转义符
1// 如果模式中用到特殊字符(元字符)包括非字母字符必须使用转义 \ 包括\自身
2// 如果使用 RegExp 构造函数转换符必须使用双 \ ,即 \\var str aini is [aini] ia ainsii;
var pattern /\[aini\]/g ;
var pattern1 new RegExp(\\[aini\\],g); 双斜杠转义
console.log(str.match(pattern))
console.log(str.match(pattern1))11-4 精确匹配
11-4-1 元字符
// 元字符是正则表达式拥有特殊含义的字符即是正则表达式语法的一部分其包括
// ^ $ . * ? ! : | \ / ( ) [ ] { } 15 个
// 这些元字符在正则表达式中都有一或多种特殊用途某些符号只有在正则的某些上下文中才具有某种特殊含义在其他
// 上下文中则被当作直接量处理然而任何时候如果要使用这些特殊字符进行匹配都必须进行转义。var pattern1 /[bc]at/i; // 匹配第一个 bat 或 cat不区分大小写
var pattern2 /\[bc\]at/i; // 匹配第一个[bc]at不区分大小写
var pattern3 /.at/gi; // 匹配所有以 at 结尾的 3 个字符的组合不区分大小写
var pattern4 /\.at/gi; // 匹配所有.at不区分大小写// 注其他标点符号没有特殊含义可以直接当作字面量进行匹配如果记不住这些特殊符号可以为
每个标点符号前都加上反斜杠另外许多字母和数字在有反斜杠做前缀时也有特殊含义11-4-2 使用特殊字符
1// 可以直接使用字符表示它们本身但也可以使用它们的 ASCII 或者 Unicode 代码来指定字符要使用 ASCII 来表示一个字符则必须指定一个两位的十六进制代码并在前面加上\x
2// 也可以使用八进制代替十六进制来指定字符
3// 如果要使用 Unicode 来表示必须指定字符串的四位的十六进制\uxxxxvar sColor blue;// b 的 ASCII 为 98, 等于十六进制 62因此 b 可以用\x62
var re /\x62/; // 16 进制 相当于/b/
var re /\142/; // 8 进制 相当于/b/
var re /\u0062/; // Unicode 相当于/b/
console.log(re.test(sColor));11-4-3 其他特殊字符
\o NULL 字符\u0000、
\t 制表符(\u0009)、
\n 换行符(\u000A)、
\v 垂直制表符(\u000B)、
\f 换页符(\u000C)、
\r 回车符(\u000D)、
\b 回退字符、
\a alert 字符、
\e escape 字符、
\xnn 由 16 进制数 nn 指定的拉丁字符
如\x0A 等价于\n、
uxxxx 由 16 进制数 xxxx 指定的 Unicode 字符
如\u0009 等价于\t、 cX 与 X 相对应的控制字符
如,\cJ 等价于换行符\n。body
textarea idtxt/textarea
textarea idresult/textarea
p idmyp/p
input typebutton onclickshow() value提交 /
script
function show(){
var str document.getElementById(txt).value;
var re /\n/g;
str str.replace(re, \n); // 保存在数据中
document.getElementById(result).value str;
str str.replace(re, br/); // 显示在页面中
document.getElementById(myp).innerHTML str;
}
show()11-4-4 预定义字符
也称为字符类一个字符类可以匹配它所包含的任意字符由于某些模式会反复用到所以提供了一些预定义字符来提高匹配的效率
. // 等同于[^\n\r] 除了换行符和回车之外的任意字符
\d // 等同于[0-9] 数字ASCII 数字
\D // 等同于[^0-9] 非数字字符除了 ASCII 数字之外的任何字符
\s // 等同于[ \t\n\0B\f\r] 空白字符任何 Unicode 空白符
\S // 等同于[^ \t\n\0B\f\r] 非空白字符任何非 Unicode 空白符的字符注和\w 不同
\w // 等同于[a-zA-Z0-9_] 单词字符(即 ASCII 字符包括所有字母所有数字和下划线) \W 等同于[^a-zA-Z0-9_]非单词字符任何不是 ASCII 字符
[\b] // 退格直接量特例注以上的字符指的是 ASCII 字符非 Unicode 字符
var str 567 9838 zeronetwork 王唯;
var re /./gi;
var re /\d/gi;
var re /\d\d\d/gi; // 匹配三个数字
var re /\D/gi;
var re /\s/gi;
var re /\S/gi;
var re /\w/gi;
var re /\W/gi;通过将一些字符放入方括号中表示要匹配的范围
简单范围// 形如/[acf]at/g
排除范围// 使用^(脱字符号)用来定义否定字符类必须出现在[ 之后匹配所有不包含在方括号内的字符形如/[^acf]at/
连续范围// 使用 – 连字符表示一个连续范围如[a-z], [0-9] ,[^1-4]
组合范围// 形如[a-m1-4\n]var str a bat, a Cat, a fAt baT, a faT, a faT cat;
var re /[bcf]at/gi; //[bat, Cat, fAt, baT, faT, faT, cat]
var re /[\u0062cf]at/gi; //[bat, Cat, fAt, baT, faT, faT, cat]
var re /[^bc]at/gi; //[fAt, faT, faT]
console.log(str.match(re));
var str num1, num2, num3, num4, num5, num6, num7, num8, num9;
var re /num[1-4]/gi;
console.log(str.match(re)); // [num1, num2, num3, num4]
var str 567 9838 abc;
var re /[0-9][0-9][0-9]/gi; // [567, 983]
var re /[0-9]{3}/gi; // [567, 983]
console.log(str.match(re));// 注有些字符类转义字符只能匹配 ASCII 字符还没有扩展到可以处理 Unicode 字符但可以通过十六进制表示法来显式定义 Unicode 字符类如/[\u0400-\u04FF]/ 用以匹配所有 Cyrillic 字符斯拉夫语11-4-5 量词
非贪婪模式放在量词后面
{n} // 匹配 n 次
{n, m} // 匹配至少 n 次但不超过 m 次
{n, } // 匹配至少 n 次
? // 匹配 0 次或 1 次等价于{0, 1}
* // 匹配 0 次或多次等价于{0, } // 匹配 1 次或多次等价于{1, }var str wangwei age is 18, (birthday) is 1998 year. jing123 age is;
var re /\d{2,4}/g; // [18, 1998,123]
var re /\w{4}\d?/g; [wang, birt, hday, 1998, year, jing1]
var re /\sage\s/g; // [ age , age ]
var re /[^(|)]*/g; // 匹配非左括号或右括号的字符11-4-6 贪婪与非贪婪
var str wwwwww;
var re /w{2,4}/;
console.log(str.match(re)); // wwww
var str wangwei;
var re /\w/;
console.log(str.match(re)); // wangwei以上匹配的特点是尽可能多地匹配这种匹配称为“贪婪”匹配
贪婪量词的原理// 先看整个字符串是否匹配如果没有发现匹配它去掉该字符串中的最后一个字符并再次尝试如果还没有发现匹配那么再去掉最后一个字符这个过程一直重复到发现一个匹配或者字符串不剩下任何字符// 与贪婪对应的就是非贪婪称为惰性方式只需要在量词的后面跟随一个问号即可如??、?、*?或{1,5}?如修改上例
惰性量词的原理先看字符串中的第一字母是否匹配如果不匹配再读出下一字符一直继续直到发现匹配或者整个字符串都检查过出没有匹配与贪婪工作方式相反支配量词只尝试匹配整个字符串如果整个字符串不匹配就不做进一步尝试已不被支持
贪婪// ? * {n} {n,m} {n, }
惰性// ?? *? ? {n}? {n,m}? {n, }?
支配// ? * {n} {n,m} {n, }使用非贪婪模式所得到的结果可能和期望并不一致
var str aaab;
var re /ab/; // aaab
var re /a?b/; // aaab
console.log(str.match(re));由于惰性匹配是从左往右匹配
11-4-7 复杂模式
7-1 候选
// 候选就是用“|”来表示的模式或关系它表示的是在匹配时可以匹配“|”的左边或右边。这个“|”相当于“或”var str i like colors:red black;
var re /red|black|green/; // red
console.log(str.match(re));
var re /jpg|png|gif/;
console.log(re.test(xxx.jpg));
var str wang is 18 age.
var re /\d{2}|[a-z]{4}/; // wang
console.log(str.match(re));候选项的尝试匹配次序是从左到右直到发现了匹配项如果左边的选择项匹配就忽略右边的匹配项即使它会产生更好的匹配如
var str abcde;
var re /a|ab/; // a 只会匹配一个 a
console.log(str.match(re));候选结合 replace() 方法 主要用在从用户输入删除不合适的单词
var str 你妈的他妈的不是东西都是坏蛋!;
var re /坏蛋|你妈的|他妈的/gi;
var newStr str.replace(re,****);
console.log(newStr);
var newStr str.replace(re,function(sMatch){
return sMatch.replace(/./g, *);
});7-2 分组
通过用一对圆括号可以把单独的项组合成子表达式
var str wangwei1998;
var re /[a-z](\d)/;
console.log(str.match(re));也会把圆括号里匹配的内容单独提取出来
为什么需要分组
1// 它是一个组合项或子匹配可作为一个单元统一操作如可以统一使用|、*、等进行处理。
2// 可以把分组匹配的结果单独抽取出来以备后用var str javascript;
var re /java(script)?/; // true script 可有可无
console.log(re.test(str));
var str dogdogdog;
var re /(dog){3}/;
console.log(str.match(re));
// 只关心匹配尾部的数字把它单独提取出来
var str 京 A88888;
var re /.{2}(\d)/;
var arr re.exec(str);
console.log(arr);
console.log(arr[1]);还可以嵌套分组
var str zeronetwork;
var re /(zero(net(work)))/;
console.log(str.match(re));反向引用
// 每个分组都被存放在一个特殊的地方以备将来使用这此分组也称为捕获组这些存储在分组中的特殊值称之为反向引用即允许在同一正则表达式的后部引用前面的子表达式其是通过在字符“\”后加一位或多数数字实现的该数字指定了分组的子表达式的在正则中的位置var str god godod gododod godododod;
var re /g(od)\1*/g;
console.log(str.match(re));由于分组可以嵌套所以反向引用是按照从左到右遇到的左括号的顺序进行创建和编号的如 (A?(B?(C?)))
1.(A?(B?(C?))) 2.(B?(C?)) 3. (C?)
var str aaabbccbb aaabbccbb;
var re /(a(b))(c)\3\2\s\1/;
console.log(str.match(re));对分组的引用并不是对分组表达式的引用而是对分组模式相匹配的文本的引用再如
// 匹配单引号与双引号之间的字符
var str wangwei \is 18 old, he is \good\ man;
var re /[][^]*[]/g; // 不要求引号的匹配
var re /([])[^]*\1/g; // 不要求引号的匹配
console.log(str.match(re));反向引用的情景通常用来处理相同连续的内容如
// 匹配连续相同的三个数字
console.log(111a222b333c123d.match(/(\d)\1\1/ig));// [111, 222, 333]
console.log(111a222b333c123d.match(/(\d)\1{2}/ig));//[111, 222, 333]
// 不同点这是匹配 3 个数字而不是相同的数字
console.log(111a222b333c123d.match(/(\d){3}/ig)); // [111, 222, 333, 123]
//匹配 ABAB 格式的数字如1212 或 3434
console.log(1212a3434b4545c123d.match(/(\d)(\d)\1\2/g)); // [1212, 3434, 4545]
// 匹配 ABBA 格式的数字如1221 或 3443
console.log(1221a3443b4554c123d.match(/(\d)(\d)\2\1/g)); //[1221, 3443, 4554]
// 检索 html 标记及内容
var html 请访问a hrefhttps://www.zeronetwork.cnzeronetwrok/a网站;
var reg /(\w)[\s]*.?(.*)\/\1/ig;
console.log(html.match(reg));反向引用几种使用方法
// 使用正则表达对象的 test(), match(), search()方法后反向引用的值可以从 RegExp 构造函数中获得var str #123456789;
var re /#(\d)/;
re.test(str);
console.log(RegExp.$1); // 123456789
// 去重
var str aaaabbbbbbbcccccc;
var re /(\w)\1*/g;
console.log(str.replace(re, $1)); // abc
// 格式化输出
var str 1234 5678;
var re/(\d{4}) (\d{4})/;
var newStr str.replace(re, $2 $1); // 5678 1234
console.log(newStr);7-3 非捕获性分组
1// 反向引用称为捕获性分组如果只需要组合不需要反向引用则可以使用非捕获性分组
2// 在较长的正则表达式中存储反向引用会降低匹配速度
3// 非捕获性分组在左括号的后面加一个问号和一个紧跟的冒号如(?: )此时使用\n 就访问不了捕获组了。var str zeronetwork;
var re /(?:net)/;
console.log(str.match(re));
console.log(RegExp.$1); // 空
// 删除 HTML 标识
String.prototype.stripHTML function(){
var re /(?:.|\s)*?/g;
return this.replace(re,);
};
var stra href#b零点程序员/b/a;
document.write(str br);
document.write(str.stripHTML());7-4 边界
边界(bounday)用于正则表达式中表示模式的位置也称为匹配表达式的锚
^ // 匹配字符串的开头在多行中匹配行开头
$ // 匹配字符串的结尾在多行中匹配行结尾
\b // 匹配单词的边界即位于字符\w 和\W 之间的位置或位于字符\w 和字符串的开头或者结尾之间
\B // 匹配非单词的边界的位置var str JavaScript; // JavaScript
var str JavaScript Code; // null
var re /^JavaScript$/; // 如果不使用$,使用\s 也可以但是包含了空格
console.log(str.match(re));
var str Study Javascript Code. JavaScripter is good;
var re /Java[sS]cript/g; // [Javascript, JavaScript]
var re /\bJava[sS]cript\b/g; // [Javascript]
var re /\bJava[sS]cript\B/g; // [JavaScript]
var re /\B[sS]cript/g; // 不会匹配单独的 script 或 Script
console.log(str.match(re));
var str wangwei is good man;
var re /(\w)$/; // man
re.test(str);
console.log(RegExp.$1);
var re /^(\w)/; // wangwei
re.test(str);
console.log(RegExp.$1);
var re /^(.?)\b/; // wangwei
re.test(str);
console.log(RegExp.$1);
var str First second third fourth fifth sizth;
var re /\b(\S?)\b/g;
var re /\b(\w)\b/g;
var arr str.match(re);
console.log(arr);7-5 前瞻先行断言
不是分组
前瞻(lookahead) // 是指检查接下来出现的是不是位于某个特定字符之前分为正向和负向
// 正向前瞻要将模式放在(? 和 )之间如(?p)要求接下来的字符都与 p 匹配但不能包括匹配 p 的那些字符也称为正向先行断言
// 负向前瞻将模式放到(?! 或 ) 之间如(?!p)要求接下来的字符不与 p 匹配也称为负向先行断言注虽然用到括号但这不是分组JS 不支持后瞻后瞻可以匹配如匹配 b 且仅当它前面没有 a
var str1 bedroom Bedding;
var re /([bB]ed(?room))/; // bed
var re /([bB]ed(?!room))/; // Bed
console.log(str1.match(re));
console.log(RegExp.$1);
var str Javascript: function is simple javascript.;
var re /[jJ]avascript(?:)/g; // Javascript 后面有冒号才匹配
var re /[jJ]avascript(?!:)/g; // javascript 后面没有冒号才匹配
console.log(str.match(re));
var str JavaScript Javascript JavaBeans javascripter;
var re /[jJ]ava(?[sS]cript)/g; // [Java, Java, java]
var re /[jJ]ava(?![sS]cript)/g; // Java
var re /[jJ]ava(?![sS]cript)\w/g; // JavaBeans
console.log(str.match(re));
// 添加千分位
var str 钱1234567890123;
var re /(?(\B)(\d{3})$)/g;
console.log(str.match(re));
console.log(str.replace(re, ,));7-6 多行模式
// 要指定多行模式需要指定 m 选项如果待匹配字符串包含多行那么^与$锚字符除了匹配整个字符串的开始和结尾之外还能匹配每行的开始和结尾var str wangwei is\ntechnical director\nof zeronetwork;
var re /(\w)$/g; // zeronetwork
var re /(\w)$/gm; // [is, director, zeronetwork]
console.log(str.match(re));
var re /^(\w)/g; // wangwei
var re /^(\w)/gm; // [wangwei, technical, of]
console.log(str.match(re));
var re /^(.)$/g; // null 如果待匹配字符串中有换行如果不使用 m,则结果为 null
console.log(str.match(re));RegExp 实例属性
global// 只读布尔值表示是否设置了 g 标志
ignoreCase// 只读布尔值是否设置了 i 标志
multiline// 只读布尔值是否设置了 m 标志
lastIndex// 可读写整数如果模式带有 g表示开始搜索下一个匹配项的字符位置从 0 起该属性一般会在 exec()和 test() . 中用到
source// 只读正则表达式的字符串表示按照字面量形式而非传入构造函数中的字符串模式返回但不包含修饰符var str wangwei name is Wangwei \r\n WANGWEI age is 18 wangwei;
var reg /Wangwei/img;
console.log(reg.global); //true
console.log(reg.ignoreCase); //true
console.log(reg.multiline); //truevar str wangwei name is Wangwei \r\n WANGWEI age is 18 wangwei;
var reg /Wangwei/img;
reg.test(str);
console.log(reg.lastIndex); //7// 是匹配完以后最后一个字符后面的位置匹配多次以后可以依次找到匹配的位置
var str wangwei name is Wangwei \r\n WANGWEI age is 18 wangwei;
var reg /Wangwei/img;
reg.test(str);
console.log(reg.lastIndex); //7
reg.test(str);
console.log(reg.lastIndex); //23
reg.test(str);
console.log(reg.lastIndex); //34可以通过设置 lastIndex 的值来控制搜索匹配的位置
var str wangwei name is Wangwei \r\n WANGWEI age is 18 wangwei;
var reg /Wangwei/img;
reg.test(str);
console.log(reg.lastIndex); //7
reg.test(str);
console.log(reg.lastIndex); //23
reg.lastIndex 0; //从头开始匹配
reg.test(str);
console.log(reg.lastIndex); //711-5 RegExp 实例方法
toLocaleString()和 toString()方法是 RegExp 实例继承的方法其都会返回正则表达式的字面量与正则表达式的方式无关如
var pattern new RegExp(\\[bc\\]]at,gi);
alert(pattern.toString()); // /\[bc\]]at/gi
alert(pattern.toLocaleString()); // /\[bc\]]at/gi说明即使是通过 RegExp 构造函数创建的但 toLocaleString()和 toString()方法仍然会像它以字面量的形式返回
正则表达式的 valueOf()方法返回正则表达式本身instanceof 判断的话是 RegExp
var pattern new RegExp(\\[bc\\]]at,gi);
alert(pattern.valueOf()); // /\[bc\]]at/giRegExp 对象定义了两个用于执行模式匹配操作的方法它们的行为与 String 类中的与模式相关的方法很类似
11-5-1 test() 方法
测试目标字符串与某个模式是否匹配接受一个字符串参数如果匹配返回 true否则返回 false该方法使用非常方便一般被用在 if 语句主如
var pattern /zero/i;
console.log(pattern.test(Zeronetwork)); // true
var text 000-00-0000;
var pattern /\d{3}-\d{2}-\d{4}/;
if (pattern.test(text)){
alert(匹配);
}11-5-2 exec()方法
// 是 RegExp 对象最主要的方法该方法是专门为捕获组而设计的// 其会接受一个参数即要应用模式的字符, 会返回第一个匹配项信息的数组就像 String 类的 match()方法为非全局检索返回的数组一样如果没有匹配成功返回 null在数组中第一项是与整个模式匹配的字符串其他项是与模式中的捕获组匹配的字符串(如果模式中没有捕获组该数组只包含一项)// 返回的虽然为 Array 实例但包含两个额外的属性index 和 inputindex 表示匹配项在字符串中的位置而 input 表示应用正则表达式的字符串var text mom and dad and baby;
var pattern /mom( and dad( and baby)?)?/gi;
var arr pattern.exec(text);
console.log(arr); // mom and dad and baby, and dad and baby, and baby
console.log(arr.index); // 0
console.log(arr.input); // mom and dad and baby
console.log(arr[0]); // mom and dad and baby
console.log(arr[1]); // and dad and baby
console.log(arr[2]); // and baby// 对于 exec()不管有没有设置全局标志其每次也只会返回一个匹配项
// 在不设置全局标志情况下在同一个字符串上多次调用 exec() 将始终返回第一个匹配项的信息。
// 而设置全局标志的情况下每次调用 exec()则都会在字符串中继续查找新匹配项会将当前正则对象的 lastIndex 属性设置为紧挨着匹配子串的字符位置为下一次执行 exec()时指定检索位置如果 exec()没有匹配结果lastIndex 被重置为 0var text cat, bat, sat, fat;
var pattern /.at/; // 没有使用 g 全局
var pattern /.at/g; // 使用 g 全局
var matches pattern.exec(text);
console.log(matches);
console.log(match:matches[0],index: matches.index ,lastIndex: pattern.lastIndex);
// 再一次执行使不使用 g结果不一样
matches pattern.exec(text);
console.log(matches);
console.log(match:matches[0],index: matches.index ,lastIndex: pattern.lastIndex);
// 再一次执行使不使用 g结果不一样
matches pattern.exec(text);
console.log(matches);
console.log(match:matches[0],index: matches.index ,lastIndex: pattern.lastIndex);使用 test()与 exec()是等价的当 exec()的结果不是 nulltest()返回 true 时它们都会影响模式的 lastIndex()属性当然是在全局下如此也可以使用 test()遍历字符串就跟 exec()一样如
var text cat, bat, sat, fat;
var pattern /.at/g;
console.log(pattern.test(text));
console.log(pattern.lastIndex);
console.log(pattern.test(text));
console.log(pattern.lastIndex);
// 一直调用 4 次test()就会返回 false
// 遍历
var text cat, bat, sat, fat;
var pattern /.at/g;
while(pattern.test(text)){
console.log(pattern.lastIndex);
}// lastIndex 属性是可读写的可以在任何时候设置它以便定义下一次搜索在目标字符串中的开始位置exec()和test()在没有找到匹配项时会自动将 lastIndex 设置为 0如果在一次成功的匹配之后搜索一个新的字符串一般需要显式地把这个属性设置为 0var text cat, bat, sat, fat;
var pattern /.at/g;
console.log(pattern.exec(text));
console.log(pattern.lastIndex);
// 匹配新的字符串
var str gat,good,pat;
pattern.lastIndex 0; // 3 如果没有此句返回 9即 pat 的位置
console.log(pattern.exec(str));
console.log(pattern.lastIndex);11-6 RegExp 构造函数属性
// RegExp 构造函数包括一些属性这些属性适用于作用域中的所有正则表达式并且基于所执行的最近一次操作而变化另外这些属性名都有别名即可以通过两种方式来访问它们可以称为长属性和短属性1. input -- $_ // 最近一次要匹配的字符串
2. lastMatch -- $ // 最近一次的匹配项
3. lastParen -- $ // 最近一次匹配组
4. leftContext -- $ // input 字符串中 lastMatch 之前左边的文本
5. rightContext -- $’ // input 字符串中 lastMatch 之后右边的文本
6. multiline -- $* // 布尔值表示是否所有表达式都使用多行模式部分浏览器未实现使用这些属性都可以从 exec()或 test()执行的操作中提取出更具体的信息
var text this has been a short summer;
var pattern /(.)hort/gm;
if(pattern.test(text)){console.log(RegExp.input); // this has been a short summerconsole.log(RegExp.lastMatch); // shortconsole.log(RegExp.lastParen); // sconsole.log(RegExp.leftContext); // this has been aconsole.log(RegExp.rightContext); // summerconsole.log(RegExp.multiline); // undefined
}由于短属性大都不是有效 JS 标识符所以必须通过方括号访问
var text this has been a short summer;
var pattern /(.)hort/gm;
if(pattern.test(text)){console.log(RegExp.$_); // this has been a short summerconsole.log(RegExp[$]); // shortconsole.log(RegExp[$]); // sconsole.log(RegExp[$]); // this has been aconsole.log(RegExp[$]); // summerconsole.log(RegExp[$*]); // false
}// 除了以上还有 9 个用于存储捕获组的构造函数属性语法为 RegExp.$1 . RegExp.$2…..RegExp.$9, 分别用于匹配第一第二…第九个捕获组在调用 exec()或 test()方法时这些属性会被自动填充var text this has been a short summer;
var pattern /(..)or(.)/g;
if(pattern.test(text)){console.log(RegExp.$1); // shconsole.log(RegExp.$2); // tconsole.log(RegExp.$3); //
}说明包含了两个捕获组本质上就是反向引用。
11-7 字符串属性
7-1 search()方法
接受的参数就一个正则表达式其返回字符串中第一个匹配项的索引如果没有找到匹配项则返回-1如
var text cat, bat, sat, fat;
// 以下的.at 可以改成 at
var pattern /.at/; // 0
var pos text.search(/.at/); // 0
var pos text.search(/.at/g); // 0
console.log(pos);// search()不支持全局检索它会忽略正则表达式参数中的修饰符 g也会忽略 RegExp 的 lastIndex 属性总是从String 的开始位置搜索即它总是返回 String 中第一个匹配子串的位置
// 如果 search()参数不是正则表达式则首先会通过 RegExp 构造函数将它转换成正则表达式var text cat, bat, sat, fat;
var pos text.search(at); // 1
console.log(pos);注也可以理解为就是查找普通的字符串的索引位置
7-2 match()方法
本质上与 RegExp 的 exec()方法相同其只接受一个参数一个正则表达式或是一个 RegExp 对象如果参数不是 RegExp则会被 RegExp()转换为 RegExp 对象如
var text cat, bat, sat, fat;
var pattern /.at/;//[cat, index: 0, input: cat, bat, sat, fat, ...]
var pattern .at;//会被转换成 RegExp 对象
var pattern /.at/g; //[cat, bat, sat, fat]
var matches text.match(pattern);
console.log(matches);返回一个包含匹配结果的数组如果没有匹配结果返回 null
// 如果没有使用修饰符 gmatch()不会进行全局检索它只会检索第一个匹配但其依然会返回一个数组此时数组唯一的一项就是匹配的字符串// 如果使用了分组第一项是匹配的整个字符串之后的每一项如果有保存着与正则表达式中的捕获组匹配的字符串var str zeronetwork is good zeronetwork;
var re1 /zero(net(work))/;
var re2 /zero(net(work))/g;
var result1 str.match(re1);
var result2 str.match(re2);
console.log(result1);
console.log(result2);全局与非全局返回的数组不一样
如果使用了全局检索数组会保存所有匹配结果而不会显示分组里面匹配的结果数组没有 inputindex属性
// match()方法如果使用了一个非全局的正则实际上和给 RegExp 的 exec()方法传入的字符串是一样的它返回的数组带有两个属性index 和 inputindex 指明了匹配文本在 String 中的开始位置input 则是对该 String本身的引用。// 匹配 URL
var str 访问零点网络官网https://www.zeronetwork.cn/index.html;
var re /(\w):\/\/([\w.])\/(\S*)/;
var result str.match(re);
console.log(result);
var o{};
if(result ! null){
o.fullurl result[0];
o.protocol result[1];
o.host result[2];
o.path result[3];
}
for(var p in o)
console.log(p : o[p]);如果使用了全局返回的是每个匹配的子串数组没有 index 和 input 属性如果希望在全局搜索时取得这此信息可以使用 RegExp.exec()方法
7-3 replace()方法
// 该方法接受两个参数第一个参数可以是一个 RegExp 对象或一个字符串这个字符串不会被转换成正则表达式第二个参数是要替换的字符串可以是一个字符串或一个函数如果第一个参数是字符串那么只会替换第一个子字符串要想替换所有子字符串唯一的办法就是提供一个正则表达式而且要指定全局 g 标志如var text cat, bat, sat, fat;
console.log(text.replace(at,ond));// cond, bat, sat, fat
console.log(text.replace(/at/g,ond));// cond,// 在第二个参数中使用捕获组使用$加索引数字replace()将用与指定的子表达相匹配的文本来替换字符这是一个非常实用的特性如var str cat, bat, sat, fat;
console.log(str.replace(/(at)/g,$1er));//cater, bater, sater, fater
// 将英文引号替换为中文引号
var str zeronetwork;
var quote /([^]*)/g;
console.log(str.replace(quote,“$1”));如果第二个参数是字符串还可以使用一些特殊的字符序列将正则表达式操作得到的值插入到结果字符串中如
$$ // $美元符号
$ // 匹配整个模式的子字符串与 RegExp.lastMath 的值相同
$ // 匹配的子字符串之前左边的子字符串与 RegExp.leftContext 的值相同
$’ // 匹配的子字符串之后右边的子字符串与 RegExp.rightContext 的值相同
$n // 匹配第 n 个捕获组的子字符串其中 n 等于 0-9如$1 是匹配第一个捕获组的子字符串$2 是第二个捕获组的子字符串以此类推如果正则表达式中没有定义捕获组则使用空字符串
$nn // 匹配第 nn 个捕获组的子字符串其中 nn 等于 00-99如$01 是匹配第一个捕获组的子字符串$02 是匹配第二个捕获组的子字符串以此类推如果正则表达式中没有定义捕获组则使用空字符串通过这些特殊的字符序列可以使用最近一次匹配结果中的内容如
var text my name is wangwei zero;
var re /(\w)g(\w)/g;
console.log(text.replace(re,$$));
console.log(text.replace(re,$));
console.log(text.replace(re,$));
console.log(text.replace(re,$));
console.log(text.replace(re,$1));
console.log(text.replace(re,$01));
var text cat, bat, sat, fat;
var result text.replace(/(.at)/g,word ($1));
console.log(result); // word (cat), word (bat), word (sat), word (fat)
// 把名字格式颠倒
var name Wei, Wang;
console.log(name.replace(/(\w)\s*,\s*(\w)/, $2 $1)); // Wang Wei// replace()方法的第二个参数也可以是一个函数该回调函数将在每个匹配结果上调用其返回的字符串则将作为替换文本// 该函数最多可传递 3 个参数模式的匹配项、模式匹配项在字符串中的位置和原始字符串// 在正则表达式中定义了多个捕获组的情况下传递给函数的参数依次是模式的匹配项、第一个捕获组的匹配项、第二个捕获组的匹配项……但最后两个参数仍然分别是模式的匹配项在字符串的位置和原始字符串此种方式可以实现更加精细、动态的替换操作如var str zero net work;
var result str.replace(/\b\w\b/g, function(match,pos,text){
return match:match ,pos:pos ,text:text \n
// word.substring(0,1).toUpperCase() word.substring(1);
});
console.log(result);// 所有单词的首字母大写
var str zero net work;
var result str.replace(/\b\w\b/g, function(word){
return word.substring(0,1).toUpperCase() word.substring(1);
});
console.log(result); // Zero Net Work// 为 HTML 字符转换为实体
function htmlEscape(text){
return text.replace(/[]/g, function(match, pos, originalText){switch(match){case :return lt;;case :return gt;;case :return quot;;case \:return amp;;}});
}
var text p class\greeting\zero network!/p;
console.log(text);
console.log(htmlEscape(text)); // p classgreetingzero network!/p
document.write(text);
document.write(htmlEscape(text));还可以在 replace 中回调函数中使用捕获组
var str aabb;
var reg /(\w)\1(\w)\2/g;
console.log(str.replace(reg,function($,$1,$2){
//return $ , $1 , $2; aabb a b
return 提取了两个字母$1和$2;
}));
// 替换成驼峰写法var str zero-net-work;
var reg /-(\w)/g;
console.log(str.replace(reg, function($,$1){
return $1.toUpperCase();
}));注与 exec()和 test()不同String 类的方法 search()、replace()和 match()并不会用到 lastIndex 属性实际上String 类的这些方法只是简单的将 lastIndex 属性值重置为 0
7-4 split 方法
可以基于指定的分隔符将一个字符串分割成多个子字符串并将结果放在一个数组中分隔符可以是字符串也可以是 RegExp 对象其可以接受可选的第二个参数用于指定数组的大小以便确保返回的数组不会超过既定大小如
console.log(1, 2, 3, 4, 5.split(/\s*,\s*/)); // [1, 2, 3, 4, 5]
var colorText red,blue,green,yellow;
var colors colorText.split(,); // red,blue,green,yellow
var colors colorText.split(,,2);// red,blue
var colors colorText.split(/[^\,]/); // [, ,, ,, ,, ]
console.log(colors);11-8 ES 正则的局限性
尽管 ECMAScript 的正则表达式的功能比较完备但仍缺少某些语言所支持的高级正则表达式特性如下
// 匹配字符串开始和结尾的\A 和\Z 锚但支持^和$
// 向后查找lookbehing但完全支持向前查找 lookahead
// 并集和交集类
// 原子组atomic grouping
// Unicode 支持(单个字符除外如\rFFFF)
// 命名的捕获组(但支持编号的捕获组)
// s(single, 单行)和 x( free-spacing, 无间隔)匹配模式
// 条件匹配
// 正则表达式注释即使存在这些限制ECMAScript 正则表达式仍然是非常强大的能够完成绝大多数模式匹配任务。
12 变量作用域预编译
12-1 变量的作用域
// 变量的作用域是指一个变量在哪个范围内可以使用可以分为两种全局变量// 在所有函数之外定义的变量其作用范围是整个变量定义之后的所有语句包括其后定义的函数及其后的script中的代码
局部变量// 定义在函数之内的变量只有在该函数中才可使用// 如果函数中定义了与全局变量同名的局部变量会覆盖全局变量var msg 这是全局变量的值;
function show(){var str 局部变量;console.log(msg);console.log(str);
}
show();
console.log(str); // Error str is not defined如果函数中使用隐式声明变量即没有使用var声明则该变量自动变成全局变量使用var声明的变量会自动被添加到最接近的环境中在函数内部最接近的环境就是函数的局部环境如
function add(num1,num2){
var sum num1num2;
// sum num1num2;return sum;
}
var result add(10,20);
alert(sum); // 由于sum不是有效的变量因此会导致错误// 注在JavaScript中不声明而直接始初化变量是一个常见的错误做法因为这样可能会导致意外建议在初始化变量之前一定要先声明并且在严格模式下初始化未经声明的变量会导致错误// ES的变量与其他语言的变量有很大区别ES变量是松散类型的这个特点决定了它只是在特定时间用于保存特定值的一个名字而已由于不存在定义某个变量必须要保存何种数据类型值的规则所以变量的值及其数据类型可以在脚本的生命周期内可以被改变尽管从某种角度看这可能是一个灵活强大的特性但同时也是容易出问题的特性// 在实际应用中ES变量还是比较复杂的比如函数的参数由于参数的数据类型不一致导致的结果也不致嵌套函数// 也称为私有函数是指处于局部作用域中的函数当函数嵌套定义时子级函数就是父级函数的私有函数外界不能调用私有函数私有函数只能被拥有该函数的函数代码调用子级函数可以使用父级函数定义的变量父级函数不能使用子级函数定义的变量其他函数不能直接访问子级函数如此就实现了信息的隐藏如function funA(){var strA funA定义的变量strA;funB();function funB(){var strB funB定义的变量strB;console.log(strA);console.log(strB);}
}
funA();12-2 预编译
// ES是一种具有函数优先的轻量级解释型或即时编译型的编程语言其可以不经过编译而直接运行但是ES存在一个预编译的机制这也是Java等一些语言中没有的特性也就正是因为这个预编译的机制导致了ES中变量提升的一些问题// JavaScript运行三部曲
// 脚本执行期间JS引擎按照以下步骤进行处理
· 1.语法分析
· 2.预编译
· 3.解释执行
// 即在执行代码前还需要两个步骤
// 语法分析就是引擎检查你的代码有没有什么低级的语法错误
// 解释执行就是执行代码
// 预编译简单理解就是在内存中开辟一些空间存放一些变量与函数
// JS预编译发生时刻
// 预编译是在脚本执行前就发生了更确切的说是在函数执行前发生的也就是说函数执行时预编译已经结束12-2-1 预编译前奏
mply global暗示全局变量任何变量如果未经声明就赋值这些变量就为全局对象(window)所有(即为window对象的属性);一切声明的全局变量也是window所有如
var a 123;
window.a 123;
function test(){// 这里的b是未经声明的变量所以是归window所有的; 连等的操作也视为无varvar a b 110;
}12-2-2 变量声明提升
在JavaScript函数里的所有声明(只是声明不涉及赋值)都被提前到函数体的顶部预编译时并不会对变量进行赋值(即不会进行初始化),变量赋值是在脚本执行阶段进行的如
console.log(before: a); // before:undefined
var a 1;
console.log(after: a); // after:112-2-3 函数声明整体提升
函数声明语句将会被提升到外部脚本或者外部函数作用域的顶部如
a(); // function
console.log(a); // f a(){...}
function a(){console.log(function);
}
console.log(a);
a();在预编译时function的优先级比var高如
// var a1; // 异常会导致下行的a()异常
a();
var a1;
function a(){console.log(function);}
var a;
console.log(typeof a);此时a的类型是function,而不是number;
函数表达式用的是变量函数并不会提升
b();// b is not a function
var b function○{
console.log(function b);
};
b();声明同名的函数会覆盖掉之前声明的函数
function c(){console.log(function c1);
}
c(); // function c2
function c(){console.log(function c2);
}要理解预编译只要弄清两点变量/函数声明与变量赋值在预编译阶段只进行变量/函数声明不会进行变量的初始化(即变量赋值所有变量的值都是undefined);变量赋值是在执行阶段才进行的
12-3 预编译步骤
// 首先JavaScript的执行过程会先扫描一下整体语法语句如果存在逻辑错误或者语法错误那么直接报错程序停止执行没有错误的话开始从上到下解释一行执行一行。执行器上下文// 英文名Activation Object,简称AO,也称为活动对象
全局对象// 英文名Global Object,简称GO;
// 函数执行前会进行预编译产生AO;
// 全局变量在执行前也会有预编译产生GO;// 局部预编译的4个步骤创建AO;找形参和变量声明将变量和形参名作为AO属性名值为undefined将实参值和形参统一在函数体里面找函数声明值赋予函数体
// 由于全局中没有参数的概念所以省去了实参形参相统一这一步
// 注GO对象是全局预编译所以它优先于AO对象所创建和执行AO对象示例
function fn(a) {console.log(a); // f a(){}// 变量声明变量赋值但只提升变量声明不提升变量赋值var a 123;console.log(a); // 123// 函数声明function a() {}console.log(a); // 123// 函数表达式var b function() {}console.log(b); // f (){}// 函数function c() {}
}
fn(1); // 调用在进行完预编译后执行函数则会以AO为基础对函数中的变量进行赋值函数执行完毕销毁AO对象。
GO对象的示例
global 100;
function test() {console.log(global); // undefinedvar global 200;console.log(global); // 200var global 300;
}
test();
var global;// 注关于GO对象和AO对象它们俩是一个种链式关系如上例如果在函数体的内部没有定义global变量这也意味着AO对象中将有这个global这个属性如果没有会去GO对象中寻找即是就近原则// 另外需要注意的是JS不是全文编译完成再执行而是块编译即一个script块中预编译然后执行再按顺序预编译下一个script块再执行但是此时上一个script块中的数据都是可用的了而下一个块中的函数和变量则是不可用的。12-4 执行环境
// 执行环境(execution context)是ES中最为重要的一个概念也称为执行上下文// 执行环境定义了变量或函数有权访问的其他数据决定了它们各自的行为每个执行环境都有一个与之关联的变量对象环境中定义的所有变量和函数都保存在这个对象中但无法访问这个对象只有解析器在处理数据时会在后台使用它// 执行环境中有个全局执行环境的概念// 全局执行环境是最外围的一个执行环境根据ES实现所在的宿主环境不同表示执行环境的对象也不一样在Web浏览器中全局执行环境被认为是window对象因此所有全局变量和函数都是作为window对象的属性和方法创建的某个执行环境中的所有代码执行完毕后该环境被销毁保存在其中的所有变量和函数定义也随之销毁。每个函数都有自己的执行环境当执行流进入一个函数时函数的环境就会被推入一个环境栈中而在函数执行之后栈将其环境弹出把控制权返回给之前的执行环境ES程序中的执行流就是由这个的机制控制着12-5 作用域链
// 当代码在一个环境中执行时会创建变量对象的一个作用域链(scope chain);作用域链的用途能够保证对执行环境有权访问的所有变量和函数的有序访问作用域的前端始终都是当前执行的代码所在环境的变量对象如果这个环境是函数则将其活动对象作为变量对象活动对象在最开始时只包含一个对象即arguments对象作用域链中的下一个变量对象来自包含(外部)环境而再下一个变量对象则来自下一个包含环境这样一直延续到全局执行环境全局执行环境的变量对象始终都是作用域中的最后一个对象function f(){
var a 10;
function b(){};
}
var g 100;
f();// 查询标识符(在作用域链中查找):// 当在某个环境中为了读取或写入而引用一个标识符时必须通过搜索作用域链来确定.该标识符实际代表什么搜索过程从作用域链的前端开始向上逐级查询如果在局部环境中找到了该标识符搜索过程停止变量就绪如果在局部环境中没有找到该变量则继续沿作用域链向上搜索搜索过程一直追溯到全局环境的变量对象如果在全局环境中也没有找到这个标识符则意味着该变量尚未声明从而会导致错误发生如var colorblue;
function getColor(){// var color red;
return color;
}
console.log(getColor());// 内部环境可以通过作用域链访问所有的外部环境但外部环境不能访问内部环境中的任何变量和函数这些环境之间的联系是线性的、有次序的每个环境都可以向上搜索作用域链以查询变量和函数名但任何环境都不能通过向下搜索作用域链而进入另一个执行环境如var colorblue;
function changeColor(){var anotherColorred;function swapColors(){// 这里可以访问color、anotherColor和tempColorvar tempColor anotherColor;anotherColor color;color tempColor;}swapColors(); // 这里可以访问color和anotherColor但不能访问tempColor
}
changeColor(); // 这里只能访问color
console.log(color is color); // red注函数参数也被当作变量来对待因此其访问规则与执行环境中的其他变量相同
12-6 延长作用域链
// 虽然执行环境的类型总共只有两种全局和局部(函数),但还是有其他办法来延长作用域链因为有些语句可以在作用域链的前端临时增加一个变量对象该变量对象在代码执行时作用域就会加长在代码执行后被移除// try-catch语句的catch块及with语句这两个语句都会在作用域链的前端添加一个变量对象对with语句来说会将指定的对象添加到作用域链中对catch语句来说会创建一个新的变量对象其中包含的是被抛出的错误对象的声明如function buildUrl(){var qs ?namewangwei;with(location){var url hrefqs;}return url;
}
console.log(buildUrl());12-7 作用域中的this对象
// this引用的是函数执行的环境对象即是调用函数的对象或者也可以说是this值(当在网页的全局作用域中调用函数时this对象引用的就是window)。window.color red;
var o {color:blue};
function sayColor(){console.log(this.color);
}
sayColor(); // red
o.sayColor sayColor;
o.sayColor(); // blue12-8 没有块级作用域
ES没有块级作用域在其他类C的语言中由花括号封闭的代码块都有自己的作用域(如果用ES的角度来讲就是它们自己的执行环境),因而支持根据条件来定义变量如下面的代码在ES并不会得到想象中的结果
if(true){var colorblue;
}
console.log(color); // blue;在ES中if语句中的变量声明会将变量添加到当前的执行环境(在这里是全局环境)中在使用for语句时表现的最为明显如
function outputNumber(count){for(var i 0; icount; i){console.log(i);}// var i;console.log(最后的值 i) // 5
}
outputNumber(5);可以模拟块级作用域使用立即执行函数进行模拟
12-9 立即执行函数
立即执行函数也称为自运行函数其没有声明本质上就是匿名函数其在一次执行后立即释放
适合做一些初始化的工作或者模拟块级作用域
(function(){console.log(这里是立即执行函数);
})();立即执行函数不允许使用函数声明方式但是如果在function前加一个号即可同时在控制台中该函数名也会被忽略如
function myFun(){console.log(这里是立即执行函数);
}(); 在function前加上、!、一、等一元操作符也是立即执行函数的写法等同上面的立即执行函数如果没有这些符号解析器会把function认为为一个函数声明
同理只要在function前加上其他的表达式语句都可以如
true function myFun(){console.log(这里是立即执行函数);
}();
// 或
0,function(){console.log(ok);
}();立即执行函数可以传值也可以有返回值如
// 传值
(function(x,y,z){console.log(xyz)
})(1,2,3); // 6
// 返回值
var result (function(x,y,z){var sum xyz;return sum;
})(1,2,3);
console.log(result); // 6//传值
(function(x,y,z){
console.log(xyz)
})(1,2,3);// 6
/返回值
var result (function(x,y,z){
var sum xyz;
return sum;
})(1,2,3);
console.log(result);// 6特例
function myFun(){console.log(这里是立即执行函数);
}(1,2,3);
// 此时不会报错函数也存在但不会立即执行原因是解析器会把它拆分成两条语句如
function myFun(){console.log(这里是立即执行函数);};
(1,2,3);// 这种技术经常在全局作用域中被用在函数外部从而限制向全局作用域中添加过多的变量和函数// 一般来说应该尽量少向全局作用域中添加变量和函数在一个由很多开发人员共同参与的大型应用程序中过多的全局变量和函数很容易导致命名冲突而通过创建私有作用域每个开发人员都可以使用自己的变量而不必担心搞乱全局作用域(function(){var now new Date();if(now.getMonth() 0 now.getDate() 1){console.log(Happy new Year);}
})();说明变量now是匿名函数的局部变量
立即执行函数也是后面要讲的闭包的基本形式但闭包有个问题就是内存占用的问题此种方法可以减少闭包占用的内存问题因为没有指向匿名函数的引用只要函数执行完毕就立即销毁其作用域链了
思考两个小示例
var foo (function f(){return 1;},function g(){return 2;}
)();
console.log(typeof foo); // numbervar x 1;
// (function fun(){}) 因为在括号中所以是一个表达式
// 运行完后就消失了所以typeof fun就是undefined
if(function fun(){}){x typeof fun;console.log(typeof fun); // undefined
}
console.log(x); // 1undefined但是IE中某些对象还在采用引用计数方式这些对象不是原生的Javascript对象如BOM和DOM中的对象就是使用C以COM对象的形式实现的而COM对象的垃圾收集机制采用的就是计数策略因此即使IE的JavaScript引擎是使用标记清除策略来实现的但Javascript访问的COM对象依然是基于引用计数策略的换句话说只要在IE中涉及COM对象就会存在循环引用的问题如
var element document.getElementById(some_element);
var myObject new Object();
myObject.element element;
element.someObject myObject;由于存在这个循环引用即使将示例中的DOM从页面中移除其也永远不会被回收
// 为了避免类似这样的循环引用问题最好是在不使用它们的时候手工断开原生JavaScript对象与DOM元素之间的连接如
myObject.element null;
element.someObject null;目前IE早已把BOM和DOM对象都转换成了真正的JavaScript对象这样就避免了两种垃圾收集算法并存导致的问题也消除了常见的内存泄漏现象
1-3 管理内存
// 使用具备垃圾收集机制的语言编写程序开发人员一般不必要操心内存管理的问题但是JavaScript在进行内存管理及垃圾收集时面临的问题还是与众不同其中最主要的一个问题就是分配给Web浏览器的可用内存数量通常要比分配给桌面应用程序的要少这样做的目的主要是出于安全方面的考虑目的是防止运行JavaScript的网页耗尽全部系统内存而导致系统崩溃内存限制问题不仅会影响给变量分配内存同时还会影响调用栈以及在一个线程中能够同时执行的语句数量。因此确保占用最少的内存可以让页面获得更好的性能而优化内存占用的最佳方式就是为执行中的代码只保存必要的数据一旦数据不再有用最好通过将其值设置为null来释放其引用即解除引用dereferencing其适用于大多数全局变量和全局对象的属性如
function createPerson(name){var localPerson new Object();localPerson.name name;return localPerson;
}
var globalPerson createPerson(wangwei);
globalPersonnull; // 手工解除globalPerson的引用// 注解除一个值的引用并不意味着自动回收该值所占用的内存解除引用的值作用是让值脱离执行环境以便垃圾收集器下次运行时将其回收。// JS的自动内存管理存在一些问题例如垃圾回收实现可能存在缺陷或者不足因此需要找到一个合适的解决方法