JavaScript break 和 continue 语句
break 语句用于跳出循环。
continue 用于跳过循环中的一个迭代。
break 语句
我们已经在本教程之前的章节中见到过 break 语句。它用于跳出 switch() 语句。
break 语句可用于跳出循环。
break 语句跳出循环后,会继续执行该循环之后的代码(如果有的话):
实例
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<p>点击按钮,测试带有 break 语句的循环。</p>
<button οnclick="myFunction()">点击这里</button>
<p id="demo"></p>
<script>
function myFunction(){
var x="",i=0;
for (i=0;i<10;i++){
if (i==3){
break;
}
x=x + "该数字为 " + i + "<br>";
}
document.getElementById("demo").innerHTML=x;
}
</script>
</body>
</html>
点击按钮,测试带有 break 语句的循环。
点击这里
由于这个 if 语句只有一行代码,所以可以省略花括号:
for (i=0;i<10;i++) { if (i==3) break; x=x + "The number is " + i + "<br>"; }
continue 语句
continue 语句中断循环中的迭代,如果出现了指定的条件,然后继续循环中的下一个迭代。 以下例子在值为 3 时,直接跳过:
for 实例
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>菜鸟教程(runoob.com)</title>
</head>
<body>
<p>点击下面的按钮来执行循环,该循环会跳过 i=3 的数字。</p>
<button οnclick="myFunction()">点击这里</button>
<p id="demo"></p>
<script>
function myFunction(){
var x="",i=0;
for (i=0;i<10;i++){
if (i==3){
continue;
}
x=x + "该数字为 " + i + "<br>";
}
document.getElementById("demo").innerHTML=x;
}
</script>
</body>
</html>
点击下面的按钮来执行循环,该循环会跳过 i=3 的数字。
点击这里
while 实例
while (i < 10){ if (i == 3){ i++; //加入i++不会进入死循环 continue; } x= x + "该数字为 " + i + "<br>"; i++; }
JavaScript 标签
正如您在 switch 语句那一章中看到的,可以对 JavaScript 语句进行标记。
如需标记 JavaScript 语句,请在语句之前加上冒号:
label: statements
break 和 continue 语句仅仅是能够跳出代码块的语句。
语法:
break labelname; continue labelname;
continue 语句(带有或不带标签引用)只能用在循环中。
break 语句(不带标签引用),只能用在循环或 switch 中。
通过标签引用,break 语句可用于跳出任何 JavaScript 代码块:
实例
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<script>
cars=["BMW","Volvo","Saab","Ford"];
list:{
document.write(cars[0] + "<br>");
document.write(cars[1] + "<br>");
document.write(cars[2] + "<br>");
break list;
document.write(cars[3] + "<br>");
document.write(cars[4] + "<br>");
document.write(cars[5] + "<br>");
}
</script>
</body>
</html>
结果:BMW
Volvo
Saab
关于 JavaScript 标签与 break 和 continue 一起使用的理解。
break 的作用是跳出代码块, 所以 break 可以使用于循环和 switch 等
continue 的作用是进入下一个迭代, 所以 continue 只能用于循环的代码块。
代码块: 基本上是{}大括号之间
然后:
1. 默认标签的情况(除了默认标签情况,其他时候必须要有名标签,否则会有惊喜)
当 break 和 continue 同时用于循环时,没有加标签,此时默认标签为当前"循环"的代码块。
当 break 用于 switch 时,默认标签为当前的 switch 代码块:
有名标签的情况
cars=["BMW","Volvo","Saab","Ford"];
list:{
document.write(cars[0] + "");
document.write(cars[1] + "");
document.write(cars[2] + "");
break list;
document.write(cars[3] + "");
document.write(cars[4] + "");
document.write(cars[5] + "");}
上述break list;会跳出list的代码块。如果将break换成continue会有惊喜,违反了明确中的第二点,因为list只是个普通代码块,而不是循环。除非list写成如下形式 list:
for(var i=0; i<10; ++i){
continue list;}
有了标签,可以使用break和continue在多层循环的时候控制外层循环。
例如下面:
outerloop:for (var i = 0; i < 10; i++){
innerloop:
for (var j = 0; j < 10; j++)
{
if (j > 3)
{
break;
}
if (i == 2)
{
break innerloop;
}
if (i == 4)
{
break outerloop;
}
document.write("i=" + i + " j=" + j + "");
}}
JavaScript Array(数组) 对象
数组对象的作用是:使用单独的变量名来存储一系列的值。
var mycars = new Array();
mycars[0] = "Saab";
mycars[1] = "Volvo";
mycars[2] = "BMW";
什么是数组?
数组对象是使用单独的变量名来存储一系列的值。
如果你有一组数据(例如:车名字),存在单独变量如下所示:
var car1="Saab";
var car2="Volvo";
var car3="BMW";
然而,如果你想从中找出某一辆车?并且不是3辆,而是300辆呢?这将不是一件容易的事!
最好的方法就是用数组。
数组可以用一个变量名存储所有的值,并且可以用变量名访问任何一个值。
数组中的每个元素都有自己的的ID,以便它可以很容易地被访问到。
创建一个数组
创建一个数组,有三种方法。
下面的代码定义了一个名为 myCars的数组对象:
1: 常规方式:
var myCars=new Array();
myCars[0]="Saab";
myCars[1]="Volvo";
myCars[2]="BMW";
2: 简洁方式:
var myCars=new Array("Saab","Volvo","BMW");
3: 字面:
var myCars=["Saab","Volvo","BMW"];
访问数组
通过指定数组名以及索引号码,你可以访问某个特定的元素。
以下实例可以访问myCars数组的第一个值:
var name=myCars[0];
以下实例修改了数组 myCars 的第一个元素:
myCars[0]="Opel";
[0] 是数组的第一个元素。[1] 是数组的第二个元素。 |
在一个数组中你可以有不同的对象
所有的JavaScript变量都是对象。数组元素是对象。函数是对象。
因此,你可以在数组中有不同的变量类型。
你可以在一个数组中包含对象元素、函数、数组:
myArray[0]=Date.now;
myArray[1]=myFunction;
myArray[2]=myCars;
数组方法和属性
使用数组对象预定义属性和方法:
var x=myCars.length // myCars 中元素的数量
var y=myCars.indexOf("Volvo") // "Volvo" 值的索引值
完整的数组对象参考手册
你可以参考本站关于数组的所有属性和方法的完整参考手册。
参考手册包含了所有属性和方法的描述(和更多的例子)。
创建新方法
原型是JavaScript全局构造函数。它可以构建新Javascript对象的属性和方法。
实例:创建一个新的方法。
Array.prototype.myUcase=function(){
for (i=0;i<this.length;i++){
this[i]=this[i].toUpperCase();
}
}
数组去重
第一种:遍历数组法
这种方法最简单最直观,也最容易理解,代码如下:
1 var arr = [2, 8, 5, 0, 5, 2, 6, 7, 2]2 var newArr = []3 for (var i = 0; i < arr.length; i++) {4 if (newArr.indexOf(arr[i]) === -1) {5 newArr.push(arr[i])6 }7 }8 console.log(newArr) // 结果:[2, 8, 5, 0, 6, 7]
这种方法很好理解,利用了indexOf()方法(indexOf()方法如果查询到则返回查询到的第一个结果在数组中的索引,如果查询不到则返回-1)。先创建一个新的空数组用来存储新的去重的数组,然后遍历arr数组,在遍历过程中,分别判断newArr数组里面是不是有遍历到的arr中的元素,如果没有,直接添加进newArr中,如果已经有了(重复),那么不操作,那么从头到尾遍历一遍,正好达到了去重的目的。
第二种:数组下标判断法
这种方法也比较好理解,代码如下:
1 var arr = [2, 8, 5, 0, 5, 2, 6, 7, 2]2 var newArr = []3 for (var i = 0; i < arr.length; i++) {4 if (arr.indexOf(arr[i]) === i) {5 newArr.push(arr[i])6 }7 }8 console.log(newArr) // 结果:[2, 8, 5, 0, 6, 7]
这和第一种方法有重叠,不说多余的,直接看if这里,在遍历arr的过程中,如果在arr数组里面找当前的值,返回的索引等于当前的循环里面的i的话,那么证明这个值是第一次出现,所以推入到新数组里面,如果后面又遍历到了一个出现过的值,那也不会返回它的索引,indexof()方法只返回找到的第一个值的索引,所以重复的都会被pass掉,只出现一次的值都被存入新数组中,也达到了去重的目的。
第三种:排序后相邻去除法
这种方法用到了sort()方法,代码如下:
1 var arr = [2, 8, 5, 0, 5, 2, 6, 7, 2]2 arr.sort()3 var newArr = [arr[0]]4 for (var i = 1; i < arr.length; i++) {5 if (arr[i] !== newArr[newArr.length - 1]) {6 newArr.push(arr[i])7 }8 }9 console.log(newArr) // 结果:[0, 2, 5, 6, 7, 8]
这种方法的思路是:先用sort()方法把arr排序,那么排完序后,相同的一定是挨在一起的,把它去掉就好了,首先给新数组初始化一个arr[0],因为我们要用它和arr数组进行比较,所以,for循环里面i也是从1开始了,我们让遍历到的arr中的值和新数组最后一位进行比较,如果相等,则pass掉,不相等的,push进来,因为数组重新排序了,重复的都挨在一起,那么这就保证了重复的这几个值只有第一个会被push进来,其余的都和新数组的被push进来的这个元素相等,会被pass掉,也达到了去重的效果。
第四种:优化的遍历数组法
1 var arr = [2, 8, 5, 0, 5, 2, 6, 7, 2, 8]
2 var newArr = []
3 for (var i = 0; i < arr.length; i++) {
4 for (var j = i + 1; j < arr.length; j++) {
5 if (arr[i] === arr[j]) {
6 i++
7 j = i
8 }
9 }10 newArr.push(arr[i])11 }12 console.log(newArr) // 结果:[0, 5, 6, 7, 2, 8]
思路:两层for循环,外面一层是控制遍历到的前一个arr中的元素,里面一层控制的是第一层访问到的元素后面的元素,不断的从第0个开始,让第0个和他后面的元素比较,如果没有和这个元素相等的,则证明没有重复,推入到新数组中存储起来,如果有和这个元素相等的,则pass掉它,直接进入下一次循环。从第1个开始,继续和它后面的元素进行比较,同上进行,一直循环到最后就是:不重复的都被推入新数组里面了,而重复的前面的元素被pass掉了,只留下了最后面的一个元素,这个时候也就不重复了,则推入新数组,过滤掉了所有重复的元素,达到了去重的目的。
第五种:数组遍历法
1 var arr = ['a', 'a', 'b', 'c', 'b', 'd', 'e', 'a']
2 var newArr = []
3 for (var i = 0; i < arr.length; i++) {
4 var bl = true
5 for (var j = 0; j < newArr.length; j++) {
6 if (arr[i] === newArr[j]) {
7 bl = false
8 break
9 }10 }11 if (bl) {12 newArr.push(arr[i])13 }14 }15 console.log(newArr) // 结果:["a", "b", "c", "d", "e"]
思路:也是两层for循环,外层for循环控制的是arr数组的遍历,内层for循环控制的是新数组的遍历,从第0位开始,如果新数组中没有这个arr数组中遍历到的这个元素,那么状态变量bl的值还是true,那么自然进入到了if中把这个值推入到新数组中,如果有这个元素,那么代表重复,则把状态变量bl取值改为false,并且跳出当前循环,不会进入到if内部,而进入下一次外层开始的循环。这样循环往复,最后也达到了去重的效果。
比较数组中数值的大小是比较常见的操作,比较大小的方法有多种,比如可以使用自带的sort()函数,下面来介绍如下几种方法,代码如下:
方法一:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | //最小值 Array.prototype.min = function() { var min = this[0]; var len = this.length; for (var i = 1; i < len; i++){ if (this[i] < min){ min = this[i]; } } return min; } //最大值 Array.prototype.max = function() { var max = this[0]; var len = this.length; for (var i = 1; i < len; i++){ if (this[i] > max) { max = this[i]; } } return max; } |
如果你是引入类库进行开发,害怕类库也实现了同名的原型方法,可以在生成函数之前进行重名判断:
1 2 3 4 5 | if (typeof Array.prototype['max'] == 'undefined') { Array.prototype.max = function() { ... ... } } |
方法二:
用Math.max和Math.min方法可以迅速得到结果。apply能让一个方法指定调用对象与传入参数,并且传入参数是以数组形式组织的。恰恰现在有一个方法叫Math.max,调用对象为Math,与多个参数
1 2 3 4 5 6 | Array.max = function( array ){ return Math.max.apply( Math, array ); }; Array.min = function( array ){ return Math.min.apply( Math, array ); }; |
但是,John Resig是把它们做成Math对象的静态方法,不能使用大神最爱用的链式调用了。但这方法还能更精简一些,不要忘记,Math对象也是一个对象,我们用对象的字面量来写,又可以省几个比特了。
1 2 3 4 5 6 7 8 | Array.prototype.max = function(){ return Math.max.apply({},this) } Array.prototype.min = function(){ return Math.min.apply({},this) } [1,2,3].max()// => 3 [1,2,3].min()// => 1 |
方法三:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | function getMaximin(arr,maximin) { if(maximin=="max") { return Math.max.apply(Math,arr); } else if(maximin=="min") { return Math.min.apply(Math, arr); } } var a=[3,2,4,2,10]; var b=[12,4,45,786,9,78]; console.log(getMaximin(a,"max"));//10 console.log(getMaximin(b,"min"));//04 |
方法四:
1 2 3 | var a=[1,2,3,5]; alert(Math.max.apply(null, a));//最大值 alert(Math.min.apply(null, a));//最小值 |
多维数组可以这么修改:
1 2 3 4 | var a=[1,2,3,[5,6],[1,4,8]]; var ta=a.join(",").split(",");//转化为一维数组 alert(Math.max.apply(null,ta));//最大值 alert(Math.min.apply(null,ta));//最小值 |
函数的定义
JavaScript三种定义函数方法:
*第一种 使用function语句定义函数
myFunction(5);function myFunction(y) {
return y * y;}
第二种 使用Function()构造函数来定义函数(不常用)
var 函数名 = new Function(“参数1”,”参数2”,”参数3”……”函数体”);
如:var 函数名 = new Function("x","y","var z=x+y;return z;");
*第三种 函数表达式
JavaScript 函数可以通过一个表达式定义。
函数表达式可以存储在变量中:
var 函数名 = function(参数1,参数2,…){函数体};//例如://定义
var add = function(a,b){
return a+b;
}
//调用函数
document.write(add(50,20));
第四种 自调用函数
函数表达式可以 "自调用"。
自调用表达式会自动调用。
如果表达式后面紧跟 () ,则会自动调用。
不能自调用声明的函数。
通过添加括号,来说明它是一个函数表达式:
实例:
(function () {
var x = "Hello!!"; // 我将调用自己})();
注意:
arguments 对象
在函数代码中,使用特殊对象 arguments,开发者无需明确指出参数名,就能访问它们。
例如,在函数 sayHi() 中,第一个参数是 message。用 arguments[0]
也可以访问这个值,即第一个参数的值(第一个参数位于位置 0,
第二个参数位于位置 1,依此类推)。
例如:arguments
x = sumAll(1, 123, 500, 115, 44, 88);
function sumAll() {
var i, sum = 0;
for (i = 0; i < arguments.length; i++) {
sum += arguments[i];
}
return sum;}
关于变量和参数问题:
1. 函数外面定义的变量是全局变量,函数内可以直接使用
2. 在函数内部没有使用var定义的=变量则为全局变量
3. 在函数内使用var关键字定义的变量是局部变量,即出了函数外边无法获取。
函数支持默认值
4. 在函数内部定义的函数为局部函数 在函数外部不能获取到
二 JavaScript 闭包
JavaScript 变量可以是局部变量或全局变量。
私有变量可以用到闭包。
函数内部可以修改函数外部的全局变量
计数器困境
设想下如果你想统计一些数值,且该计数器在所有函数中都是可用的。
你可以使用全局变量,函数设置计数器递增:
var counter = 0;
function add() {
return counter += 1;}
add();add();add();
// 计数器现在为 3
但问题来了,页面上的任何脚本都能改变计数器,即便没有调用 add() 函数。
如果我在函数内声明计数器,如果没有调用函数将无法修改计数器的值:
function add() {
var counter = 0;
return counter += 1;}
add();add();add();// 本意是想输出 3, 但事与愿违,输出的都是 1 !
JavaScript 内嵌函数
所有函数都能访问全局变量。
实际上,在 JavaScript 中,所有函数都能访问它们上一层的作用域。
JavaScript 支持嵌套函数。嵌套函数可以访问上一层的函数变量。
该实例中,内嵌函数 plus() 可以访问父函数的 counter 变量:
实例:
function add() {
var counter = 0;
function plus() {counter += 1;}
plus();
return counter; }
JavaScript 闭包
var add = (function () {
var counter = 0;
return function () {return counter += 1;}})();
add();add();add();
// 计数器为 3
实例解析
变量 add 指定了函数自我调用的返回字值。
自我调用函数只执行一次。设置计数器为 0。并返回函数表达式。
add变量可以作为一个函数使用。非常棒的部分是它可以访问函数上一层作用域的计数器。
这个叫作 JavaScript 闭包。它使得函数拥有私有变量变成可能。
计数器受匿名函数的作用域保护,只能通过 add 方法修改
JavaScript 函数参数
JavaScript 函数对参数的值没有进行任何的检查。
函数显式参数(Parameters)与隐式参数(Arguments)
在先前的教程中,我们已经学习了函数的显式参数:
functionName(parameter1, parameter2, parameter3) { // 要执行的代码…… }
函数显式参数在函数定义时列出。
函数隐式参数在函数调用时传递给函数真正的值。
参数规则
JavaScript 函数定义显式参数时没有指定数据类型。
JavaScript 函数对隐式参数没有进行类型检测。
JavaScript 函数对隐式参数的个数没有进行检测。
默认
ES5 中如果函数在调用时未提供隐式参数,参数会默认设置为: undefined
有时这是可以接受的,但是建议最好为参数设置一个默认值:
实例(ES5)
function myFunction(x, y) { if (y === undefined) { y = 0; } }
或者,更简单的方式:
实例(ES5)
function myFunction(x, y) { y = y || 0; }
如果y已经定义 , y || 返回 y, 因为 y 是 true, 否则返回 0, 因为 undefined 为 false。 |
如果函数调用时设置了过多的参数,参数将无法被引用,因为无法找到对应的参数名。 只能使用 arguments 对象来调用。
ES6 函数可以自带参数
ES6 支持函数带有默认参数,就判断 undefined 和 || 的操作:
实例(ES6)
function myFunction(x, y = 10) { // y is 10 if not passed or undefined return x + y; } myFunction(0, 2) // 输出 2 myFunction(5); // 输出 15, y 参数的默认值
arguments 对象
JavaScript 函数有个内置的对象 arguments 对象。
argument 对象包含了函数调用的参数数组。
通过这种方式你可以很方便的找到最大的一个参数的值:
实例
x = findMax(1, 123, 500, 115, 44, 88); function findMax() { var i, max = arguments[0]; if(arguments.length < 2) return max; for (i = 0; i < arguments.length; i++) { if (arguments[i] > max) { max = arguments[i]; } } return max; }
或者创建一个函数用来统计所有数值的和:
实例
x = sumAll(1, 123, 500, 115, 44, 88); function sumAll() { var i, sum = 0; for (i = 0; i < arguments.length; i++) { sum += arguments[i]; } return sum; }
通过值传递参数
在函数中调用的参数是函数的隐式参数。
JavaScript 隐式参数通过值来传递:函数仅仅只是获取值。
如果函数修改参数的值,不会修改显式参数的初始值(在函数外定义)。
隐式参数的改变在函数外是不可见的。
通过对象传递参数
在JavaScript中,可以引用对象的值。
因此我们在函数内部修改对象的属性就会修改其初始的值。
修改对象属性可作用于函数外部(全局变量)。
修改对象属性在函数外是可见的。
JS冒泡排序
原理
依次比较相邻的两个值,如果后面的比前面的小,则将小的元素排到前面。依照这个规则进行多次并且递减的迭代,直到顺序正确。
时间复杂度,空间复杂度,稳定性
- 平均时间复杂度O(n*n)
- 最好情况O(n)
- 最差情况O(n*n)
- 空间复杂度O(1)
- 稳定性:稳定
冒泡排序的写法
var examplearr=[8,94,15,88,55,76,21,39];function sortarr(arr){
for(i=0;i<arr.length-1;i++){
for(j=0;j<arr.length-1-i;j++){
if(arr[j]>arr[j+1]){
var temp=arr[j];
arr[j]=arr[j+1];
arr[j+1]=temp;
}
}
}
return arr;
}
sortarr(examplearr);
console.log(examplearr);
解析
两个循环
当i=0的时候,里面的循环完整执行,从j=0执行到j=6,这也就是第一遍排序,结果是将最大的数排到了最后,这一遍循环结束后的结果应该是[8,15,88,55,76,21,39,94]
当i=1的时候,里面的循环再次完整执行,由于最大的数已经在最后了,没有必要去比较数组的最后两项,这也是j<arr.length-1-i的巧妙之处,结果是[8,15,55,76,21,39,88,94]
说到这里,规律就清楚了,每次将剩下数组里面最大的一个数排到最后面,当第一个循环执行到最后的时候,也就是i=6,此时,j=0,只需要比较数组的第一和第二项,比较完毕,返回。