案例:菱形的输出
分析:有多种思考方式,但最关键的问题是获得空格与行号的关系,星号与行号的关系。第一种:行号为正三角行的行数,总行数实际上是2row-1
<script>
var row = Number(prompt("请输入行数(奇数)"));
for(var i=1; i<=2*row-1; i++) {
var blanks = Math.abs(row - i);//空格的个数
var stars = 2 * row - 1 - 2 * blanks;//星号个数
for (var j = 1; j <= blanks; j++) {
document.write(" ");
}
for (var k = 1; k <= stars; k++) {
document.write("*");
}
document.write("<br>");
}
</script>
这里的思路是空格与星号有关系,因为列数是固定的。
另外一种菱形的行数与实际行数相同,使用函数
var blanks = Math.abs((row+1)/2 - i);//空格的个数
var stars = row - 2 * Math.abs(i - (row+1)/2);//星号个数
函数
6.1函数
为什么需要用函数?
首先看看输出100以内所有素数问题的解决方案
方案一:循环的嵌套
for(var i=2; i<=100; i++){
var flag = true;
var m = Math.ceil(Math.sqrt(i));//根据相关数学定理
if(i>2){
for (var j = 2; j <= m; j++) {
if (i % j == 0) {
flag = false;
}
}
}
if(flag){
document.write(i+”<br>”);
}
}
这种解决方案程序比较复杂,阅读也比较困难,需要比较高超的技术。
方案二:运用函数
<script>
/*
* 假设系统已经提供了一个判断素数的函数isPrime(x),那么问题是不是就特别简单
* */
for(var i=2; i<=100; i++){
if(isPrime(i)){
document.write(i+"<br>");
}
}
function isPrime(x){
var m = Math.ceil(Math.sqrt(x));//根据相关数学定理
If(x>2){
for (var i = 2; i <= m; i++) {
if (x % i == 0) {
return false;
}
}
}
return true;
}
</script>
这种解决方案是将一个比较复杂的问题分解为两个较为简单的问题去解决,实际上是用“量”去克服“难”和“大”的问题。也就是“大事化小”。
这种解决方案给我们提供了一个解决规模大、难度高的问题的解决思路:将它分解为多个规模相对较小、难度相对较低的问题去解决,如果分解后的问题仍然规模到或者难度高,可以按照这个思路一种分解下去,直到分解后的问题足够小、简单。
6.2定义与调用
函数是什么,怎么用?
定义:
function isPrime(n) {
……
}
function定义函数的关键字
isPrime是函数的名字,和变量名一样的命名规则和原则
n形式参数(形参)
isPrime(12),12就是实际参数(实参)
函数头部:体现的是函数的设计
函数体:体现的是函数的实现过程
设计比实现更重要
案例:验证100以内的数都符合角谷定理
<script>
var flag = true;
for(var n=2; n<=100; n++){
if(!isJiaogu(n)){
flag = false;
break;
}
}
alert("角谷定理验证"+(flag?"成功":"失败"));
function isJiaogu(n){
while(n != 1){
if(n%2 == 0){
n /= 2;
}else{
n = n*3+1;
}
}
return true;
}
</script>
功能:判断一个数是否符合角谷定理
名称:isJiaogu
输入参数:待判断的数
输出结果:true/false
案例:验证10000以内哥德巴赫猜想成立
<script>
var flag = true;
for(var i=8; i<=10000; i+=2){
if(!canSplit(i)){
flag = false;
break;
}
}
alert("哥德巴赫猜想验证"+(flag?"成功":"失败"));
function canSplit(n){
for(var a=2; a<=n/2; a++){
if(isPrime(a) && isPrime(n-a)){
return true;
}
}
return false;
}
function isPrime(x){
var m = Math.ceil(Math.sqrt(x));//根据相关数学定理
if(x>2){
for (var i = 2; i <= m; i++) {
if (x % i == 0) {
return false;
}
}
}
return true;
}
</script>
假设系统有一个函数能帮我们判断大于6的偶数能否分解的
设计一下该函数
功能:判断一个数能否分解为两个素数之和
名称:canSplit
输入参数:待分解的数
返回结果:true/false
那么如何实现这个函数呢,好像还是不够简单,那就继续分解
如果系统有一个能判断素数的函数,那么这个问题也简单
设计
功能:判断一个数是否为素数
名称:isPrime
输入参数:待判断的数
输出结果:true/false
函数的本质:直观理解就是实现某个独立功能的代码段,或者说它就是一个数据加工的黑箱子
输入参数 输出结果
所谓“黑箱子”,就是我们只关心外面的东西,比方说它是干啥的,需要输入什么,可以得到什么结果,而不关心里面是怎么工作的。
忽略实现细节
6.3参数传递
所谓参数传递,就是将实参的值传递给形参。通过调试可以确定形参在函数被调用之前是不存在的,当函数被调用的那一刻,形参被创建,并且把实参的值传递给形参。
参数传递有两种方式:值传递和引用传递
<script>
var a = 5;
increase(a);
alert(a);
function increase(x){
x++;
}
</script>
a的值并没有显示预期中的6,还是5,。因为形参x和实参a是两个不同的变量,x的变化和a没有任何关系。
<script>
/*引用传递*/
var a = new Object();
a.value = 5;
increase(a);
alert(a.value);
function increase(x){
x.value++;
}
</script>
a.value没有被显示修改,但是a.value确实是加1了,因为x就是a,或者说x是a的别名,专业一点就叫引用。
常规类型的参数采用的是值的残敌,比如Number、String、Boolean
对象类型采用的是引用传递,Object。
如果希望把参数从函数中带出来,但是函数的返回值只有一个
6.4变量的作用域
局部变量:在函数内部定义的变量,这个变量只能够在函数的内部使用,在全局中不能够使用。比如在三国时期,袁术称帝后,只有袁术阵营的人才认为他是皇帝,他发布的命令只有在本阵营里面起作用。但是其他人不认为他是皇帝,他发布的命令没有作用。
function LocalVar(){
var a = 1;
alert(a);
}
LocalVar();
alert(a);
全局变量:在函数外部定义的变量,这个变量可以在全局进行使用。比如汉朝的皇帝发布了命令,那么不管是袁绍还是曹操都要听这个命令。
vara = 1;
function allVar(){
alert(a);
}
allVar();
冲突处理原则:就近原则。
当函数中定义了一个和全局变量名相同的变量,此时在函数中在定义前使用,那么这个变量还是函数中的变量,为undefined,不使用全局变量。
要理解就近原则,而不是从上到下。
vara = 1;
function doubleVar(){
var a= 2;
var a= 3;
alert(a);
}
局部和全局同时定义了一个相同的名字的变量,如何在局部里面访问全局变量?在局部中给变量加上window的前缀,就可以访问到全局的变量。
<script>
var a = 1;
function allVar(){
var a = 2;
var a = 3;
//alert(a);
alert(window.a);
}
allVar();
</script>
注意:在函数内部定义一个变量,如果没有加上var,那么这个变量被认作为全局变量。
<script>
function LocalAllVar(){
a = 1;
}
function test(){
alert(a);
}
LocalAllVar();
test();
alert(a);
</script>