JavaScript语法(一)

前言

  • 现在Android开发需求越来越少了,快过年也没什么事,公司叫我学学前端,也罢了,技多不压身.

变量的声明:

  • js中使用var关键字声明变量,同时变量名是大小写敏感的,即A与a是两个不同的变量.如果只是声明了变量而没有赋值var a ;那么a的值就是undefined,这是js中的一个关键字,表示未定义.js是一门动态类型语言,这意味着你可以var a = 1; a = "hello",赋值不同类型.

变量提升

  • 在js中,js引擎会将所有代码的声明全部先解析,然后再一行行执行语句,这就意味着所有变量会被提到头部执行,例如console.log(a);var a = 1等价于var a ; console.log(a);a=1;,所以良好的编程习惯还是得先声明变量再使用.

区块

  • {}以一对花括号表示,对于var来说,区块不构成单独作用域,也就是说
    {
        var a = 1;
    }
    console.log(a);

在控制台上会输出a=1;这里说一下js的局部变量只在函数内部才能声明,区块中无效.可以理解为var任然是全局变量.

标签

  • 作用与kotlin中的标签一样,主要用来跳出循环等操作,声明格式:label:语句,下面是一个例子
  top:
   for(var i=0;i<10;i++){
       for(var j=0;j<10;j++){
           if(i===j===1){
               return top;
           }
       }
   } 

数据类型转换

  • js是一种动态类型语言,变量类型可以随意赋值,但是使用运算符时,如果变量类型不符合预期类型则会自动转型,举个栗子:"4"-"1",这里使用了-运算符,要求元素类型为数值,所以js会自动将字符串转为数值.运行结果为3,使用typeof输出结果为number类型,说明结果被自动转型了.
  • js也提供了几个可以强制转型的方法Number(),Boolean(),String(),下面分开细说这几个方法
Number()
  • 分为参数为原始类型与对象类型,原始类型包括数值,字符串,布尔,下面贴上一段测试代码
     //纯数字字符串
        console.log(Number('321'));
        //字符串
        console.log(Number('321abc'));
        //true
        console.log(Number(true));
        //false
        console.log(Number(false));
        //对象 null
        console.log(Number(null));
        //对象 undefined
        console.log(Number(undefined));

接下来我们来看看输出的结果情况:

输出结果

可以看到当参数是无法解析的字符串时例如包含abc输出结果为NaN(not a number),
接下来我们来看看参数传对象试试,console.log(Number({name:"xiaoming"}));,这个时候输出了NaN.
这里说一下Number()的转换过程

  1. 首先调用自身的valueof方法,如果返回原始类型,则调用Number()方法,否则进行下一步.
  2. 如果valueof方法返回的是对象,则调用toString()方法,如果toString()返回原始类型则调用Number()方法
  3. 如果toString还是返回对象,则返回NaN
String()
  • 该方法可以将任意类型转为字符串,
        console.log(String({name:"xiaoming"}));//[object object]
        console.log(String(true));//'true'
        console.log(String(false));'false'
        console.log(String(null));'null'
        console.log(String(undefined));'undefined'
Boolean()
  • 除以下五个值为false,其余皆转换为true
 undefined,null,+0/-0,NaN,''(空字符串)

值得一提的是,所有对象的转化结果都是true,甚至new Boolean(false)也是true

错误机制

  • JS通过Error对象来构造一个错误实例,它包括message属性与name属性,当然还有stack堆栈属性.
    下面介绍一下JS中原生的Error对象:
    1.SyntaxError:语法错误
    2.ReferenceError:引用错误
    3.RangeError:区间错误,通常是操作数组不当时发生
    4.TypeError :类型错误,通常使用参数或者变量不是预期类型时发生
    5.URIError :Uri相关错误
  • Try Catch
    和其他语言一样,JS提供了try catch语句帮组我们捕获错误,当然也同样可以使用finally,你也可以使用throw关键字抛出一个错误.

函数

  • 我们知道JS是一门函数式的编程语言,所以函数在JS中是一等公民一样的存在,函数可以作为参数,返回值,表达式等使用.
  • 在JS中声明一个函数如下:
  function name(params) {
          
        }

使用fuction关键字进行声明.因为JS的函数与大部分语言都很类似,接下来讲讲JS中使用函数值得注意的一些地方.
1.如果声明了多个同名函数,那么后面声明的函数会覆盖前面的函数

 function name(params) {
          console.log(1);//1
        }
        function name(params) {
          console.log(2);//2
        }
        name();

上面这段代码输出的结果为2,相信大家也注意到了,我声明的函数name有一个参数,但是我在调用它的时候并没给参数,一样不报错可以执行.这也是JS的函数一个不同点,这里用到一个知识点agrument等会再讲.在这里如果要查看定义函数的参数个数可以使用length属性,它返回函数在定义时的参数个数,比如上面上的代码的调用name.length则返回1.此时无论你在调用时输入多少个参数,它始终返回的是函数定义时的参数

函数的作用域

这里只讲ES5的作用域,因为ES6的我也没有学- -,JS中作用域分为:全局作用域局部作用域

  • 全局作用域:变量在整个程序中都存在,任何位置都可以访问
  • 局部作用域:变量只能存在于函数内部
    我们用一段代码来看看这个定义
var a = 1;
    function name(params) {
      var a = 2;
      console.log("局部作用域:" + a);
    }
    name();
    console.log("全局作用域:" + a);

作用域

可以看到局部作用域只在函数内部有作用,同时函数内的a覆盖了全局变量.同时注意函数内部的变量提升,接下来我们改造下代码看看

  var a = 1;
    function name(params) {
      console.log("局部作用域:" + a);  //undefined
      var a = 2;
      console.log("局部作用域:" + a);//2
    }
    name();
    console.log("全局作用域:" + a);//1

这个时候第一句console输出了undefined,是不是感觉应该输出全局变量1,这里就是JS的变量提升在搞怪了,上面的代码在JS引擎解析时候会变成这样:

 function name(params) {
      var  a ;
      console.log("局部作用域:" + a);  //undefined
      a = 2;
      console.log("局部作用域:" + a);//2
    }

将函数内部的var a =2变量a提到了头部,因为a在头部没有赋值,所以是undefined

函数本身的作用域
  • 由于在JS中函数是一等公民一样的存在,你就可以把它和变量看做是一样的,它的作用域和变量也是一样的,就是在其声明时的作用域.举个例子
    var a =1;
    //全局作用域
    function name(params) {
      console.log(a);
    }
    //函数内部调用
    function test(params) {
      var a = 2;
      name();
    }
    test();//1

我们在看一个作用域在函数内部的

    var a =1;
    //函数内部调用
    function test(params) {
      var a = 2;
      return function name(params) {
        console.log(a);
      }
    }
  var f= test();
  f();//2

上面这种结构又被称为闭包

参数

在JS中,参数是可以省略的,我们来看看下面的代码

  function name(a, b) {
      console.log(a + b);
    }
    name();//NaN 因为没传参数,所以a,b都是undefined,他们进行相加返回NaN
    name(1);//NaN
    name(1, 2);//3
  • 在JS中根据传递的参数的类型
  1. 传原始数值:传值传递,函数内部修改不影响原值(类似形参)
 var a = 1;
    function name(params) {
      params = 3;
      console.log(params);//3
    }
    name(a);
    console.log(a);//1

2.传对象:传址传递,会修改对象的内存地址,修改参数会影响到原值(类似实参)

 var people = {
      name:"xiaoming"
    }
    function test(params) {
      params.name="xiaomi"
    }
    test(people);
    console.log(people.name);//xiaomi

此时people.name已经变更成了xiaomi

agrument对象

这个对象主要用来在函数内部获取参数,因为js允许参数数目不定,所以需要argument来获取,它包括了函数运行时的所有参数,这里所几个常用的属性

  1. callee:返回它对应的函数
  2. length:返回函数调用时的参数个数

闭包

要理解闭包得先了解作用域,我们知道在函数的内部是可以访问全局变量的,但是外部是无法访问函数内部的变量的,但是有时候我们又需要在外部访问函数内部的变量怎么办?
我们先看下这段代码

    function f1(params) {
      var a = 666;
      return function f2(params) {
        return a;
      }
    }
    //返回函数f2
    var f2 = f1();
    //返回值a
    var a = f2();
    console.log(a);//666

我们要想拿到函数内部的变量a,又重新定义了一个函数f2,对函数f2而言,f1内部的变量是可以访问的,但是f2却是对f1不可访问的,这就是JS的链式作用域,因为函数在JS中是可以作为返回值的,我们使f2携带f1中的变量a并作为f1的返回值返回不就可以最终访问到a了吗?
总结一下闭包得两个特点:
1.读取函数内部变量
2.使这些变量始终在内存中,通过栗子说明:

  function f1(params) {
      return function f2() {
        return params++;
      }
    }
    var a = f1(5);
    console.log(a());//5
    console.log(a());//6
    console.log(a());//7

可以看到参数的值都在递增,说明上一次调用时候返回值是保存在了内存当中
3.闭包还有个用处就是用来封装私有属性和函数

函数立即执行

格式:函数+(),在函数后面立马跟上一个圆括号,表示该函数立即执行
通常这么定义(function(){}())在外面再加一个大括号,防止JS引擎解析时产生歧义.

最后

先暂时写到这了,我也是边学边写,参考的是W3CSchool和阮一峰大神的博客,各位大佬看到后有错误欢迎指出,快过年了,希望这个年假能把前端基础过一遍写点像样的东西了,毕竟
扎心

推荐阅读更多精彩内容