js 函数

函数

命名函数创建

  • function fn(a,b){
    console.log(a+b);
    }
    fn(3,4);
  • 在当前函数在script标签中创建时,优先放置函数在堆中,函数名放在栈中。
  • 当前代码所在script标签上面的script的标签中任意函数和全局变量都是可以调用的,但是其下面script标签中的就不能调用了。
  • 函数名:驼峰式命名法,首字母一般小写;如果是构造函数,首字母大写。
  • 函数名后的()执行当前函数需要传入的参数。
  • { } 里面是函数的语句块,执行函数时该语句块被执行。
  • 尽量不要函数嵌套函数。

匿名函数

  • 定义匿名函数
    var fn = function(){
    //代码运行到定义匿名函数这行后才可以调用该匿名函数
    }

  • 自执行函数,定义就执行
    执行完,就会立即被销毁。

  • 自执行函数的两种形式:
    (function(){
    console.log(“i am number one”);
    })();
    //W3C推荐下面这一款~
    (function(){
    console.log(“i am number two”);
    }());
    包括自执行函数,函数都有执行上下文AO,都要经历预编译。

()为执行符号,只有表达式才能被执行符号执行,函数一旦被执行符号执行,就会忽略函数名。
()也算是数学符号,可以把函数变成表达式,所以(function (){}());这就是自执行函数了。

构造函数 定义函数

  • var fn = new Function(“a”,“b”,“console.log(a+b)”);
    fn(3,5);
  • 运行速度缓慢。但灵活。
  • 动态构建函数。

作用域

  • 函数外定义的变量,函数中可以调用。
    函数内定义的变量,不能被函数外部调用。
    var 定义变量,如果函数中没有用var定义变量,直接使用该变量,则此变量为全局变量。
    window.a的window可以省略,但a还是全局变量
    function fn(){a = 10;}此函数内外没有使用var a = 10;所以,a是window下的属性(ES6中是不允许不定义变量就使用)。
    在函数中任意位置使用var定义的变量,在该函数的任意位置都认为该变量是局部变量。
    有局部变量时,先调用局部变量,没有局部变量才带哦用全局变量。
    参数就是局部变量。
	var a = 1;   //函数外定义的变量a
	var d = 4;   //全局变量
	function fn(e) {
	    console.log("a=" + a);   //a=1 访问函数外定义的变量a
	    var b = 2;   //函数内定义的变量
	    c = 3;   //直接使用该变量,c为全局变量
	    console.log("d=" + d);   //d=undefined,函数任意位置使用var定义了的变量,在次函数中,都认为是局部变量,此时系统认为d未赋值
	    var d = 44;
	    console.log("d=" + d);   //d=44,函数中有局部变量时,先调用局部变量d;没有局部变量,才会像a一样,调用全局变量
	    console.log("e="+e);   //e=6 参数也是局部变量
	    var e=66;   //相当于重新定义了一个局部变量e
	    console.log("e="+e);   //e=66 
	}
	fn(6);
	//    console.log("b="+b);    //报错:b is not defined;说明函数内定义的变量,函数外不能直接访问
	console.log("c=" + c);    //c=3 c未被定义而直接使用,意味着在直接在window中增加属性,乃window.c,不过是window被省略了
  • 补充:
    局部变量:函数内用var定义的变量。
    全局变量:函数外用var定义的变量;函数内不使用var,直接给变量赋值,相当于给window增加一个属性,此属性也是全局变量。
    局部变量 > 全局变量(权重)。
    this = window (全局作用域window)。
    函数运行完成后,局部变量就会被销毁。

作用域属于一个函数,一个函数产生一个作用域。
[[scope]]:代表作用域。
每个js函数都是一个对象,作用域是不能访问的属性,仅供js引擎存取。
[[scope]]指的是我们所说的作用域,其中存储了运行期上下文的集合。
(运行期上下文AO,一个函数每调用一次,就会产生一个执行期上下文AO,当函数执行完毕,执行期上下文AO就会被销毁)

  • 示例
1、判断a、b的作用域
	var a = "a=1";
	function fn1() {
	    var a = "a=11";
	    function fn2() {
	        var b = "b=2";
	        var a = "a=111";
	        console.log(a);   //a=111;可调用自身的变量
	    }
	    fn2();
	    // console.log(b);   //错误:b is not defined;外部不能调用函数fn2中的变量
	}   
	fn1();

2、地狱b模式
// 函数预先放入堆中;函数b,外部变量b都是全局变量,下面b="b=2"还没有运行,所以在这里全局变量就是函数b,我们把函数b当前参数填入;
	b(b);   //ƒ b(b){console.log(b);var b="b=22";} 打印的是函数b
	var b="b=2";   //此时外部变量b覆盖函数b,所以此时b不在代表函数b
	console.log(b);   //b=2
	function b(b){
	    console.log(b);
	    var b="b=22";
	}
	b(b);   //错误:b is not a function

参数

  • 实参与形参一一对应,如果没有对应填入,该形参即为undefined。

function fn(a,b){
console.log(a+b);
}
fn(3,5);

  • 填入参数 == 给参数赋值(相当于)。
  • js是弱类型语言,so,参数不能强制约定类型。
  • ES5中不允许设置参数的默认值。ES6可以。

function(a,b=3){
console.log(a+b);
//就是这种,ES5报错,ES6算出结果
}
fn(2);

  • 对象作为参数
	function fn(obj){
	  // 相当于给obj赋值一个o,obj和o的引用地址相同
	  // 当修改obj的属性时,全局的o属性也会被修改
	  obj.a=10;
	}
	var o={a:1};
	fn(o);
	
	function fn(obj){
	  // 重新设置了obj的新的引用地址
	  obj={
	      a:3
	  }
	}
	var o={a:1};
	fn(o); 
  • 如果参数的数量不确定,就不设置参数(ES5);

    function getMaxValue(){
    console.log(arguments);//可以打印出当前传入的实参,ES5以上版本尽量少用;
    }
    getMaxValue(1,2,3,4,5);//Arguments(5) [1, 2, 3, 4, 5, callee: ƒ, Symbol(Symbol.iterator): ƒ]

    arguments参数集 (有 伪数组 之称);
    arguments.callee 函数本身;用于匿名函数,调用自身函数;
    arguments.callee.caller 调用当前函数的环境函数。

    示例:没有指定参数个数和大小的情况下,比较大小,选出最大的
    
    function getMaxValue(){
        //尽量少用if else语句,多些单条件判断语句即可
        if(arguments.length===0) return;   //如果没有可比较的参数,就不用比了
        var max=arguments[0];   //从第一个开始判断
        for(var i=0;i<arguments.length;i++){
            max=max>arguments[i]?max:arguments[i];
        }
        console.log("max="+max);
    }
    getMaxValue(4,2,5,7,43,0);   //max=43
    
  • 临时变量(也是局部变量),临时变量一般用 _variable (下划线开头),一般用在函数参数里。

  • 堆栈溢出:递归或者回调的次数过多,没有阻止递归或者回调的条件;要避免这种情况。

回调函数

  • 参数调入函数,执行别的函数
1、回调函数-基础
	function fn(f){
	    var a=1;
	    f(a); //以函数为参数,执行
	}
	function fn2(a){
	    console.log(a+100);
	}
	fn(fn2);   //101

2、用计时器来回调函数
	i=0;
	function callBack(){
	    console.log("i="+i);
	    i++;
	}
	setInterval(callBack,1000);   //用计时器每隔1s执行一次;结果是,i从0开始,每1s加1
  • 示例:信号灯的变化,功能简单,就三个灯的顺序更迭
	function getLight(first,second,third){
	    first(second,third);
	}
	function getRedLight(fn,fn1){
	    var f=arguments.callee;   //被调用者,此处指函数getRedLight
	    var ids=setTimeout(function(){
	        console.log("red");
	        clearTimeout(ids);   //清除计时器,用一次清一次,以免内存泄漏
	        fn(fn1,f);   //执行回调函数
	    },1000);   //每个1000ms即1s更迭一次
	}
	function getYellowLight(fn,fn1){
	    var f=arguments.callee;  
	    var ids=setTimeout(function(){
	        console.log("yellow");
	        clearTimeout(ids);   
	        fn(fn1,f);  
	    },1000);
	}
	function getGreenLight(fn,fn1){
	    var f=arguments.callee;  
	    var ids=setTimeout(function(){
	        console.log("green");
	        clearTimeout(ids);   
	        fn(fn1,f);  
	    },1000);
	}
	getLight(getRedLight,getYellowLight,getGreenLight);

递归

  • 单函数递归:执行自己
1、调用自己
	 var i=0;
     function fn(){
         i++;
         if(i<10) fn();
     }
     fn();
     console.log("i="+i);//i=10

2、匿名函数中调用自身
     var j=0;
     (function(){
         j++;
         if(j<10) arguments.callee();
     })();
     console.log("j="+j);//j=10
  • 双函数递归:arguments.callee与arguments.callee.caller可以完成双函数递归。
 	var y=0;
    function fn1(f){
        console.log("aaa");
        f();
    }
    function fn3(f){
        console.log("ccc");
        f();
    }
    function fn2(f){
        // arguments.callee.caller 当前函数的环境函数,调用当前函数的函数
        console.log("bbb");
        y++;
        if(y<10)arguments.callee.caller(arguments.callee);
    }
    fn3(fn2);   //会不断打印ccc bbb,直到不再满足y<10
    //但是耦合度太高,不建议使用

注意:有一“随机块 随机颜色 随机长度 随机排列”的示例在20191228的随堂练习及课后作业有展示,可自行参考。

  • 示例:递归在对象中的应用
    var obj = {
        a: 1,
        b: 1,
        c: {
            a: 3,
            b: 4,
            c: {
                a: 5,
                b: 6,
                c: {
                    a: 7,
                    b: 8,
                }
            }
        }
    }

1、遍历对象中的元素一般用for in来遍历; 浅复制
    var obj1 = {};
    for(var prop in obj){
        obj1[prop]=obj[prop];
    }
    obj.a=10;   //obj1与obj为不同对象
    obj.c.a=1000;   //obj1与obj中存入属性c对象的引用地址一致,故,一个变,皆变
    console.log(obj1,obj);

2、用递归来遍历,深复制
    function cloneObj(target, source) {
        for (var prop in source) {
            //每次将属性为对象的分离出来处理
            if (typeof source[prop] === "object" && source[prop] !== null) {
                target[prop] = {};
                cloneObj(target[prop], source[prop]);
            } else {
                target[prop] = source[prop];
            }
        }
        return target;
    }
    cloneObj(obj1,obj);
    obj.a=10; 
    obj.c.a=1000;  
    console.log(obj1,obj);   //只有obj里的内容改变,说明此时二者存入的c的引用地址亦不相同了

3、同样的属性 使用**引用关系**的方式深度遍历,深复制
    function cloneObj(target, source) {
        for (var prop in source) {
            if (typeof source[prop] !== "object" || source[prop] === null) {
                target[prop] = source[prop];
            }
        }
        // 使用引用关系的方式深度遍历
        var o = source.c;
        target.c = {};
        var o1 = target.c;
        while (o) {
            for (var prop in o) {
                if (typeof o[prop] !== "object" || o[prop] === null) {
                    o1[prop] = o[prop];
                }
            }
            o = o.c;
            if (o) {
                o1.c = {};
                o1 = o1.c;
            }
        }
        return target
    }
    var obj1 = {};
    obj1 = cloneObj(obj1, obj);
    obj.c.c.a = 1000;
    console.log(obj1,obj);   //obj.c.c.a变了,obj1没变
  • 二叉树遍历
	var obj3={};
    function addValue(obj,left,right){
        obj.left={
            value:left
        }
        obj.right={
            value:right
        }
    }
    //依次向二叉树obj3(对象)中添加左右元素
    addValue(obj3,1,2);
    addValue(obj3.left,3,4);
    addValue(obj3.right,5,6);
    addValue(obj3.left.left,7,8);
    addValue(obj3.left.right,9,10);
    addValue(obj3.right.left,11,12);
    addValue(obj3.right.right,13,14);
    console.log(obj3);  //{left: {…}, right: {…}}
    //开始遍历
    function mapTree(obj){
        if(!obj) return;   //直到遍历完,才结束
        if(obj.value) console.log(obj.value);
        //先打印完左边的,再去打印右边的
        mapTree(obj.left);
        mapTree(obj.right);
    }
    mapTree(obj3);

函数 - 多态

  • 代码的复用性高
  • 代码耦合度要尽可能的低
  • 代码解耦:减小代码耦合度

return

return的返回值

  • 阻断作用

function fn(n){
if(n<5) return;
console.log(“aa”);
}
fn(3);

  • 可以允许函数返回一个值,仅一个,但可以用对象、数组返回多个值;
  • 返回函数执行的结果,如果函数中没有return,就会返回 undefined。
  • return分支返回,典例:输入数字,返回汉字。
    var arr=["零","一","二","三","四","五","六","七","八","九"];

   // console.log(arr[5])
   // var str="123";
   // console.log(str[0],str[1],str[2])


   // 5 "五";
   // 10 "十";
   // 15 "十五";
   // 20 "二十";
   // 36 "三十六";
   // 100 "一百";
   // 105 "一百零五";
   // 110 "一百一十";
   // 135 "一百三十五";
   // 350 "三百五十";
   
   function getCNNumber(n){
       if(n<0 || n>1000) return "错误的消息";
       if(n<10) return arr[n];
       if(n===10) return "十";
       if(n<20) return "十"+arr[String(n)[1]];
       if(n>=100 && n%100===0) return arr[String(n)[0]]+"百";
       if(n>100 && n%10===0) return arr[String(n)[0]]+"百"+arr[String(n)[1]]+"十";
       if(n%10===0) return arr[String(n)[0]]+"十";
       if(n<100) return arr[String(n)[0]]+"十"+arr[String(n)[1]];
       if(n%100<10) return arr[String(n)[0]]+"百零"+arr[String(n)[2]];
       return arr[String(n)[0]]+"百"+arr[String(n)[1]]+"十"+arr[String(n)[2]];

   }
   console.log(getCNNumber(175)); 
  • 返回多个值,通过对象返回;
	function fn(w,h){
	//    var perimeter=(w+h)*2;//一般不是都这么写嘛
	 //    var area=w*h;
	    return {
	        perimeter:(w+h)*2,//其实也可以这么写,以对象的形式输出多个值
	        area:w*h
	    }
	}
	console.log(fn(3,9));
  • 如果对象不存在,可以创建一个即o=o||{},不过这种每次执行函数都会创建一个新对象,称这种为工厂模式;单例。
  • 每次执行函数创建一个新对象 工厂模式
	function fn(){
	    var o={};
	    o.a=10;
	    o.b=20;
	    return o;
	}
	console.log(fn()===fn());
  • 单例
var o;
//console.log(o);   //undefined
function fn(){
	//console.log(o);   //{a: 10, b: 20}
    o=o || {};   //对象o不存在就新建一个,若参数有o,或者在函数中另外新定义了,就无需新建。
    o.a=10;
    o.b=20;
    return o;
}
console.log(fn()===fn());   //true
console.log(fn());   //{a: 10, b: 20}
  • 返回函数体
    注意:相关示例请看20191228随堂练习的“函数try”

return break continue 的区别

  • return只能使用在函数中;无视任何内容,直接跳出函数。如果函数最后没有返回值,尽量不要写return。
    return有时在循环中可以替代break。

function fn(){
for(var i=0;i<10;i++) if(i===5) return;
console.log(“a”);
}
fn();

  • break 用于switch或者循环语句中,跳出当前循环或者锚点循环或者switch语句,循环外的语句继续执行。
  • continue 只能用在循环语句中,仅跳出当前次循环,继续下一次循环。

计时器

计时器:setInterval setTimeout ;
一般使用他们的两个参数(执行的函数,间隔的时间);
他俩还有第三个参数,可以给相关函数传入数值;
setInterval是能够间隔一段时间自动再次执行某函数,直到被清除为止;
setTimeout是间隔一段时间后执行,仅执行一次,可以把它放到循环里面;
他俩都能返回一个数值,根据这个数值,分别用clearInterval(ids)与clearTimeout(ids)来清除掉(假设将返回的数值赋值给ids哈);

1、setInterval
    var i=1;
    var ids = setInterval(function(){
        i++;
        if(i>5) clearInterval(ids);//仅仅是停掉计时器,程序未结束
        console.log("aa",i);
    },1000);
    
2、setTimeout
    var i=1;
    var dis = setTimeout(function(){
        i++;
        // if(i>5) clearTimeout(dis);//延迟1秒输出i=2,说明,不循环执行
        clearTimeout(dis);//清除时间间隔,每次用完一定记得清除
        console.log(i);
    },1000);
    
3、用计时器实现一个方块从左到右行走
    var dists=0,bool=false;//表示出事距离
    init();
    function init(){
        var div = document.getElementById("div0");
        div.style.width="100px";
        div.style.height="100px";
        div.style.backgroundColor="teal";
        div.onclick=function(){
            bool=!bool;
        }
        setInterval(timer,16,div);
    }
    function timer(div){
        if(!bool) return;
        dists++;
        div.style.marginLeft=dists+"px";
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值