Javascript基础复习(一)

视频链接:Web前端JavaScript权威课堂
本视频对于初入JavaScript特别友好,成哥讲的很通透~~~
本文为视频笔记,感谢阅读~~~

web发展史

  1. 目标路线:
    jquary(javascriptt封装版本,jquary是一个工具
    未知(这个未知也是一个工具是CSS3的简化工具)
    css3 html5.0 es6.0 
    bootstrap
    node.js(后端元素) 等等
    
  2. 金融:最低资金成本 国债 国际结算货币:美元
  3. Mosaic 是互联网历史上第一个获普遍使用和能够显示图片的网页浏览器 于1993年问世
    Netscape Navigator公司,火狐 IE都是在Mosaic基础上完善的script

js发展史

  1. javascript作为netscape Navigator浏览器的一部分首次出现在1996年最初设计目标是改善用户体验
  2. 作者:Brendan Eich

浏览器组成

  1. shell:用户能操作的部分
  2. 内核:能够处理代码 并且把代码显示出来的 浏览器核心
    • 内核大致分为
      • 渲染引擎:(语法规则和渲染 )主要负责绘制,16毫秒才会更新一下浏览器状态
        • 功能:html css基础语言的识别 以及与浏览器高效绘制页面
      • js引擎:控制javascript的
      • 其他模块:负责异步等其他东西

js引擎

引擎:类似汽车中发动机能让汽车动起来的物件

  1. 1996年 javascript没有引擎 所有动地都依赖于渲染引擎负责,若是写的javascript代码超过20行 那么网页就会崩溃
  2. 2001年 发布ie6 首次实现了js引擎的优化和分离 从js里单独拨了一个引擎给javascript 可以将js执行代码提升到万行以上 现在有些银行 政府还是用ie6(XP系统御用浏览器ie6)(革命性)
  3. 2008年 Chrome浏览器快,webkit内核中的御用引擎就是javascript引擎 代号V8(用C++写的) 直接将js代码翻译为机械代码01010等 所以同网速的情况下 chrome最先渲染出网页来 速度更快
  4. 后Firefox 也推出了Firefox3.5 traceMonkey对频繁执行的代码进行了路径优化,Firefox4.0 leagerMonkey

js的逼格(解释性 单线程)

  1. 翻译的过程分为两种:编译型 解释型
    由于翻译的性质不同 诞生了两种不同性质的语言分别为
    • 编译性语言:通篇翻译 像c生成.obj 像c++
      • 优点:快
      • 不足:移植性不(不跨平台)
    • 解释性语言:不生成文件 翻译一行 执行一行 javascript php
      • 优点:跨平台
      • 不足:稍微慢
    • java既不是编译性也不是解释性语言 叫oak语言
      • java --javac -->编译–>.class–>jvm --解释执行
    • unix linux 由黑客编程者共同开发的操作系统
  2. 线程
    • 多线程(类似异步asynchronization):一个执行体同时能干很多事
    • 单线程(类似同步synchronization):一个执行体一个时间内只能干一件事 想要干其他事 必须将手头的工作放下才能执行其他事
  3. ECMA标注 (ECMA ==> European Computer Manufactures Association)
    • javascript标准是ECMA制定的
    • 将ecmascript,DOM,BOM统称为javascript
    • ecmascript:
      • 原生的 原有的:javascript
      • 宿主提供:
        DOM是操作文档 html css
        BOM是操作浏览器的
  4. 轮转时间片:把每一个任务按照时间分解为片段 送至js引擎 这个过程叫做轮转时间片(没有先后过程)

js三大部分:ECMAscript DOM BOM

主流浏览器

主流浏览器(在市场上有份额 大于百分之三 有独立研发的内核)

  1. 浏览器及其内核

    浏览器 内核
    IE trident
    Chrome webkit/blink
    firefox Gecko
    Opera presto
    Safari webkit
  2. 注意浏览器和内核的区别!!!

  3. js引入方式两种(页面级js文件(可以放在head中或者body中) 外部js文件)

    • 页面级js文件
        
      
    • 外部js文件
        
      
    • 内部引用和外部引用不要同时用,同时用也是外部的生效 有特殊需求的写在页面里 没有特殊需求的用外部js文件打包
  4. 正常开发要求标准:结构(html) 行为(js) 样式(css) 相分离(每个要求都是单独的文件)

零散知识

  1. 编程语言:有变量 有函数 有数据结构 可以进行基本的运算 所以html css不叫编程语言 只能叫做脚本语言
    • 例:
      var a ;        //声明变量(向系统中申请内存 名字叫做a 类似酒店开房)
      a = 100 ;       //将100赋值给a
      
  2. 命名规则
    • 变量名必须以英文字母、_、$ 开头
    • 变量名可以包括英文字母、_、$、数字
    • 不可以用系统的关键字、保留字作为变量名
      • 关键字:有特殊语法含义的词
      • 保留字:未来可能被用作关键字的词

基本语法

  1. 数据类型分为两种:原始值和引用值
    • 原始值分为五大类:number boolean String undefined null
      • 第一种:整型
      • 第二种:布尔类型只有两个值(true或false)
      • 第三种:字符串类型
      • 第四种:
      • 第五种:null
    • 引用值:Array Object date RegExp
  2. 值类型由值决定 可以var a=“10”; a=“abc” 没有问题 不像C++中的int char
    • 注意:undefined+1 等于NaN。
  3. 原始值
    • 将第一个值赋给第二个 再改变第一个值 第二个值不变 而引用值 第二个也变
    • 特点:不可改变的原始值,要是改变,会再申请新的房间名字不变,原来那个就变为野房间了
    • 但是原始值怎么能改变呢:就是进行二次覆盖才可以,要不是不会删掉的。
      原因:
      • 原始值存在stack(栈)中 first in last out后进先出,栈是将复制一个a值的副本,然后赋值给b,a中值与b中的值没有关系了,所以改变a中的值对b中的值没有影响,栈和栈之间赋值是拷贝
      • 引用值大致上都是存在堆(heap)中,堆中存放数据的内容,对应栈中存放堆的地址 所以复制时 将堆的地址赋值给其他变量
    • 例1:
      var a = [1,3];
      var b = a;
      a.push(2);//新添入值,地址不变。 
      a = [1,2,4,5]; 堆中开辟新的空间,a中存放新的地址,和原来地址不一样
      document.write(b);
      
    • 例2:
      var arr = [1,2];
      var x = arr.length;
      arr.push(3);
      console.log(x);//得到的x还是2,而不是3。
      
  4. javascript是一种动态语言
    • 动态语言基本上都是解释性语言,解释性语言基本上都是脚本语言,解释性语言边解释边执行
    • javascript声明变量的关键字只有一个 不需要像其他语言int float string char等
    • java天生是浮点型 不是整型

零散知识

  1. 函数后不用加分号
      function text(){}
      if(){}后也不用加分号
      for(){}后也不用加分号
      任何符号两侧 都要留有空格 例如var arr = [1,2];
    
  2. 错误分为两种
    • 低级错误(语法解析错误,在扫描的时候就能被扫出了,程序一行都不会执行),例如中文标点 错误以下的不能执行 以上的能执行
    • 逻辑错误(标准错误,情有可原),一个文件中可以写多个script并且之间是互通的 变量的内容可以相互使用,并且一个代码块的错误(包括逻辑错误与低级错误)都不影响其他代码块的执行
    • 例1:扫描时候不会出现错误的,但是执行到第二行就会出错了·
      var a;
      document.write(b)
      

js运算符

  1. 加号就有两点作用(顺序自左向右,括号优先)
    • 数学运算
    • 字符串连接(任何数据类型加字符串都得字符串,字符串字符串之间加法运算就是连接);
  2. javascript不能利用加减运算将字符串转化为ASCII码 但是可以利用大于小于转化
  3. "-" "*" "%" "=" "()"
    • “/”:凡是那种应该得出数字类型的值 但又没法表示 就会给出NaN(not a number)例如0/0
    • 优先级"=“最弱,”()"优先级较高
    • 赋值符号运算符最低
    • 例:
        1/0       //显示infinity
        -1/0      //显示-infinity
        1/0 	    //Infinity Number
        0/0       //Not a number --> NaN(数字类型)
      
  4. "++" "--" "+=" "-=" "*=" "/=" "%="
    • “++”:document.write(b ++); 先打印后加加(先运行执行语句 后++)
      document.write(++ b); 先加加和后打印都是一个数(先++ 再执行语句打印)
    • “–”:
    • “+=”: var b = 10; b += 10+1; 计算顺序:b+=11;
    • 例1:
      var a = 10;
      var b = ++a - 1 + a ++; 先+1 执行完语句后a再+1;
      结果:b = 21 a = 12
      
    • 例2:
      var a += 10 + 1; 后两个先算
        赋值的顺序 自右向左,计算的顺序,自左向右
      
  5. 小测验 交换a和b的值,算法如下:
    a = a + b;
    b = a - b;
    a = a - b;
    
  6. 注意:一些公司笔试可以做做

比较运算符

  1. 比较运算符 ">" "<" "=" ">=" "<=" "==" "!=" 比较运算符返回true或者false
    • 比较数字时:结果是错 输出false 正确 输出true
    • 字符串比较时 比较ASCII码(最初是00000000-11111111共128个,A是65,a是97),若是字符串中多个字符,则从第一个字符开始比较第一个数相等 再从第二个数开始比较等等
      • 例1:var a = "10" > "8"  -->false
        
    • NaN == NaN–>false(就它是特例)
    • Infinity == Infinity -->true

逻辑运算符

逻辑运算符 "&&" "||" "!"(返回的是表达式的值不是对应的boolean的值,但比较时,是比较的boolean的值)

  1. "&&"运算时
    • 会先看与运算前面的表达式转化为布尔值是否为true
    • undefined,null,NaN,""(空串),0,false==>转化成布尔值都为false,其余之都为true
    • 总结:两个表达式进行与运算("&&"),若第一个值为false,则不往下运行,直接返回第一个表达式的值,若第一个表达式的值为true,则返回第二个表达式的值。
    • 当多个表达式式依 次执行,到假就停,真就继续向后执行。+ - 优先级高于&& ||,&&运算符两边可以为表达式或者函数等都行
      • var c = 1 + 1 && 1 - 1;
        1+1=2
        1-1=0
        2&&0=0;
        
    • 当&&两端为语句时 则运算符大致翻译为“如果那么”,称之为短路语句。
  2. "||"运算时
    • 会先看第一个是否为真,若为假则继续向下执行,直到出现真为止,可若只有两个式子,那么,只要第一个是假,那么不管第二个是否为真,都返回第二个的值。
    • 一般可以用作兼容
    • 总结:&&运算符碰见假就停,||运算符碰见真就停。
  3. “!”
    • 转换为布尔值再取反(六种(undefined,null,"",0,flase,NaN)取反都为true 其余的就按正常计算)
  4. 单独"&"运算时,是转化为而二进制,再进行与运算,得到0/1。
    • 例:
          var a = (window.foo || (window.foo = 'bar'));//a = bar;(bar为表达式的值)
          首先:或运算符的优先级高于赋值运算符,要是不加括号就会报错的
          然后:这题在后面加了个括号,所以就先执行一下window.foo = 'bar'
          最后:执行前面时,window.foo等于bar了,所以是对的了,最后返回的就是bar
      
  5. 前端的任务就是拿到后端的数据放在页面中去,然后就可以用这个短路语句来判断数据是否有意义,有意义才执行后面的内容,以免出错

条件运算符

    • if(条件){ } 条件成立执行,条件不成立不执行,条件中若是有逻辑运算符,不用反复计算,就是全真为真或者全假为假等。
        var c = parseInt(window.prompt('input'));
        /*系统提示输出框,输入,网页就会显示输入的信息*/
      
    • else if与if之间互斥,没有交叉点时才可以用
    • else 表示除了if与else if之外所有的情况
    • if与&&的转换 if(1 > 2){document.write(‘cuo’);}<==>1>2&&document.write(‘cuo’);
  1. 输出
    • document.write();
    • console.log();
  2. 注释:
    • html注释
    • css注释/* */
    • js行注释//
    • js段落注释/* */
  3. for循环的用法
    • 既能被2也能被5也能被7整除的数
          for (var i = 0 ; i < 100; i++){    //从0 到100执行里面,执行了100遍
          if(i % 2 == 0 && i % 5 == 0 && i % 7 == 0){
                  document.write(i + " ");
              }
          }
      
    • 1-100中既能被2也能被5也能被7整除的数,而且for里只能写一句话,里面没有,判断出循环的条件的语句
      var i = 100;
      for (; i --; ){
          if(i % 2 == 0 && i % 5 == 0 && i % 7 == 0){
              document.write(i + " ");
          }
      }
      
    • 打印10个a,不用break
      var i = 1;
      var count = 0;
      for(;i;){
          document.write('a');
          count++;
          if(count == 10){
              i = 0;
          }
      
      }
      
    • 执行顺序: 循次往复
          var i = 0;
          if(i < 100){.....}
          i++;
          if(i < 100){......}
          .....
      
  4. while循环的用法(可以转换为for循环,就是for循环简化版)
    • 例1:
          var i = 0;
          while(i < 10){
              document.write(i + " ");
              i ++;
          }
      
    • 例2:死循环(never-ending loop),不会报错 网页会一直转圈圈。
          var i = 1;
          while(i){
              document.write(i);
          }
      
  5. do while循环用法(了解 不重要 最好不用)
        var i = 0;
        do{
            document.write('a');//十个a
            i ++;
        }while(i < 10)
    
  6. 备注
    • javascript天生是浮点数 当计算1/3时 会输出0.333…不会像C语言一样
    • 素数:只能被两个不同的数整除,所以1不是
    • 输入一行 打印一行 以免出错找不出来
    • 推荐可以看看一部美剧(west world 盗梦空间导演)
    • 定义多组变量
          var first = 1,
             second = 1,
             third;
      
    • console控制台 可以计算可以看程序某个变量的运行情况 可以做很多事情
    • 质数:平方根是临界点
    • document.write是输出文档流,最好不要干别的

条件语句补充

  1. switch语句://极其的不负责任 尽管找到了符合条件的语句 但是也会执行下面的语句
    • 例1:
        switch(n){//n可以为任何东西:字符串、数字、布尔类型、都可以。
              case 1:
                  console.log('a');
                  break;//执行完后这句后跳出,不执行下面的语句
              case 2:
                  console.log('b');
                  break;
              case 3:
                  console.log('c');
                  break;
          } 
      
    • 例2:
        switch(n){
              case "monday":
              case "wednesday":
              case "周三":
              case"thursday"://将坏处变为好处
              case"friday":
                  console.log('working');
                  break;
              case "周六":
              case "周天":
                  console.log('relaxing');
                  break;
      
          }   
      
  2. break 必须放在循环中,要不然会报错
        var sum = 0;
        for(var i = 1;i < 100;i ++){
            sum += i;
            console.log(i);
            if(sum > 100){
                break;//根据某些条件跳出循环
            }
        }
    
  3. continue
        for(var i = 0; i < 100;i ++){
            if(i % 7 == 0 || i % 10 == 7){
                continue;//终止本次循环,不执行下面的语句,直接执行下次即i++
            }
            console.log(i);
        }
    

初始应用值

  1. 数组
        var arr = [1,2,3,4,5,6];//定义数组
        for(var i = 0; i < arr.length;i ++){//arr.length是数组的长度
            arr[i] += 1;//修改数组中数的值
            console.log(arr[i]);
        }
    
  2. 对象(object):其中包括属性和方法
        var deng = {
            //key    value(可以为字符串,数字等都可以)
            lastName:"Deng",           //lastName:属性名    "Deng":属性值(重点)
            age:40,
            son:"xiaodeng",
            wife:undefined,
        }
        console.log(deng.lastName);  -->Deng
        deng.lastName = "old Deng";//改变变量值
        console.log(deng.lastName); -->Old Deng
    

编程形式的区别:

  1. 语言可以按好多种形式分
    • 解释性 编译性
    • 面向过程 面向对象
    • 弱势类型 强势类型
    • 脚本语言 非脚本语言
  2. 面向对象 面向过程
    • C语言是典型面向过程型语言,java,C++都是面向对象型语言,javascript既面向过程,又面向对象。
    • 面向对象是找什么人去做一件事,善于利用身边的关系,面向过程是分步骤完成一件事情,第一步做什么第二步做什么,大致是这样。
    • javascript是半面向对象,半面向过程的语言

typeof

能帮我们区分数据类型:可以typeof(a)或者typeof a两种形式都行`

  1. 用法
        var num = {};
        console.log(typeof(num));//type正确用法
        var str = "jfakl"
        console.log(typeof str);//typeof后可以跟空格但是最好不要这样用
    
  2. 可以返回六种数据类型
    number string undefined boolean function object
            object(泛泛的引用值都返回object)
            function(自己查各种类型的返回条件)
    
  3. 数组arr类型返回object
  4. null返回object

    有个历史遗留性问题,虽然null是原始值,原因是最早时null是代替空对象,出现的,给空对象占位的,直到现在也没有改回来。`

  5. function 返回 function

显示类型转换

  1. Number
    • 用法
      var num = Number(true);//将布尔类型转换为数字类型
      console.log(typeof(num)  + " " + num);
      
    • 特殊:
      • undefined不能被转换为数字类型结果NaN
      • null转换为数字是0
      • 'a’或者"abc"都不能转换为数字,显示为NaN
      • 而"-123"就可以转换为数字-123
      • "1314.anvka"就为NaN
      • “011” 转化为11。
    • 经历number,不管能不能转换为一个数,最终都能转换为数字类型,如果不能转换为数字,那么就显示NaN。
    • 总结
      • null–> 0
      • “-123”–>-123
      • 其余像undefined,‘a’,“123abc”,什么的都转换为NaN
  2. parseInt:转换为整型
    • parseInt和toString
      • parseInt(10,16) = 16
      • 32.toString (16) = 20)
      • 基底(也就是第二个参数的位置)要是0的话,什么也不影响,写了和没写一样。但是有的浏览器显示不一样,有的显示3,有的显示NaN
    • 用法
      var demo = "124.333";
      var num = parseInt(demo);
      console.log(typeof(num));//结果为124(直接去掉小数部分)
      
      var demo = "10";
      var num = parseInt(demo,16);//以目标进制为基底转化为十进制,所以输出结果是16。
      console.log(typeof(num));    
      
    • 两个用途:
      • 变为整型
      • 进制转换:parseInt(string,radix) 以目标进制为基底转化为十进制。
        • 注意:radix取值只能为2-36,不能过小,不能过大。转化进制时,string中的数也不能乱写,若radix为二进制 那么string值就只能为0或1。
    • 注意:
      • 它重点在取整方面,而Number是想千方百计把其他类型转换为整型。
      • 像字符串中含有数字的 那么可以转换为数字 其他类型一律结果为NaN
      • 以数字为开始看,看到非数字位截止所以var demo = “123abc” 结果就为123。var demo = “123.3fjakl”; 结果为123,因为 . 是非数字,所以从 . 开始砍断了,不看了,结果就为123。
    • 特殊:
      • null --> NaN, “-123.fa” --> -123,其余转化为NaN。
      • parseInt只有含有数字才能转换为数字,像undefined或者null或者true,false都不行,但是像"123.abx"都可以转换为数字
      • Number就是必须全为数字才能转换为数字,像"123"才可以,还有就是null和true和false能转换为数字,但是其他的不行,像"214vab"就不行
    • 补充:parseFloat据就是转化为小数
      • 例1:
        console.log(parseFloat("124.2wg")); 得到结果:124.2
        
  3. String:转化为字符串:写什么在string中都会转换为字符串
  4. Boolean
    • 除了那六个值 undefined,null,NaN,0,""("“空串,” "空格字符串),false,其余的都转换为true。
  5. toString
    • 用法:想将谁转换为string就.toString
      var demo = 123;
      var num = demo.toString();
      console.log(typeof(num) + " " + num);
      
      var demo = 32;
      var num = demo.toString(16);//将radix转换为16进制,与前面parseInt中的radix不同
      console.log(typeof(num) + " " + num);
      
    • 注意:
      • toString是将十进制转化为目标进制的过程
      • undefined,null不能用toString 会报错
      • 一般很少人用toString,可以用string或者直接加空串转换为string类型,任何东西加空串都可以转换为空串。
    • 其他用途: 将2进制转换为16进制
      var num = 10101010;
      var num1 = parseInt(num,2);
      var num2 = num1.toString(16);
      

隐式类型转换

  1. isNaN():判断括号中的内容是不是NaN
    • isNaN先将括号中的内容转换为Number,然后再和NaN作比较,所以isNaN(‘abc’)的最终结果是true
  2. ++用法
    • ++先将变量变为Number型 然后再计算。
    • 用法
      var a = "123";
      a ++;
      console.log(a);//a为124且为数字类型
      
  3. +正 -负
    var x = + "abd";
    console.log( x + " : " + typeof(x));
    //结果为NaN,虽然不是个数,但类型为数字类型。
    
  4. +加号:当加号两侧有一个东西时字符串类型时,就会将两个东西都变为字符串类型
  5. -减号 *乘号 %取余
    • 都先将变量变为Number型,然后再计算,最后是不是数不确定,可能为NaN,但是类型一定是数字类型。
      +例:var "a" * 1 -->NaN
  6. && || !
    • 在比较时会将第一个表达式,第二个表达式转换为布尔值再比较,中间有隐式类型转换的过程,只不过最后的结果为表达式的值,不是隐式类型转换的值,但判断的是隐式类型转换的值,进行boolean隐式类型转换
  7. < > <= >=
    • 用法
      var a = 1 > "2";//是将字符串转换为数字2再进行比较,结果为false
      console.log(a);
      
      var a = "3"> "2";//是将字符串3与2的ASCII码进行比较,结果为true
      console.log(a);
      
    • 数字优先:字符串与字符串比较ASCII的值,数字与字符串进行比较,比较的则为数字的值。
      2 > 3 < 1 true
      //一个一个算,先算前面的boolean值,然后将boolean值转化为数字和1再比较
      2 > 1 > 3 false
      3 > 2 > 1 false
      
  8. == != ===
    • === :绝对相等,必须完全一样,包括类型和值什么的,像null ==undefined,但是null不绝对等于undefined
    • 例:
      1 == true成立
      1 == "1"成立
      
    •     undefined > 0             //false
          undefined < 0              //false
          undefined == 0           //false
      
          null > 0 false
          null < 0 false
          null == 0  false
          undefined == null       // true
          NaN == NaN     //false 
          /*  唯一一个原始值,任何一种情况都不等于自身,而且什么啥也不等于
              但是如果需要判断得到的结果是不是NaN的话,可以利用变量加上空串的方式
              例如NaN+"" == "NaN"
          */
      
    •     {} == {}               //false      
          /*对象是引用值,引用值比较的是地址
          这两个空对象指向两个不同的房间,看着再一样也是两个房间,不相等。
          */
      
    • 例:
          var obj  = {};
          var obj1 =  obj;
          obj1 === obj;
          //返回true,因为引用值赋值赋的是地址。所以一个房间那肯定相等。
      
    • 例:
      字符串
      "a" +                -->加啥都等于字符串
      "a" *                 -->转换为Number类型
      "a" >                -->两个字符串比较ASCII码
      
  9. === !==
    • 对于基础类型String,Number等基础类型来说,=是严格判断是否相等的,若是类型不同,那么一定不等。
    • 对于高级类型 来说,==与 ===相同的
  10. 补充小知识点
    • typeof
        console.log(typeof(typeof(a))); 返回string
        console.log(typeof(a)); 未定义就是使用a,返回字符串类型的undefined
        console.log(a);  报错 not defined
      
    • toFixed
      var x = 123123.4232332;
      alert(x.toFixed(5));//科学记数法四舍五入,保留几位有效数字。alert是弹出框
      
  11. 都是注意点:
    • 带数学符号像% - * /等(+号除外,因为+还有连接的功能)的一般会将运算符两侧转化为Number类型,不能转的一般就是NaN。
    • && || 比较的是运算符两侧的布尔值,假如两侧最后都为String类型,那么布尔值就是都是1,就是对的,或者当一侧为"undefined",另一侧为"NaN",两侧都为字符串类型的,转化为boolean值也都为1
    • 运算中,+号,数字隐式转换成字符串。其余的运算符号是字符串隐式转换成数字。

函数,小练习,初始作用域

函数其实本质上和变量一样,都是一个筐,只不过这个筐装了很多条语句。它也是引用值,存的是地址,输出时永远不能输出地址,输出地址指向的房间

定义函数方式:

  1. 函数声明方式
        function theFirstName(){
            document.write("hello world");
        }
        theFirsrName();//命名:小驼峰原则
    
  2. 函数表达式方式
    • 命名函数表达式
         var test1 = function test(){
          从function开始到大括号结束,这些都叫做表达式 
          表达式是可以忽略它的名字的,调用时只有调用test1才执行,调用test报错,但是test1.name为test,但是要是函数声明方式中test1.name为自己"test1"
            document.write("hello world");
         }
      
    • 匿名函数表达式 — 函数表达式(由于这种方式比较常用所以简化为函数表达式)
         var testN = function(){//b1与b2之间的区别在于b1函数的名字为test1,调用时应调用test1,而不是test,而b2函数的名字为默认本身testN,直接调用就可以。
             document.write("hello world1");
         }
      

函数的组成形式

  1. 函数名称:sum
    • 例:
        function sum(a){//形参 
              for(var i = 0; i < arguments.length ;i++){
                  console.log(arguments[i]);//系统可以打印出来 ,arguments:实参列表,是个数组,sum.length形参的长度
              }
            }
            sum(1 , 2 , 3)//1 ,2 , 3 实参,都存放在arguments中[1,2,3] 对arguments求和可以将所有实参求和
      
    • 例:
             function sum(a,b,c,d){      //等价于在函数中var a,b,c,d;
              if(sum.length > arguments.length){
                  console.log('形参多了');
              }else if(sum.length < arguments.length){
                  console.log('实参多了');
              }else{
                  console.log('相等');
              }
             }
             sum(1 , 2 , 3 ,undefined)//结果为相等
      
  2. 参数:不限制位数(形参可以比实参多)
    • 形参(形参比实参多,其余显示undefined)不定参的
      • 例:任意个数的求和
        	  function sum(){
                	//arguments 1 2 3 4 5 6
        	        var result = 0;
                	for(var i = 0; i < arguments.length; i ++){
        	            result += arguments [i];
                	}
        	        console.log(result);
        	  }
        	  sum(1,2,3,4,5,6);
        
      • 例:
            	 function sum(a,b){
                        //arguments 1 6
                        a = 10;
                        console.log(arguments[0]);//结果为10 因为arguments与形参存在映射关系,但不是同一个东西,所以一个变另一个也变,只有形参实参相等的时才会有映射的规则,不相等不对应实参了,相等的地方保持映射,它俩不等的地方就不映射了
            	 }
            	 sum(1,6);
        
      • 例:
            	 function sum(a,b){
                    	//arguments 1
            	        b = 10;
                    	console.log(arguments[1]);//结果为undefined,因为只有形参实参相等的时才会有映射的规则,不相等不对应实参了,相等的地方保持映射,它俩不等的地方就不映射了,arguments中不会有其他形参,例题中的b只当作变量用,不用作参数。
            	 }
                 sum(1);
        
    • 实参(实参比形参多,取第一位)
  3. 返回值
    • 例1:
         function sum(a,b){
          return 245;//有两个含义既返回 值,又终止函数。
          console.log('a');//因为上面有return 所以这行不执行,因为return终止函数了
         }
         var num = sum();
      
    • 例2:
      	function myNumber(target){
          	return + target;
      	}
      	var num = myNumber(123);//
      	console.log(typeof(num)+ " " + num);
          //函数结果为数字 类型123
      
  4. 一般函数作为一个实现功能。一般来说不直接打印

零散知识

  1. 高内聚低耦合
    • 函数最基本的用法:简化代码(解决偶合)
    • 函数可以用作 定义功能
    • 解释性语言永远不可能输出地址,只输出地址指向的房间即内容。
  2. 耦合:重复的,冗余的,耦合度很高

小练习

  1. 写一个函数,功能是告知你所选定的小动物的叫声。
        function scream(animal){
            switch(animal){
                case"dog":
                    document.write('wang');
                    return;
                case"cat":
                    document.write('miao!');
                    return;
                case"fish":
                    document.write('oooo');
                    return;
            }
        }
    
  2. 定义一组函数,输入数字,逆转并输出汉字形式。
       function hanzi(){
            var str1 = "";
            var str = window.prompt('input');
            for(var i = str.length - 1 ;i >= 0 ; i --){
                if(str [i] == '1' ){
                    str1 += '壹';
                }
                else if( str [i] == '2'){
                    str1 += '贰';
                }
                else{
                    str1 += '叁';
                }
            }
            console.log(str1);
        }
        hanzi();
    
  3. 写一个函数,实现n的阶乘。
        function jiecheng(n){
            if( n == 1 ){
                return 1 ;
            }
            return n * jiecheng(n - 1);
        }
        var x = parseInt(window.prompt('input'));
        console.log(jiecheng(x));
    
  4. 写一个函数,实现斐波那契数列?

变量

  1. 全局变量:定义在script全局中的
  2. 局部变量:定义在函数里面的
  3. 全局变量与局部变量之间的关系:里层函数可以访问外层函数的东西,外层函数不可以访问里层函数的东西,彼此独立的事件不可以相互访问。

挑战型作业?

读规则:千分位有零必须读,没有零不用读 101000:十萬壹

作用域精解

  1. 递归:先执行的东西,最后被执行完。
    • 特别符合人为思考题的过程,找规律
    • 必须要有出口
      • 以阶乘为例
        //找规律:n! = n * (n - 1)!;
        function mul(n){
            
            return n * mul(n - 1);
        }
        //mul(3) ==> 3 * mul(2);
        //mul(2) ==> 2 * mul(1);
        //mul(1) ==> 1 * mul(0);
        
        //找出口,必须找已知条件当出口
           if(n  == 1 || n == 0){
                return 1;
            }
        
      • 以斐波那契数列为例
        //找规律 :fb(n) == fb(n - 1) + fb(n - 2);
        // fb(5) ==> fb(4) + fb(3);
        // fb(4) ==> fb(3) + fb(2);
        // fb(3) ==> fb(2) + fb(1);
           function fb(n){
            if( n == 1 || n == 2){
                return 1;
            }
            return fb(n - 1) + fb( n - 2);
           }
        
    • 递归唯一优点:能使代码变得简洁,剩下啥作用没有,而且特别慢。
    • 对于那些很容易找到规律的,利用递归很方便。

js运行三部曲

  1. 语句分析:通篇查看扫描有没有低级错误
  2. 预编译
    • 函数声明整体提升:将函数声明(不管写在哪里)永远提升到逻辑的最前面
    • 变量 声明提升:当声明并赋值变量时,会将变量声明提到最前面。
      • 例:
            function aa(){
                console.log('a');
            }
            aa();//能执行
        
      • 例:
        aa();
        function aa(){
            console.log('a');
        }//能执行
        
      • 例:
        var a = 1234;
        console.log(a);//正确输出 1234
        
      • 例:
        console.log(a);
        var a = 1234;
        //输出undefined,因为,系统会将语句分解为两部分 var a; a = 1234; 然后将var a提到最前面
        
  3. 解释执行(就是解释性语言最后的部分,解释一行,执行一行)

预编译前奏

  1. 未经声明的变量就直接赋值,那么这个变量归window所有。即 a =10; --> window.a = 10;
  2. 全局上的任何变量即使声明了也归window所有
  3. window就是全局的域
    • 1 ==>window {
          a : 123
       }
      2 ==>var a = 123;
      1 == 2 //系统下次再访问a时,访问的就是window.a
      
    • //var a = b = 123; 会先将123的值付给b,然后声明a,然后再把b的值赋给a。因为赋值是从右向左的,将b的值赋给a是发生在执行过程中,不是预编译
      function test(){
          var a = b = 134;//执行window.a结果为undefined,执行window.b时结果为123,因为a在函数中声明了,而b只赋值了
      }
      test();
      
  4. 一切声明的全局变量都是window的属性,意思就是window就是全局的域
    var x = 123; 
      ==>window.x == 123; 
      ==> window{x:123}
    console.log(x) 
      ==> console.log(window.x);
    

预编译:就是解决执行顺序的东西

  1. 发生时间:函数执行的前一刻
  2. 四部曲:(声明,形参,函数)
    • 创建AO对象(活跃对象activation object)(执行期上下文或者说作用域:由于函数执行而产生的存储空间库)
    • 找形参和变量声明,将变量和形参名作为AO属性名,值为undefined,或者将第二步叫做变量声明提升,优先执行
    • 将实参值和形参统一
    • 在函数体里面找函数声明,值赋予函数体
      • 例:
            function fn(a){
                console.log(a);//function a (){}
        
                var a = 121;//就只执行 a = 123; 因为var a已经预编译执行了,
        
                console.log(a);//a = 121;
        
        	    function a (){}//预编译已经执行过了,预编译看过的就不会再看了
        
                console.log(a);//a = 121;
        
                var b = function (){};//b= function (){};
        
                console.log(b);//b= function (){};
        
                function d(){};
            }
            fn(1);
           (1)第一步:AO{
                
             }
           (2)第二步:AO{
                a:undefined,
                b:undefined,
            }
           (3)第三步:AO{
                a:1,
                b:undefined,
            }
           (4)第四步:AO{
                a:function a(){},
                b:undefined,
                d:function d(){};
            }
        
  3. 全局
    • 生成一个叫GO对象(global object)=== window
      • 例:
            var a = 134;//执行 a = 123,
            function a(){
                
            }
           1.生成GO对象
            GO{
                a:undefined,
            }
           2.GO{
                a:function a(){},
            }
        
      • 例:
            function text(){
                var a = b = 123;
                console.log(window.a);
            }
           因为a声明了,所以在AO中。但是b没有,b未声明直接赋值了,就发生了暗示全局变量的过程,b就归GO所有(b = 123),所以window中没有a的值,访问window只有undefined
        
      • 例:
            /*GO{
                a:undefined,
                text:function (){},
            }*/
            function text(){
                console.log(b);
                if(a){     //if不管会不会执行,if中的什么东西都会被拿出来。所以上一句语句是b为undefined。
                    var b = 134;//因为a为undefined,所以此句不会执行,b还为undefined
                }
            }
            var a ;
            text();
            /*AO{
             b:undefined,
            }*/
            a = 10;
            console.log(a);
        
      • 例:
            a = 100;
            function demo(e){
                function e(){}
                arguments[0] = 2;
                console.log(e);                      //2
                if(a){
                    //备注:这个if中声明function,Google之前是允许的,但是现在不允许了。
                    var b = 123;
                    function c(){}
                }
                var c;
                a = 10;
                var a;
                console.log(b);                      //undefined
                f = 123;
                console.log(c);                       //undefined
                console.log(a);                       //10
            }
            var a;
            demo(1);
            console.log(a);                           //100
            console.log(f);                            //123
        
          解:函数拆解执行
            第一步:
                    /*GO{
                      a:undefined,
                      demo:function(){},
                    }*/
            第二步:
                    a = 100;
                    /*GO{
                        a:100,
                        demo:function(){},
                      }
                    */
                    function demo(e) {
            第四步:
                      /*var AO = {
                        e:1,
                        b:undefined,
                        c:undefined,
                        a:undefined,
                      }*/
                    function e() {
                    }
            第五步:
                      /*var AO = {
                        e:function e(){},
                        b:undefined,
                        c:undefined,
                        a:undefined,
                      }*/
                    arguments[0] = 2;
            第六步:
                      /*var AO = {
                        e:2,
                        b:undefined,
                        c:undefined,
                        a:undefined,
                      }*/
                    console.log(e);    //e = 2;
                    if(a){
                        var b = 123;
                        function c(){};//不执行
                    }
                    var c;
                    a = 10;
            第七步:
                      /*var AO = {
                        e:2,
                        b:undefined,
                        c:undefined,
                        a:10,
                      }*/
                    var a;
                    console.log(b);   //b = undefined
                    f = 123;
            第八步:
                           /*GO{
                           a:100,
                           demo:function(){},
                           f:123,
                         }*/
                        console.log(c);  //c = undefined
                        console.log(a);  //a = 10
                    }
                    var a;
                    demo(1);
            第三步:
                  /*var AO = {
                    e:undefined,
                    b:undefined,
                    c:undefined,
                    a:undefined,
                  }*/
                console.log(a);   // a = 100
                console.log(f);   // f = 123
        

百度笔试题:

  1. console.log(bar());
        function bar (){
            foo = 10;
            function foo(){
                //body...
            }
            var foo = 11;
            return foo;	//返回11,只要这个名在最下面被赋过值,那么直接输出那个值就好了。
        }
    
  2.    function bar(){
            return foo;	//返回function(){},若是第一句为return,那么就等价于console.log,而且第一句为return,若是下面有函数,那么直接返回函数。
            foo = 10;
            function foo(){
                //body ...
            }
            var foo = 11;
        }
        console.log(bar());
    
  3. 嵌套关系:自己函数中有就用自己的,自己没有,就往上一层找。

打字注意

  1. 左边的手用右边的shift 右手的键盘用左边的shift()
  2. 9 0 () 用中指无和名指来按
  3. 打字的时候手腕离开键盘托了,那么打字一定是慢的
  4. 建议下载一个什么什么键盘修改器,把ctrl与Alt调换一下4
    • 复制:ctrl + c
    • 粘贴:ctrl + v
    • 剪切:ctrl + x
    • 撤回:ctrl + z
    • 反撤回:ctrl + y
    • 整体移动:选中按tab键,挪多了,按shift + ctrl,向左移
    • 查找:ctrl + f

作用域精解(详情请见javascriptPPT课件)

  1. 作用域:由于函数产生而产生的一种独特的东西
    • 例:
      test.[[scope]]
      //存放由这个函数产生而产生的作用域,存放的是一个作用域,隐式的属性
      系统会定期的调用这个属性,但是不会让我们用
      
  2. 运行期上下文:当函数执行时,会创建一个称为执行期上下文(像AO,GO等)的内部对象。一个执行期上下文定义了一个函数执行时环境,函数每次执行时对应的执行上下文都是独一无二的,所以多次调用一个函数会导致创建多个执行上下文,当函数执行完毕,自己产生的执行上下文被销毁。
  3. 作用域链:[[scope]]中所存储的执行期上下文对象的集合,这个集合呈链式链接,我们把这种链式链接叫做作用域链。
    • 例:
        function a() {
              function b() {           //b得到的a的劳动成果,就是a的aAO,拿到的是引用,注意:所有的aAO都是一个人,或者GO也都是一个人。
                  var b = 123;
              }
          	var a = 123; 	
              	b();
          }
          var glob = 100;
          a();
          //a刚被定义时,即a刚出生时(就有a.name 和a.[[scope]]等),存放的是它所在环境的执行期上下文,要是在全局环境下,存放的就是GO。
          具体解释:
          a函数被定义时,发生如下过程:
            1.当a定义时,a.[[scope]]中存放GO(a刚定义时a.[[scpoe]]存的是a所在环境的执行期上下文),
            2.然后a执行,产生一个执行期上下文并放在自己作用域链的最顶端,然后将最初创建的GO往下串一格,然后形成了自己的作用域链,有第0位,有第1位,一旦我们在a中查找变量的话,会在a的作用域链的顶端即第0位,依次向下查找。
          b函数被定义时,发生如下过程:
            1.当b定义时,继承a的scope(使用a的劳动成果)
            2.当b函数执行时,会创建属于自己的bAO,放在b.[[scope]]最顶端即第0位,依次第1位,第2位存放a[[scope]]的东西,找变量时,先从自己作用域链的最顶端依次向下寻找。
          注意:b从a运行后a.[[scope]]中得到的AO与a中的AO是一个AO,指向同一个东西,即b可以更改了a中属性的值
          最后
          当b执行完后,b只是删除了自己执行时创建bAO但是死死地攥着a的aAO,一直拴在自己身上,恢复最初被创建的状态,
          当a执行完后,就将a执行时创建的AO(包括b函数)删掉,不包括GO,GO还在,然后等待下一次执行,再重新创建一个独一无二的a.[[scope]]。
      
    • 例:
      function a(){
              function b(){
                  function c(){
                      
                  }
                  c();   //c最先被执行完 ,c.[[scope]]最先被销毁。
              }
              b();       //c被执行完,才标志着b()执行完,然后b.[[scope]]被销毁。
          }
          a();           //b被执行完,才标志着a最后一条语句被执行完,a.[[scope]]最后被销毁,可以说销毁和执行完是一个概念。
      执行步骤:
          a defined a.[[scope]] --> 0 : GO
          a doing   a.[[scope]] --> 0 : aAO//只有a执行时,a中的语句(像函数b等等)才会被读,b才会被定义,系统中才会有b.[[scope]],a执行才能导致b被执行。
                                    1 : GO
      
          b defined b.[[scope]] --> 0 : aAO
                                    1 : GO
      
          b doing   b.[[scope]] --> 0 : bAO
                                    1 : aAO
                                    2 : GO//所以站在b的窗口访问不到c中的内容,没法看到c窗口中的内容
      
          c defined c.[[scope]] --> 0 :bAO
                                    1 : aAO
                                    2 : GO
      
          c doing   c.[[scope]] --> 0 : cAO//c被执行完就删除了,再执行时就会产生新的c.[[scope]],除了cAO被删除,剩下的都不变,一样,只是会产生新的cAO。
                                    1 : bAO
                                    2 : aAO
                                    3 : GO
          // 在任何一个函数里面想访问变量的话就找函数的作用域链就可以了
      
  4. 在哪个函数里面查找变量:从哪个函数作用域链的顶端依次向下要查找。
  5. 每一个函数都有一个执行期上下文的集合,叫作用域链,真正在函数里访问变量要遵循函数所产生的作用域链去访问。
  6. 总结:
    • 只有里面的能看见外面的,外面的看不见里面的,然后按顺序访问,按空间访问。
    • 所有的aAO,bAO都是一个。

闭包(详情见PPT)

  1. 内部的函数被保存到了外部,将会生产闭包。
    • 例1:(b函数)
          function  a(){      
              /* 不管a还是b,各自的作用域链中摆脱不掉的是被定义时的状态,
              就像a,a.[[scope]]中始终存着GO,像b.[[scope]]始终存着aAO,
              第0位:aAO;
              第1位:GO;
              */
              function b(){   //内部的函数
                  var bbb = 234;
                  document.write(aaa);
              }
              var aaa = 123;
              return b;  
              //被保存到了外部,所以生成了闭包。
              备注:这里是a先被销毁,但是a销毁前,b虽然没被执行,
              但是已经拿到了a的执行期上下文了。
          }
          var glob = 100;
          var demo = a();
          //a执行完了,a将指向自己AO的线删掉。b定义时得到的是a执行的劳动成果,
          虽然a删掉了指向自己AO的那个线,但是b被引用到外部,b依旧保存与那个房间(aAO)之间的关系,
          a想删掉那个房间(aAO)都没有办法。
          demo();       //结果输出123
      
    • 例2:
          function a(){
              var num = 100;
              function b(){
                  num ++;
                  console.log(num);
                  //函数只有在被调用时才会执行里面的语句,虽然声明完b,
                  但是可以运行出结果并显示的原因,是后面demo也就是b被调用了。
              }
              return b;
          }
          var demo = a();  
          //自己理解:a执行后删除线,但是b.[[scope]]中保存着aAO,
          执行完a将函数的引用(也就是b引用)抛出去赋给demo,
          a执行结束,所以demo()指的是b();
          demo();    
          //执行输出101 ,
          原因:b执行未产生新的bAO。继续执行b函数中的num++,
          num从b.[[scope]]第0位找,虽然a执行完了删掉了,但是b还是保存着aAO。
          demo();    
          //执行输出102 ,
          原因:b继续从b.[[scope]]第0位找num ,执行num++。
          但此时的num因为被b执行一遍了更改为101了,所以再执行一遍就为102了。
      
  2. 闭包缺点:会导致原有作用域链不释放(导致程序里的空间被占用的过多),造成内存泄漏(意思为像内存泄漏,就像手里的水,你越用力,剩的就越少,在程序里就可以理解为,我用的闭包越多,剩余空间就越少,其实并不准确)
  3. 两个函数或者多个函数互相嵌套把里面函数保存到了外部,保存到了全局,这样的情况必然生成闭包,里面的函数在外面执行时一定能够调用原来它在的那个函数环境里面的变量。

闭包的作用

  1. 实现公有变量
    • 例1:函数累加器(不依赖于外部函数变量并且能实行的函数累加器)
        function add(){
              var count = 0;
              function demo(){
                  count ++;
                  console.log(count);
              }
              return demo;
          }
          var counter = add();
          counter();//执行结果1
          counter();//        2 
          counter();//        3
      
    • 例2:
          function test(){
              var num = 100;
              function a(){
                  num ++;    
                  //aAO中的num,a函数中的num与b函数中修改的num是一个num,都来自于aAO中。
                  console.log(num);
              }
              function b(){
                  num --;   //aAO中的num
                  console.log(num);
              }
              return [a,b]                         
              //返回了一个数组,myArr[0],代表执行a,myArr[1]代表执行b。
          }
          var myArr = test();
          myArr[0]();    //101
          myArr[1]();    //100
      
  2. 可以做缓存(存储结构,多个函数同时和一个函数形成闭包的话,他们之间的变量相当于可以共用,因为大家保存的是同一个域,就算是全局变量也一样,所以说这样的结果就有点像缓存,相当于仓库一样,但是你看不到,每次拿到的结果都是上次修改过的值)
    • 例1:
          function eater(){
              var food = "";                                
              //类似存储结构
              var obj = {
                  eat : function (){
                      console.log("i am eating " + food);    
                      //先执行push,push改变了food的值,再执行eat的时候输出food部分为apple
                      food = "";
                  },
                  push : function (myFood){
                      food = myFood;
                  }
              }
              return obj;
          }
          var eater1 = eater();
          eater1.push("apple");
          eater1.eat();
      
  3. 私有化变量,不会污染全局变量。
    • 例1:在函数里面定义一个变量,外部无法访问,只有自己可以见,下题中prepareWife就是在执行期上下文中,但是因为使用它的函数随着对象(divorce或者其他)被保存到了外部,所以自己也和外部形成了闭包,通过也实现了私有化。
          function Deng(name,wife){
              var prepareWife = 'xiaozhang';                         
              //在外部访问时deng.prepareWife显示是undefined,实现了私有化。
              this.name = name;
              this.wife = wife;
              this.divorce = function (){
                  this.wife = prepareWife;
              }
              this.changePrepareWife = function (target){
                  prepareWife = target;
              }
              this.sayPreparewife = function (){
                  console.log(prepareWife);
              }
          }
          var deng = new Deng('Deng','xiaoliu');
      这个Deng执行完就被销毁了,但是Deng里面的方法被保存到了外部,手里还攥着Deng的
      执行期上下文,所以就是我可以用prepareWife这个变量,但是别人访问不到,
      也就实现了变量私有化
      补充:对象里是可以有函数的,一个对象里是可以有方法的,一个方法也是种函数,只不过定义方法不同
      
  4. 可以实现封装,属性私有化。
    • 例1:
        Person();
      

立即执行函数

  1. 定义:立即执行函数:执行后就将函数立即释放或者说函数的引用不会保存,唯一一个执行后就立即销毁的函数
  2. 特点:
    • 一个函数只要被定义,就会占用存储空间,永远处于等待被执行。
    • 一个函数只执行一次,叫初始化程序或针对初始化功能的函数。
    • 立即执行函数除了执行完就释放,剩余的与普通函数没有任何区别。
    • 立即执行函数有执行期上下文,并且有预编译过程。
    • 立即执行函数被读到的时候马上就被执行。
  3. 格式:两种格式
    • (function(){}()):w3c给出的标准这个较好一点
    • (function(){})():因为最后面的大括号的优先级高所以执行时和上面的那个执行顺序差不多
    • 举几个栗子
      • 例:立即执行函数的一般格式
            (function () {   //只执行一次,执行后就被立即销毁
                var a = 123;
                var b = 234;
                console.log(a + b);
            }())
        
      • 例:带参数的立即执行函数格式
            (function (a,b,c) {       //传递参数
                console.log(a + b + c);
            }(1,3,5))                 //传递参数
        
      • 例:有返回值的立即执行函数格式
            var num = (function (a,b,c) {
                var d = a + b + c + 2;
                return d;
            }(1,3,5))
        
  4. 只有表达式才能被执行符号执行,能被执行符号执行的表达式,这个函数的名字就会被自动忽略。被执行符号执行的表达式就变成了立即执行函数。,函数执行一次之后就被永久的放弃了,就剩了个声明了,后面的函数就没有了,变量里存的就不是函数的地址了,
    • 例:
          var test = function (){   
              //函数表达式。test是变量名,这个函数执行完后。就被永久的放弃了,就没了
              再执行test结果就是undefined,test()就是报错。
             console.log('a');
          }()                      
          // 执行符号就是(),()之前的只有是表达式才能被执行符号执行,
          这个例子是函数表达式,所以能被执行符号执行。还有()后面加不加;都行。
      
    • 例:
          + function test() {     
              //加上+(正号)号或者-(负)号或者"!"或者"与或非"或者"()"都行 
              不过前面得加东西符合"与或非"使用规则,加上以上就是表达式,
              所以能被执行符号执行。
              console.log('a');
          }();
      
    • 例:
            (function test(){})  //函数声明被括号包起来了,是表达式。
      
    • 例:(w3c建议使用的方式)
          (function test(){   
              //因为test是执行完然后就被释放了,再执行就会报错,
              所以函数名就可以不写了(立即执行函数的演变过程)
              console.log('a');
          }())               
          //这些括号先识别外边的,只有最外层的是数学运算小括号,
          剩下的所有括号都是有语法意义的。所以最外面的括号会将里面的东西变成表达式,
          表达式正好被执行符号执行。这个函数执行位置不在它定义位置。
      
    • 例:
          function test(a,b,c,d){ 
          console.log(a + b + c + d);
          }(1,2,3,4);        
          //系统不报错也不执行,它会将前面的函数声明与后面的 (1,2,3,4)当作两个语句分开,再执行。
      
  5. 其实也可以通俗地理解为闭包产生的原因是里面的函数活得比外面的函数还长。

利用闭包问题解决闭包

必须记住这个,怎么触发,怎么解决等

  1.     function test(){                          
            //test函数中一共有两个变量,arr与i
            var arr = [];
            for(var i = 0; i < 10; i ++){          //不管中间怎么执行的,最后i的值就为10(i = 10才能结束循环,执行完函数)
                arr[i] = function(){              //只是将函数赋给arr[i],函数中内容什么的不管,只有执行时才会考虑,所以函数中的i并不是1,2,3,4等等,不随着arr[i]变化,函数定义时不用看里面的东西,没有意义,当函数执行时再找里面的东西。
                    document.write(i  + "  ");    //系统只是将一个引用抛进去了,其中内容是什么不知道,只有当执行时才会考虑i到底是什么,系统执行时就是将一个函数抛进去了,但是函数内容是什么并不知道
                }
            }
            return arr;                           // arr中存放的是十个不同的相互独立的,但是长得一样的函数。
        }
        var myArr = test();                      //这时才会真正执行那个arr[i](一共10个函数)
        for(var j = 0;j < 10;j ++){
            myArr[j]();                          //才会真正访问test作用域,并且因为所有函数都是访问一个作用域,所以访问的都是一个i值所以输出结果为10个10
        }
    
  2. 对闭包的解决,有这一种解法!!!
        function test (){
            var arr = [];
            for(var i = 0 ; i < 10; i ++){
                (function (j){                   //立即执行函数立即被执行,里面的语句一条一条被执行
                    arr[j] = function (){        //这只是赋值语句,但并不代表这个函数就要执行,就像1中一样。这个函数保存function(j)函数的成果,每个j不一样,所以各自产生的作用域不同。当执行时,arr中存的每个函数都会各自的上层的函数(即每个不同的立即执行函数)的劳动成果,每个function(j)(立即执行函数)在执行完后都会销毁(线断了),但是由于arr中函数引用
    
                        document.write( j + " "); 这个执行过程是这样的,立即执行函数执行,j=0/1/2........,然后每次执行时,找的j是每个立即执行函数中的j,一对一,每个都不一样。
                    }
    
                }(i))
            }
            return arr;
        }
        var myArr = test();
        for( var j = 0; j < 10; j ++){
            myArr[j]();
        }
    
  3.     for(var a = 0; a < 10; a ++){
            
        }
        console.log(a);                         //可以打印出a = 10;
    

备注:课堂中闭包精细版不是新的知识,是闭包精讲。

习题:一小时45分例题上面也有例题

  • 习题1:

     
    
    
        
        frame
        
    
    
    
    • a
    • a
    • a
    • a
  • 习题2:

    
    
    
        
        frame
    
    
    
    
    
    最终结果: 2 undefined undefined 10 100 123
    
  • 习题3:题目在闭包精细版里的1小时52分钟处(就是求字节数)

    
    
    
        
        frame
    
    
    
    
    
    
  • 习题4:

    var a = (1 - 1, 1 + 1);      // a = 2
    例1:
        var f = (
                function f(){
                    return "1";
                },
                function g(){
                    return 2;
                }
        )();
        console.log(typeof f);        //结果为number
    
    • 补充一个概念:逗号运算符:若是逗号前面是表达式,就计算前面的,然后,若是逗号后面是表达式,就计算后面的表达式,都计算完了,将后面表达式的值返回。
  • 习题5:

    //所有变量未经声明就使用肯定报错,唯一一个 不报错的就是放在typeof中 并且返回undefined
        var x = 1;
        if(function f(){}){     //首先 function不是那六个中的,所以这个if语句判断条件是true,里面的内容肯定会执行,其次括号在这里不但判断条件时有用,括号还有自己的用途就是将里面的东西变为表达式,所以就不再有f函数了,所以函数再访问f时就已经消失了,返回字符串类型的undefined。
            x += typeof f;   //未经声明的变量只有放在typeof操作符中才不报错,返回undefined(字符串类型)
        }
        console.log(x);      //若是1加上非字符串类型的undefined得到的结果为非数NaN,此题结果为1undefined
    

你可能感兴趣的:(Javascript基础复习(一))