JavaScript
完整的JavaScript是由ECMAScript( 语法 ).Brower Objects( DOM.BOM )(特性)组成的,JavaScript区分大小写、变量为松散类型,可保存任何类型的数据。
JavaScript是单线程语言,单线程就说所执行的代码必须按照顺序。
JavaScript 基础
杂七杂八
NaN
:即非数值(Not a Number)是一个特殊的数值
- 任何涉及NaN的操作(例如 NaN / 10)都会返回 NaN;
- NaN 与任何值都不相等,包括 NaN 本身;
值得注意的栗子:
var a = typeof(18-"abc");
console.log(a);
运行结果:
undefined 值
是派生自 null 值的,所以 undefined == null 的返回值结果是 true;
< script >标签有一个布尔型属性 defer
。设置该属性能够延迟 JavaScript 文件到页面解析完毕后再运行。
defer 属性适用于外部 JavaScript 文件,不适用于< script >标签包含的 JavaScript 脚本。
在默认情况下,网页都是同步加载外部 JavaScript 文件,如果 JavaScript 文件比较大,就会影响后面 HTML 代码的解析。
现在可以为< script >标签设置 async 属性
,让浏览器异步加载 JavaScript 文件,即在加载 JavaScript 文件时,浏览器不会暂停,而是继续解析,这样能节省时间,提升响应速度。
async 是 HTML5 新增的布尔型属性,通过设置 async 属性,就不用顾虑< script >标签的放置位置,用户可以根据习惯继续把很多大型 JavaScript 库文件放在< head >标签内。
全局变量
定义全局变量
有以下3种方式:
- 在任何函数体外直接使用 var 语句声明;
var f = 'value';
- 直接添加属性到全局对象上。在Web浏览器中,全局作用域对象为window;
window.f = 'value';
- 直接使用未经声明的变量,以这种方式定义的全局变量被称为隐式的全局变量;
f = 'value';
数据类型
JavaScript 的 6 种基本数据类型:
数据类型 | 含义 | 说明 |
---|---|---|
null | 空值 | 表示非对象 |
undefined | 未定义的值 | 表示未赋值的初始化值 |
number | 数字 | 数学运算的值 |
string | 字符串 | 表示信息流 |
boolean | 布尔值 | 逻辑运算的值 |
object | 对象 | 表示复合结构的数据集 |
JavaScript 使用 typeof 运算符可以检测上述 6 种基本类型。
console.log(typeof 1); //返回字符串"number"
console.log(typeof "1"); //返回字符串"string"
console.log(typeof true); //返回字符串"boolean"
console.log(typeof {}); //返回字符串"object"
console.log(typeof []); //返回字符串"object"
console.log(typeof function(){}); //返回字符串"function"
console.log(typeof null); //返回字符串"object"
console.log(typeof undefined); //返回字符串"undefined"
注意:
typeof 运算符以字符串的形式返回 6 种基本类型之一。但是,通过比较会发现,typeof 返回值与上表有两点差异,简单说明如下:
- 把 null 归为 Object 类型,而不是作为一种特殊类型(Null)的值;
- 把 function() { } 归为 Function 类型。即把函数视为一种独立的基本数据类型,而不是 Object 类型的一种特殊子类。
由于 null 值返回类型为 Object 类型,使用下面自定义函数可以避开因为 null 值而影响的基本类型检测。
//如果是 null 值,则先返回字符串"null",否则返回(typeof o)的值
function typeOf(o) {
return (o === null) ? "null" : (typeof o);
}
console.log( typeOf({}) ); //返回字符串"object"
console.log( typeOf(null) ); //返回字符串"null"
console.log( typeOf(undefined) ); //返回字符串"undefined"
类型检测
constructor 是 Object 类型的原型属性,它能够返回当前对象的构造器(类型函数)。利用该属性,可以检测复合型数据的类型,如对象、数组和函数等。
undefined 和 null 没有 constructor 属性,不能够直接读取,否则会抛出异常;
数值直接量也不能直接读取 constructor 属性,应该先把它转换为对象再调用;
console.log((10).constructor); //返回 Number 类型
console.log(Number(10).constructor); //返回 Number 类型
JavaScript 基本运算
算数运算
语法:Math.random()
功能:返回大于等于 0 小于 1 的一个随机数
返回值:Number
实例:
求n到m之间(不包括 n 和 m)的随机整数的公式:
var n=1,m=9; //n与m随意定义
random = Math.floor(Math.random() * (m - n + 1) + n );
console.log(random);
对象操作运算符
对象操作运算符主要是针对对象、数组、函数这 3 类复合型对象执行的操作,涉及运算符包括:in、instanceof、delete。
举一些栗子来展现它们的用途吧!
下面的代码使用 in 运算符
检测属性 a、b、c、valueOf 是否为对象 o 的成员:
var o = { //定义对象
a:1, //定义属性a
b:function(){} //定义方法b
}
console.log("a" in o); //返回true
console.log("b" in o); //返回true
console.log("c" in o); //返回false
console.log("valueOf" in o); //返回true,继承Object的原型方法
console.log("constructor" in o); //返回true,继承Object的原型属性
下面的代码使用 instanceof 运算符
检测数组 a 是否为 Array、Object 和 Function 的实例:
var a = new Array(); //定义数组
console.log(a instanceof Array); //返回true
console.log(a instanceof Object); //返回true,Array是Object的子类
console.log(a instanceof Function); //返回false
下面的代码使用 delete 运算符
删除对象 a 的属性 x:
var a = { //定义对象a
x : 1, //定义对象成员
y : 2 //定义对象成员
};
console.log(a.x); //调用对象成员,返回1
console.log( delete a.x); //删除对象成员x成功,返回true
console.log(a.x); //返回undefined,没有找到该对象成员
JavaScript 程序结构
分支语句
- if 语句
- else 语句
- switch 语句
- default 语句
default 是 switch 的子句,可以位于 switch 内任意位置,不会影响多重分支的正常执行;如果 default 下面还有 case 子句,应该在 default 后面添加 break 语句,终止 switch 结构;
下面的示例使用 switch 语句设计一个四则运算函数:
function oper(a, b, opr){
switch (opr){
case "+" : //正常枚举
return a + b;
case "-" : //正常枚举
return a - b;
case "*" : //正常枚举
return a * b;
case "/" : //正常枚举
return a / b;
default : //异常处理
return "非预期的 opr 值";
}
console.log( oper( 2, 5, "*")); //返回10
}
循环结构
- while 语句
- do / while 语句
- for 语句
- for / in 语句
下面的示例使用嵌套循环求 1~100 的所有素数:
for(var i=2; i<100; i++){ //打印2~100的素数
var b = true;
for(var j = 2; j < i; j++){ //判断i能否被j整除,能被整除则说明不是素数,修改布尔值为false
if(i%j == 0) b = false ;
}
if(b) document.writeln(i + " "); //打印素数
}
异常处理结构
try / catch / finally 语句
语法格式如下:
try{
//调试代码块
}
catch(e){
//捕获异常,并进行异常处理的代码块
}
finally{
//后期清理代码块
}
如果在 try 子句中发生运行时的错误,或者使用 throw 语句主动抛出异常,并执行 catch 子句中的代码,同时传入一个参数,引用 Error 对象。
举个栗子:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="./test.js"></script>
<title>try/catch/finally语句的实例</title>
</head>
<body>
<p>请输入 5 和 10 之间的一个数:</p>
<input id="demo" type="text">
<button type="button" onclick="myFunction()">检测输入</button>
<p id="message"></p>
<script>
function myFunction() {
var message, x;
message = document.getElementById("message");
message.innerHTML = "";
x = document.getElementById("demo").value;
try {
if (x == "") throw "为空";
if (isNaN(x)) throw "不是一个数字";
if (x > 10) throw "太大了";
if (x < 5) throw "太小了";
}
catch (err) {
message.innerHTML = "输入的值 " + err;
}
}
</script>
</body>
</html>
运行结果:
设计杨辉三角:
var a1 = [1, 1]; //上一行数组,初始化为[1, 1]
var a2 = [1, 1]; //下一行数组,初始化为[1, 1]
for(var i = 2; i <= n; i ++ ){ //从第3行开始遍历高次方的幂数,n为幂数
a2[0] = 1; //定义下一行数组的第一个元素为1
for(var j = 1; j < i - 1; j ++ ){ //遍历上一行数组,并计算下一行数组中间的数字
a2[j] = a1[j - 1] + a1[j];
}
a2[j] = 1; //定义下一行数组的最后一个元素为1
for(var k = 0; k <= j; k ++ ){ //把数组的值传递给上一行数组,实现交替循环
a1[k] = a2[k];
}
}
字符串处理
字符串基础操作
在 JavaScript 中,定义字符串有以下 3 种方法:
- 字符串直接量:使用双引号或单引号包含任意长度的文本;
- 构造字符串:使用 String() 类型函数可以构造字符串,该函数可以接收一个参数,并把它作为值来初始化字符串;
- 使用字符串编码:使用 fromCharCode() 方法可以把字符编码转换为字符串。该方法可以包含多个整数参数,每个参数代表字符的 Unicode 编码。返回值为字符编码的字符串表示;
使用字符串的 toString() 方法可以返回字符串的字符表示;
使用 valueOf() 方法可以返回字符串的值;
两个方法的返回值始终相同,所以一般不用直接调用这两个方法。
使用字符串的 length 属性可以读取字符串的长度。长度以字符为单位,该属性为只读属性;
连接字符串的最简便方法是使用加号运算符;
使用字符串的 concat() 方法可以把多个参数添加到指定字符串的尾部。该方法的参数类型和个数没有限制,它会把所有参数都转换为字符串,然后按顺序连接到当前字符串的尾部,最后返回连接后的字符串;
concat() 方法不会修改原字符串的值,与数组的 concat() 方法操作相似;
在特定的操作环境中,也可以借助数组的 join() 方法来连接字符串;
举个栗子:
var s = "JavaScript", a = []; //定义一个字符串
for(var i = 0; i < 1000; i ++) //循环执行1000次
a.push(s); //把字符串装入数组
var str = a.join(""); //通过join()方法把数组元素连接在一起
a = null; //清空数组
console.log(str);
在上面的栗子中,使用 for 语句把 1000 个 “JavaScript” 字符串装入数组,然后调用数组的 join() 方法把元素的值连接成一个长长的字符串。使用完毕应该立即清楚数组,避免占用系统资源;
运行结果:
使用字符串的 charAt() 和 charCodeAt() 方法,可以根据参数(非负整数的下标值)返回指定位置的字符或字符编码;
对于 charAt() 方法来说,如果参数不在 0 和字符串的 length-1 之间,则返回空字符串;
而对于 charCodeAt() 方法来说,则返回 NaN,而不是返回 0 或空字符串;
使用字符串的 indexOf() 和 lastIndexOf() 方法,可以根据参数字符串,返回指定子字符串的下标位置,如果没有找到该字符串,则返回 -1;
search() 方法与 indexOf() 功能相同,查找指定字符串第一次出现的位置。但是 search() 方法仅有一个参数,定义匹配模式。该方法没有 lastIndexOf() 方法的反向检索功能,也不支持全局模式;
match() 方法能够找出所有匹配的子字符串,并以数组的形式返回;
举个栗子:
下面的代码使用 match() 方法找到字符串中所有字母 h,并返回它们:
var s = "http://www.mysite.cn/index.html";
var a = s.match( /h/g ); //全局匹配所有字符 h
console.log( a ); //返回数组[h,h]
match() 方法返回的是一个数组,如果不是全局匹配,match() 方法只能执行一次匹配;
例如,下面的匹配模式没有 g 修饰符,只能够执行一次匹配,返回仅有一个元素 h 的数组:
var a = s.match( /h/ ); //返回数组[h]
substr() 方法能够根据指定长度来截取子字符串。它包含 2 个参数:第 1 个参数表示准备截取的子串的起始下标,为负数时,会将传入的负值与字符串的长度相加;第 2 个参数表示截取的长度,省略时截取至字符串的末尾,为负数时,返回空字符串;
slice() 和 substring() 方法都是根据指定的起止下标位置来截取子字符串。它们都可以包含 2 个参数,第 1 个参数表示起始下标,第 2 个参数表示结束下标;
slice() 方法:当参数为负数时,其值为字符串长度 + 该负数
substring() 方法:当参数为负数时,自动将参数转换为 0
使用字符串的 replace() 方法可以替换指定的子字符串。该方法包含 2 个参数,第 1 个参数表示执行匹配的正则表达式,第 2 个参数表示准备替换匹配的子字符串;
在 replace() 方法中约定了一个特殊的字符($
),这个美元符号如果附加一个序号就表示对正则表达式中匹配的子表达式存储的字符串引用;
举个栗子:
var s = "JavaScript";
var b = s.replace( /(Java)(Script)/, "$2-$1"); //交换位置
console.log( b ); //返回字符串"Script-Java"
toLowerCase() 可以将字符串转换成小写,toUpperCase() 可以将字符串转换成大写。
toLowerCase() 和 toUpperCase() 是两个本地化原型方法,它们能够按照本地方式转换大、小写字母,由于只有几种语言(如土耳其语)具有地方特有的大、小写映射,所有通常与 toLowerCase() 和 toUpperCase() 方法的返回值一样;
使用字符串的 split() 方法可以根据指定的分隔符
把字符串转劈开为数组;
如果使用数组的 join() 方法,可以把数组元素连接为字符串;
split() 方法支持第2个参数,该参数是一个可选的整数,用来指定返回数组的最大长度。如果设置了该参数,返回的数组长度不会大于这个参数指定的值。如果没有设置该参数,整个字符串都会被分割,而不考虑数组长度;
使用数组
数组的基本操作
for 和 for / in 语句都是可以迭代数组。for 语句需要配合 length 属性和数组下标来实现,执行效率没有 for / in 语句高。另外,for / in 语句会跳过空元素;
对于超长数组而言,建议使用 for / in 语句进行迭代;
举个栗子:
var a = [1, 2, ,,,,,,true,,,,,,, "a",,,,,,,,,,,,,,,4,,,,,56,,,,,,,"b"] //定义数组
var b = [], num = 0;
for( var i in a ){ //遍历数组
if( typeof a[i] == "number" ) //如果为数字,则返回该元素的值
b.push( a[i]);
num++; //计数器
}
console.log( num ); //返回7,说明循环了7次
console.log( b ); //返回[1,2,4,56]
使用 forEach() 方法可以为数组执行迭代操作。具体用法如下:
array.forEach(callbackfn,[thisArg])
参数说明如下:
- callbackfn:必需函数,最多可以接受 3 个参数的函数。forEach 将为数组中的每个元素调用 callbackfn 函数一次;
- thisArg:可选函数,callbackfn 函数中的 this 可引用的对象。如果省略 thisArg,则 this 的值为 undefined;
举个栗子:
//数组 Array
var f = [1,2,3,4,5,6,7,8,9]
//数组的长度,即元素的个数
var len = f.length
console.log('The length of f is ' + len)
//数组遍历
for(let i = 0; i < len ; i++){
console.log(f[i])
}
console.log('索引 值')
f.forEach(function(value, index){
console.log(index, value)
})
//求平方和
function get_pf(arr){
var pf = 0;
var sum = 0;
arr.forEach(function(val,index){
pf = val * val;
sum = sum + pf;
})
return sum;
}
var sums = get_pf([10,20])
console.log(sums)
//求平均数
function get_aver(arr){
var sum = 0;
var aver = 0;
arr.forEach(function(val,index){
sum = val + sum;
aver = sum / (index+1);
})
//var len = arr.length
//return (sum / len)
return aver;
}
var average = get_aver([10,20])
console.log(average)
//求最大值
function get_max(arr){
var max = arr[0];
arr.forEach(function(val){
if(val>max){
max = val;
}
})
return max;
}
var maxs = get_max([10, 20, 3, 6, 7])
console.log(maxs)
//最小值
function get_min(arr){
var min = arr[0];
arr.forEach(function(a){
if(a<min){
min = a;
}
})
return min;
}
var mins = get_min([10, 20, 3, 6, 7])
console.log(mins)
keys() 是 Object 的静态函数,专门用来遍历对象获取键名。
Object.keys() 函数的参数是一个对象,返回一个数组,元素是该对象所有本地属性名。如果使用该函数迭代数组,可以汇集数组的所有元素下标值;
下面的代码直观比较了 keys 迭代对象和数组有什么不同:
var o = {a:"A", b:"B", c:"C"}
console.log(Object.keys(o)); //返回["a","b","c"]
var a = ["A", "B", "C"]
console.log(Object.keys(a)); //返回["0","1","2"]
keys 功能比较单一,应用范围比较窄,但是执行效率比较高;
使用 push() 和 pop() 方法可以在数组尾部执行操作:
- push() 方法能够把一个或多个参数值附加到数组的尾部,并返回添加元素后的数组长度;
- pop() 方法能够删除数组中最后一个元素,并返回被删除的元素;
使用 unshift() 和 shift() 方法可以在数组头部执行操作:
- unshift() 能够把一个或多个参数值附加到数组的头部,第 1 个参数为数组新的元素 0,第 2 个参数为新的元素 1,以此类推,最后返回添加元素后的数组长度;
- shift() 方法能够删除数组第 1 个元素,并返回该元素,然后将余下的所有元素前移一位,并填补数组头部的空缺。如果数组为空,shift() 将不再进行任何操作,返回 undefined;
截取子数组(数组片段)的方法有以下两种:
- splice() 方法可以添加元素、删除元素,也可以截取数组片段。其中第 1 个参数为操作的起始下标位置,第 2 个参数设置要删除的项数,然后通过第 3 个及后面的参数设置要插入的元素。删除元素时,将返回被删除的数组片段,因此可以使用splice() 方法截取数组片段;
- slice() 方法与 splice() 方法功能相近,但是它仅能够截取数组中指定区段的元素,并返回这个子数组。该方法包含 2 个参数,分别指定截取子数组的起始和结束位置的下标;
使用 reverse() 方法能够颠倒数组元素的顺序;
使用 sort() 方法能够根据一定条件对数组元素进行排序。如果调用 sort() 方法时没有传递参数,则按字母顺序对数组中的元素进行排序;
//升序
function f(a,b){ //排序函数
return (a-b) //返回比较参数
}
var a = [3,1,2,4,5,7,6,8,0,9]; //定义数组
a.sort(f); //根据数字大小由小到大进行排序
console.log(a); //返回数组[0,1,2,3,4,5,6,7,8,9]
//降序
function f(a,b){ //排序函数
return -(a-b) //取反并返回比较参数
}
var a = [3,1,2,4,5,7,6,8,0,9]; //定义数组
a.sort(f); //根据数字大小由大到小进行排序
console.log(a); //返回数组[9,8,7,6,5,4,3,2,1,0]
使用对象
在 JavaScript 中,对象是一个泛化的概念,任何值都可以转换为对象,以对象的方式进行使用,如数字对象、布尔值对象、字符串对象、类型对象、函数对象、数组对象等,它们都继承 Object 类型对象,拥有共同的基本属性和方法;
此外,JavaScript 也允许自定义对象;
定义对象
在 JavaScript 中定义对象的方法有 3 种:
- 使用 new 运算符调用构造函数,可以构造一个实例对象:
var objectName = new functionName(args);
参数说明如下:
objectName:返回的实例对象;
functionName:构造函数,与普通函数基本相同,但是不需要 return 返回值,返回实例对象,在函数内可以使用 this 预先访问;
args:实例对象初始化配置参数列表;
- 使用直接量可以快速定义对象,也是定义对象最高效、最简便的方法:
var objectName = {
属性名1 : 属性值1,
属性名2 : 属性值2,
……
属性名n : 属性值n
};
- Object.create 是 ECMAScript 5 新增的一个静态方法,用来定义一个实例对象。该方法可以指定对象的原型和对象特性。具体用法如下:
Object.create(prototype, descriptors)
参数说明如下:
prototype:必需参数,指定原型对象,可以为null;
descriptors:可选参数,包含一个或多个属性描述符的 JavaScript 对象。属性描述符包含数据特性和访问器特性。
其中数据特性说明如下:
▷ value:指定属性值;
▷ writable:默认为 false,设置属性值是否可写;
▷ enumerable:默认为 false,设置属性是否可以枚举(for/in);
▷ configurable:默认为 false,设置是否可修改属性特性和删除属性;
访问器特性包含两个方法,简单说明如下:
▷ set() :设置属性值;
▷ get() :返回属性值;
操作对象
引用对象
对象的引用型数据,赋值操作实际上就说赋予地址;
复制对象
复制对象就说利用 for/in 遍历对象,然后把每个对象成员赋值给另一个对象;
克隆对象
克隆对象也是一种复制操作,不过它的执行效率更高一些;
实例设计一下吧!
第 1 步,封装一个克隆工具。为 Function 类型扩展一个原型方法:
var clone = function (obj) { //对象克隆方法
function Temp() { }; //新建空构造函数
Temp.prototype = obj; //把参数对象赋值给该构造函数的原型对象
return new Temp(); //返回实例化后的对象
}
第 2 步,调用工具函数 clone() 把 obj 克隆给 obj1:
var obj = { //定义对象
x: true,
y: false
}
var obj1 = {}; //新的空对象
obj1 = clone(obj); //克隆对象
第 3 步,检测对象 obj1,其拥有对象 obj 所有属性,但是它们不全等:
console.log(obj1 === obj); //false,说明两个对象不同
console.log(obj1.x); //true
console.log(obj.x); //true
销毁对象
JavaScript 能够自动回收无用的存储单元,当一个对象没有被引用时,该对象就被废除了,JavaScript 会自动销毁所有废除的对象。把对象的所有引用都设置为 null,可以强制废除对象;
举个栗子:
var obj = { //定义对象,被变量obj引用
x:true,
y:false
}
obj = null; //设置为空,废除引用
操作属性
在对象直接量中,属性名与属性值之间通过冒号分隔,冒号左侧是属性名,右侧是属性值,名值对(属性)之间通过逗号分隔。另外,也可以使用点语法定义属性。
使用 Object.defineProperty() 函数可以为对象添加属性,或者修改现有属性。如果指定的属性名在对象中不存在,则执行添加操作:如果在对象中存在同名属性,则执行修改操作。
Object.defineProperty(object, propertyname, descriptor)
参数说明如下:
▷ object:指定要添加或修改属性的对象,可以是 JavaScript 对象或者 DOM 对象;
▷ propertyname:表示属性名的字符串;
▷ descriptor:定义属性的描述符,包括对数据属性或访问器属性;
Object.defineProperty 返回值为已修改的对象,
使用 Object.defineProperties() 函数可以一次定义多个属性。具体用法如下:
Object.defineProperties(object, descriptors)
参数说明如下:
▷ object:对其添加或修改属性的对象,可以是本地对象或 DOM 对象;
▷ descriptors:包含一个或多个描述符对象。每个描述符对象描述一个数据属性或访问器属性;
举个栗子:
var obj = {};
Object.defineProperties(obj, {
x: { //定义属性x
value: 1,
writable: true, //可写
},
y: { //定义属性y
set: function (x) { //设置访问器属性
this.x = x; //改写obj对象的x属性的值
},
get: function () { //设置访问器属性
return this.x; //获取obj对象的x属性的值
},
}
});
obj.y = 10;
console.log(obj.x); //10