美文网首页js基础
[前端学习]js语法部分学习笔记,第三天

[前端学习]js语法部分学习笔记,第三天

作者: 印象rcj | 来源:发表于2017-09-22 14:35 被阅读0次

    JS循环

    for循环

    • 格式:
      for( var i=0; i<5 ; i++){
      循环代码;
      }
    • i=0,是初始化条件,即循环从几开始,在整个循环过程中,它只会在最开始的时候执行一次;
    • i<5,是退出条件,规定了循环多少次结束;
    • i++增量参数
    • 整个流程就是,先执行初始化条件,然后判断条件,满足循环一次,然后执行增量,再判断条件,再循环一次,之后就是反复的增量-判断-循环,直到条件不满足退出循环。

    多重for循环

    • 当多个for循环嵌套时,可以把里层的for循环看作外层for循环的代码
    • 外层循环一次,就执行内层的代码,执行内层for时再开始循环,当内层for循环次数完毕后,再回到外层,外层继续循环在执行内层代码
    • 所以通俗来说就是每外层执行一次循环,内层for就要循环完所有次数,再返回外层循环。比如:外层循环5次,内层循环10次,那么内层一共需要循环50次。
    • 如果不能理解,可用断点调试来看执行顺序。

    双重for循环小案例解析

    • 倒直角三角形,代码:
      for(var i=1; i<10; i++) {
      for(var j=i; j<10; j++) {
      document.write("*");
      }
      document.write("<br />");
      }
    • 这个案例的重点在于,内层循环出来的*号要越来越少,才能实现倒三角行。而满足这个条件很简单,让内层循环j=i就能实现,因为外层i循环自增会越来越大,如果内层j=i,那么在规定了j<10的情况下,就会让内层循环次数越来越少。这样就能出现倒直角三角形的效果。
    • 九九乘法表,代码:
      <style>
      table, td {
      border: 1px solid #aaa;
      }
      table {
      border-collapse: collapse;
      border-spacing: 0;
      }
      </style>
      <script>
      document.write("<table>");
      for(var i=1; i<=9; i++) {
      document.write("<tr>");
      for(var j=1; j<=i; j++) {
      document.write("<td>" + j + "×" + i + "=" + j*i + "</td>");
      }
      document.write("</tr>");
      }
      document.write("</table>");
      </script>
    • 要实现九九乘法表要满足两个条件,一:表是9行;二:第几行对应的就有几列。
    • 那么开始分析思路:既然要9行,就让外层的i循环来控制行数,i<=9就可以循环出9行内。
    • 重点在于怎么让每行显示对应的列数,这里可以用内层循环j<=i来实现。每次循环次数由i的值来控制,从而达到目的。
    • 又因为九九乘法表每行都是从1开始乘并递增,所以让j=1再输出jxi的字符串即可。

    斐波那契数列循环案例

    • 代码如下:
      var num1 = 1;
      var num2 = 1;
      var temp = 0;
      for(var i = 1; i<= x; i++){
      temp = num2;
      num2 = num2 + num1;
      num1 = temp;
      }
    • 斐波那契数列循环有一个很重要的规律的就是:第三个值等于前两个值的和。根据兔子计算的题来看,一般是求到x月时有多少只兔子。
    • 这个可以理解为是一种递增的模式,我们需要使用循环来让num1和num2通过上面的规律来增加,相当于往后走了一级。
    • 这样就很好实现了,创建一个临时变量来中转,把num2的值给临时变量,让num2 = num1 + num2,再把临时变量中原num2的值赋给num1,这样就相当于num1的值变成了num2的值,num2的值变成了后一个数字的值。不就是相当于往后走了一层吗。
    • 然后就可以根据需求来循环,求第几个月后,就循环几次,而num2的值因为是和所以就是最终结果。
      • 总结:
      • 并不是要记住这两个案例怎么写,两个案例都是循环值交叉作用的体现。要透彻的理解案例的原理,并举一反三学会在实际工作中具体的应用。

    for in遍历

    • 使用for in不仅可以遍历数组,还可以遍历非数字索引的对象。key是一个存储键的变量,in后面跟上要遍历的数据名称。
      for (var key in obj) {
      console.log(obj[key]);
      }

    while循环

    • 格式:
      var i = 0;
      while(条件表达式) {
      内容代码
      i++;
      }
    • 可以看到while和for虽然写法上略有不同,但是循环规律都是一样的,都是通过初始值、循环条件、自增量来组成。基本上for能做的while也能做,但是如果做数字方面的循环,for会看得更清晰一些。
    • 利用循环求1加到100值时,除了初始值外还需要声明一个存放结果的变量来配合。

    do while循环

    • 格式:
      var i = 0;
      do {
      内容代码;
      i++;
      } while(条件表达式)
    • do while是while循环的一种拓展方法,从书写顺序就能知道,do while是先执行再判断。
    • 那么就可以得出do while和while的区别了:while是先判断,如果不满足则不执行;do while是先执行一次,再进行判断,不满足就不执行。所以如果使用do while循环,就算条件不满足也会执行一次。

    continue和break的区别

    • continue跳过本次循环,略过下面的代码,直接进入下一次循环,可用“计算1-100偶数和案例”来深入理解;
    • break跳出所有循环,直接结束。可用“循环出1-100之间第一个能被3整除的数案例”来深入理解。如果要使用break跳出多层循环,需要使用标记来结束。例如:tag{ for(){ for(){ break tag}}},在循环外部做一个标记,然后在内层使用break时跟上标记,就能结束多层循环

    JS数组

    • 数组的三种声明赋值方式
    • 第一种使用构造函数创建:
      var arr = new Array("值1","值2".....);
      注意这里如果构造函数参数写多个值就是正常的数组赋值,如果只写一个是设置数值的长度。
    • 第二种通过字面量直接创建:
      var arr = ["值1","值2"....];
    • 第三种:(第三种稍微特殊一点,先声明空的数组,然后再根据数字索引来写入值,数组的数字索引默认是从0开始)
      var arr = [];
      arr[0] = "值1";
      arr[1] = "值2";
    • 因为JS弱类型的特性,可以数组内可以放任意类型的值,但是实际工作中基本不会这样做。

    数组的访问

    • 普通访问:
      数组名[索引值];
    • 获取数组的长度:
      数组名.length;
      • 数组length属性
        • 数组length属性是非常强大的,因为它的会随着数组个数变化而动态改变,所以能应用到的地方很多。比如:
        • 通过length来个数组追加值。数组[数组.length] = 值,通过规律可以发现,空数组是length为0,那么第一次赋值就是给0索引号赋值。第二次length变成1了,那么再次赋值就给1索引号赋值。以此类推,你会发现不不用写索引号,直接放入length属性就可以保证从0开始连续追加值。
        • 另外给length赋值可以直接影响数组内容的个数。
    • for循环遍历数组:
      for( var i = 0; i < 数组名.length; i++) {
      console.log(arr[i]);
      }
    • for循环直接让循环值小于数组长度即可(因为索引值是从0开始,所以必须小于才不会多循环一次)
    • for反向遍历数组
      for( var i = 数组名.length-1 ; i >= 0 ; i--) {
      console.log(arr[i]);
      }
    • 反向遍历,就让循环初始值等于该数组的最大索引号,然后用递减的方式倒序来输出即可。
    • 使用for循环来给数组赋值时,可以配合length属性来用。一个空数组,它的length就是0,那么就可以把length当作是它的初始索引号,然后循环过程中,每附一个值length会自动增加1。但是这个方法仅限初始数组是空数组。

    如何找出数组中最大最小值及其索引

    • 首先要分析需求,目的是要找出四个值。那么肯定需要四个变量来装这四个值。
    • 接下来用假设值的方法来遍历数组并获取结果
    • 代码如下:
      var arr = [99,22,38,45,23,84,48];
      //假设最大值最小值和索引,然后通过循环判断来改变
      var max = arr[0];
      var maxIndex = 0;
      var min = arr[0];
      var minIndex = 0;
      //这时就可以通过for循环来把数组中所有值找出来,并依次判断
      for(var i=0 ; i< arr.length; i++) {
      //因为假设的max是最大的值,如果它小于当前循环出来的值,那么证明当前值更大,就把当前值赋给max,同时索引号也要变
      if(max < arr[i]) {
      max = arr[i];
      maxIndex = i;
      }
      //因为假设的min是最小的值,如果它大于当前循环出来的值,那么证明当前值更小,就把当前值赋给min,同时索引号也要变
      if(min > arr[i]) {
      min = arr[i];
      minIndex = i;
      }
      }
    • 记住,找出最大或者最小值,必须要先设置一个假设值,再遍历比较。

    翻转数组

    • 翻转数组实际上是利用for循环,把一个数组的所有值以反向的顺序赋给另一个数组
    • 那么根据这个逻辑,就可以分析出一个规律:翻转数组[索引号] = 原数组[数组长度-1-当前循环索引号],这样就实现了数组的翻转务必亲手写几次,理解原理)。
    • 代码如下:
      var arr = ["值1", "值2", "值3", "值4", "值5"];
      var arr1 = []; 要声明一个空数组来装翻转的数据
      for(var i=0; i<arr.length; i++) {
      arr1[i] = arr[arr.length-1-i]; 这样arr1[0]的值就是arr最后一个索引的值,然后循环i的值变化,arr1的索引值越来越大,arr的索引值越来越小,这样就实现了一个反顺序的数组值赋值。
      }
    • 也可以通过倒序遍历来把值正序赋给一个新的变量,也能实现翻转。
      var arr= [];
      var newArr = [];
      for (var i=arr.length-1; i>=0; i--) {
      newArr[newArr.length] = arr[i];
      }

    数组冒泡排序案例

    • 需求是把数组中所有的值按从小到大或者从大到小的顺序进行排列
    • 实现代码(以从小到大为例,比较符反过来就是从大到小了)如下:
      var arr = [19,20,85,24,28,94,30,1,22];
      //外层循环控制一共循环多少次才能把所有值都排序好,根据排序规律可以得出,需要数组值总数-1次循环才能完成排序,又因为是从0开始,所以得小于数组值总数-1次
      for (var i=0; i<arr.length-1; i++){
      //声明一个外层循环的值
      var flag = true;
      //内层循环控制数组每个值要比较多少次才能到位置,正常情况也是需要数组值总数-1次循环,但是又因为每次循环会固定好一个值的位置,因此循环次数是有规律的递减。
      for( var j=0; j<arr.length-1-i; j++) {
      //开始前后两个值两两比较,如果前面的大就放到后面去
      if(arr[j] > arr[j+1]){
      flag = false; //只要重新赋值就说明顺序不对,在交换,如果没有重新赋值就是没有进来,说明所有顺序是对的
      var temp = arr[j];
      arr[j] = arr[j+1];
      arr[j+1] = temp;
      }
      }
      //如果经过循环flag的值没有改变,则表示已经是正确顺序,直接跳出不再执行
      if(flag) {
      break;
      }
      }
    • 大致原理代码已经说清楚了,多敲几遍理解就好。只要明白冒泡排序需要双层for来控制比较次数和所有值排序次数,以及循环次数的规律、前后值交换需要一个临时变量即可。

    JS函数

    • 函数是可以重复执行的代码块
    • 格式:
      function fun(形参1,形参2...) {
      函数内部代码
      返回值
      }
      fun(实参1,实参2) 在调用函数时,传入参数实现不同结果
    • 函数是可以多次重复调用的代码块,有助于节省代码量易于维护。有点类似于css的类选择器。
    • 参数可以写也可以不写。实参可以是任意形式的值。如果实参比形参多,多出来的实数会浪费掉;如果实参比形参少,多出来的形参会默认为undefined。另外传入参数时,只是把实参的值取出来复制了一份给形参,并不是直接把实参传入了。
    • return就是返回值,在函数中return有结束函数运行的作用,执行到它函数就结束了,并通过return把执行结果返回到函数调用的地方。
    • js中没有重载,所以函数名不能相同,否则会覆盖。
    • 函数因为有预解析,执行顺序会先执行调用代码,再进入函数内部执行,不受书写顺序的影响,在浏览器调试工具中,有对应进入函数内调试的按钮。

    函数声明提升

    • 在JS中,会默认把函数声明提升到最顶端,就是说最优先加载。变量声明也会声明提升,但是变量只提升的声明却不提升赋值。所以用表达式形式声明函数,并在函数前调用会出错。

    全局变量和局部变量

    • JS中在外部声明的变量就叫做全局变量,它的特点是在任何地方都能正常使用
    • 局部变量是指在函数内部声明的变量,局部变量只能在声明的函数内使用,在外部调用会报错。

    函数的递归

    • 通俗来说,递归就函数在内部自己调用自己。递归一定要有跳出条件。这里只是做一个基础的了解,后面会深入了解。
    • 一个递归累加的案例,稍作了解:
    • 分析累加的规律,1的累加是1,2的累加是2+1,3的累加是3+2+1,4的累加是4+3+2+1。可以看到实际上累加是当前值加上前一个数的累加,那么得出累加公式n = n + (n-1的累加),那么通过递归实现就是:
      function num(n) {
      //必须要作一个判断当做结束条件,不然递归会无限循环
      if( n == 1) {
      return 1;
      }
      var num = n + num(n-1);
      return num;
      }
      num(n);
    • 实际是就是通过递归函数,来反复调用自己来计算前一个数的累加值,一直到1就结束递归,并返回给第一次递归调用时。

    回调函数

    • 因为函数实际上也是一种数据,它既可以调用,也可以当作值来赋给其他类型。那么把函数当作参数来传给其他函数,也是可以实现的。
    • 这种把一个函数当作参数传给另一个函数,就叫做回调函数。注意传入回调函数时,只传入函数名,不需要带上括号,等于是把函数内部的代码传进去,并不是立刻执行

    函数求三个数中的最大值

    • 代码如下:
      function getMax(a, b, c) {
      renturn (a>b ? a : b) > c ? (a>b? a : b) : c;
      }
    • 比较三个值就用三元先比较前两个,得到大的,再和第三个用三元比,再返回比较出来大的那个。

    JS复杂数据类型:object对象

    • js的数据类型分为基本数据类型(数值型、字符型、布尔型、空型、未定义型),复杂数据类型(对象)
    • 对象实际上是对存在事物的抽象化表示,所以也有种说法叫万物皆为对象。
    • 对象和基本数据类型的最大的区别就是,对象有属性和方法。
    • 属性代表这个对象的名词(年龄、性别、姓名等),方法代表这个对象的动词(说话、走路、唱歌、跳舞等)
    • 注意对象和它的属性与方法是通过.号或者[]来连接。
      • 对象的声明和赋值的两种方法:
      • 构造函数创建:
        var obj = new Object(); 声明一个叫obj的对象
        obj.name = xxx; 声明obj对象的姓名属性
        obj.sex = "男"; 声明obj对象的性别属性
        obj.run = function(形参) { 声明obj对象的走路方法
        return "我在走路"
        };
        obj.talk = function(形参) { 声明obj对象的说话方法
        return "我在说话"
        };
      • 字面量创建
        var obj = {
        name: xxx;
        sex: "男";
        run: function() {
        return "走路";
        }
        }
      • 因为方法是动作,所以需要用函数来承载,也可以说函数在对象中叫做方法。以上声明的属性和方法都是属于obj这个对象的。
    • 当然因为对象也是无序的键值对组成,所以也可以使用类似数组的方法来赋值和访问。这种方式更加灵活,可以再中括号内使用字符串拼接或者变量等。
      var obj = {};
      obj['name'] = xxx;
      obj['run'] = function(){};
    • 对象属性可以存放任意类型的数据。
      • 对象的访问:
        console.log(obj); 打印出这个对象所有的属性和方法
        console.log(obj.name); 访问属性值需要将值打印出来
        obj.run(实参); 方法调用与函数类似,直接写对象名.方法名()就可以
      • 总结,任何对象的属性和方法,它们的区别都是方法有小括号(),属性没有。

    键值对的概念

    • 实际上我们已经见过很多键值对了,比如数组、css、字典等,都是键值对。
    • 键值对,就是一个键对应一个值,当我们想使用或查找某个值时,直接调用对应的键就可以了,这样可用大大调高效率。而对象也是键值对,属性名或者方法名就是键,存储的内容就是值。调用名字就能获取值或者执行代码。

    this伪变量

    • 三种情况:
      • 1.当this在全局上下文时(即函数外部),它对应的是window
      • 2.当this在函数内部时,它对应的是当前函数所属的对象
      • 3.在构造函数中时,会改变this的指向,直接指向对象不再根据所属,并返回这个对象

    批量创建对象

    • 工厂函数模式:方法很简单,就是把创建一个对象的流程代码放进去函数封装起来,并给函数写上所有对象属性对应的形参(方法一般是在内部写好,很少传进去),在函数最后返回创建好的对象。当外部调用这个函数时就创建了一个对象,而且根据实参的不同,每次创建的对象值也不同。就像是其他编程语言中的类。
    • 构造函数模式:为了解决工厂函数模式的一些缺点(不能查看对象类别),构造函数就诞生了。大致的写法相似,但有一些变化。构造函数模式中,不需要手动创建对象,所有的属性和方法赋值时都用this.名称的方式来写,并且因为this的特性不用再写返回值。创建对象时,使用new关键字。
    • 一个手机构造函数案例:
      //一般构造函数使用名词命名,且首字母大写
      function Phone(brand, color, price) {
      this.brand = brand;
      this.color = color;
      this.price = price;
      this.call = function() {
      console.log("打电话");
      }
      this.message = function() {
      console.log("发短信");
      }
      }
      var huaweiPhone = new Phone("华为","黑色",5000);
      console.log(huaweiPhone.color); //因为huaweiPhone是通过Phone创建的实例对象,所以创建后直接用实例名调用属性和方法就可以了
      huaweiPhone.call();
    • 可以通过(A instanceof B)来比较A是不是通过B创建出来的实例对象。是返回true,否则返回false

    数据存放的位置

    • 在JS中基本数据类型的值存放在栈内层中,复杂数据类型的值存放在堆内存中。
    • 因为基本数据类型存储数据比较少,所以直接把值放在栈内层中访问速度很快。把一个基本数据赋值给另一个基本数据,它们虽然值相同,但实际因为直接把值放过去所以是两个引用地址。它们完全独立,所以任意一基本数据再重新赋值后另一个不受影响 。
    • 复杂数据类型因为占用空间较大,所以是把值放到堆内存中,而栈内层开辟的空间只是存放了一个指向堆内存引用地址。这是如果把一个复杂数据赋值给另一个复杂数据,只是在栈内存中把引用地址赋给了它,而实际上还是共用的存放在堆内存中的值,所以当修改了堆内存中数据时,因为引用地址相同所以值会一起变。
    • 把基本数据类型和复制数据类型作为实参,传入函数中,再做修改,结果和上面说一样。结论就是:在栈内存中存放的值,那么赋值过去,怎么修改都不会影响。但是如果存放的是引用地址,那么赋值过去,修改会一起变(有一种特殊情况,就是传入之后,有又创建了一个对象赋值给形参,那么形参的引用地址就变了)。
    • 可以找具体的图来看,更容易理解。

    内置数学对象

    • 因为内置的数学对象太多,这里不做记录,去课件里看。

    JS基础测试题总结

    • 未定义的变量,使用.toString()转换字符串函数,会直接报错。
    • continue只能在循环中使用。
    • 'var a= + 任意类型',JS加法运算中,加号左边不写或者null或者undefined+任意类型,都会转换成数值型。无效数字返回NaN
    • JS是面向对象的语言,不是面向过程
    • ++在前先自增再执行,++在后先执行后自增

    相关文章

      网友评论

        本文标题:[前端学习]js语法部分学习笔记,第三天

        本文链接:https://www.haomeiwen.com/subject/gsgosxtx.html