能够表示并操作的值的类型叫做数据类型,编程语言最基本的特征就是能够支持多种数据类型。变量则是值的符号名称,通过名称可以获得值的引用。这个机制是编程语言的另一个基本特征。

JavaScript的数据类型分为两种:原始数据类型(primitive type)和对象类型(object type)。JavaScript的原始类型包括数字,字符串和布尔值。还有两种特殊的原始值:null(空)和undefined(未定义)。代表了各自特殊类型的唯一的成员。

Javascript除了原始类型,就是对象了,对象是属性的集合,每个属性都由“名/值对”构成,其中有一个比较特殊的对象---全局对象。

普通的JavaScript对象是“命名值”的无序集合,同样定义了一种特殊的对象--数组,表示带编号的有序集合。

函数是具有与它相关联的可执行代码的对象,通过调用函数来运行可执行代码,并返回运算结果

如果使用函数来初始化(使用new运算符)一个新建的对象,我们称之为构造函数。每个构造函数定义了一类(class)对象--由构造函数初始化的对象组成的集合。类可以看成是对象类型的子类型。

除了数组类和函数类之外,还定义了日期类来代表日期的对象,正则类表示正则表达式,错误类表示JavaScript程序中运行时错误和语法错误

JavaScript解释器有自己的内存管理机制,可以自动对内存进行垃圾回收。程序可以按需创建。

JavaScript的变量是无类型,可以被赋予任何类型的值,使用关键字var来声明变量,JavaScript采用词法作用域管理,不在任何函数内声明的变量称做全局变量,在程序中任何地方都可见,在函数内声明的变量具有函数作用域,自在函数内可见。

3.1 数字

JavaScript所有数值均用浮点数值表示,采用64位的浮点格式表示数值。

3.1.1 整型直接量

  • 用一个数字序列表示一个十进制整数
  • 以“0”+由0~7之间的数字组成的序列表示8进制(ES6不支持8进制)
  • 以“0x”或“0X”+十六进制数串的直接量表示一个十六进制数

    3.1.2 浮点型直接量

    digits[(E|e)[(+|-)]digits]

    3.1.3 JavaScript中的算术运算符

    +  -  *  /  %

    JavaScript的算术运算在溢出,下溢或被零整除时不会报错。
    溢出的数值按照正负(+|-)无穷大表示(Infinity),基于他们的加减乘除还是无穷大,保留正负号。
    下溢返回0,负数发生下溢时,返回负零。
    被零整除时返回正负无穷大,零除以零则返回非数(NaN),另外,给任意负数开平方运算,或者算术运算符与不是数字或者无法转换为数字的操作数一起操作时,均会返回NaN.

非数值有一点特殊,它和任何值都不相等,包括自身,也就是说,没有办法通过X==NaN来判断变量x是否时NaN,用当使用X!=来判断,当且仅当x为NaN时,来返回true。函数ISNAN()的哦那个作业与此类似。

正零和负零在数值上时相等的,除了做为除数。

3.1.4 二进制浮点数和四舍五入错误

实数由无数个,但JavaScript通过浮点数只能表示有限个数,其采用的二进制浮点数表示法并不能精确表示类型0.1这用的数字。

var x=.3-.2;
var y= .2-.1;
x==y   // false,两值不相等

进行使用大整数进行金额计算,

3.2 文本

字符串是由一组16位值组成的不可变的有序序列,来自于Unicode字符集。JavaScript通过字符串类型来表示文本,字符串的长度是其所含16位值的个数。空字符串的长度为0

3.2.1字符串直接量

JavaScript中的字符串直接量由单引号或双引号括起来的字符序列。单引号定界的字符串可以包括双引号,由双引号定界的字符串可以包含单引号。
如果JavaScript夹杂HTML代码的字符串和HTML中夹杂JavaScript代码,最后使用各自独立的代码风格。例如在JavaScript中使用双引号表达字符串,而在html中使用双引号表达字符串。

3.2.2 转义字符

JavaScript中使用反斜线(\)作为转义符,JavaScript中转义字符如下
Alt text
其中由两个是通用的,通过十六进制数表示Latin=1或者Unicode中的任意字码,例如\xA9表示版权符号,版权符号在Latin-1的编码是十六进制的A9,同样,\u表示由4个十六进制数制定的任意Unicode字符。

3.2.3 字符串的使用

在ES5中,字符串可以当作数组使用,除了使用charAt()方法外,也可以使用方括号来访问数组中的单个字段。

3.2.4 模式匹配

JavaScript定义了 regexp()构造函数来定义正则表达式对象,采用perl中的正则表达式语法,String对象和RegExp对象均定义了利用表达式进行模式匹配和查找与替换的函数。
当然,正则表达式依然由直接量的写法,在两条斜线之间的文本构成了一个正则表达式直接量,第二个斜线后面可以跟随一个或者多个字母,用来修饰匹配模式的含义。

3.3 布尔值

任意JavaScript对象都可以被转换为布尔值,下面这些值将会转换成false

  • undefined
  • null
  • 0
  • -0
  • NaN
  • ""

其他所有值包括所有对象和数组都会转换成true。

if(o!==null){//o不等于null时才执行
}

if(o){//o转换为布尔值时,等于true时才执行
}

3.4 NULL和undefined

null表示一个特殊的值,用来描述“空值”,对null进行typeof预算,结果返回字符串“object”
undefined来表示这个属性或元素不存在,如果函数没有任何返回值,则返回undefined,引用没有提供实参的函数形参的值也只会得到undefined。对其进行typeof,返回“undefined”,表明这个值是这个值的唯一类型,undefined是预定义的全局变量。

3.5全局对象

全局对象的属性是全局定义的符号,当JavaScript解释器启动时,它将创建一个新的全局对象,并给他一组定义的初始属性。

  • 全局属性:比如undefined、infinity、NaN
  • 全局函数,比如isNaN()、parseInt()、eval()
  • 构造函数,比如Date()、RegExp()、String()、Object()、Araay()
  • 全局对象,比如Math和JSON

在代码的最顶级-不在任何函数内的JavaScript代码-可以使用关键字this来引用全局对象。
在客户端JavaScript中,在其表示的浏览器窗口中的所有JavaScript代码中,Window对象充当了全局对象,这个全局的Window对象有一个属性window引用其自身,它可以代替this来引用全局对象。

3.6 包装对象

在存取字符串、数字或布尔值的属性时创建的临时对象称做包装对象,它只是偶尔用来区分字符串和字符串对象,数字和数值对象,布尔值和布尔值对象。通常,包装对象只是被看作一种实现细节,不用特别关注。

3.7 对象引用

JavaScript中原始值与对象有着根本区别,原始值不可改变,任何方法都无法改变一个原始值,原始值的比较是值的比较,只有在他们值相等时,他们才相等。
对象的比较并非值的比较,即使两对象包含同样的属性以及相同的值,他们也是不想等的。各个索引元素完全相等的两个数组也不相等。

var o={x:1},p={x:1}
o===p //false,两个单独对象不相等
var  a= [],b=[]
a===b //false,两个单独数组不相等

对象成为引用类型,对象的值都是引用,对象的比较时引用的比较当且仅当他们引用同一个基对象时,他们才相等。

3.8类型转换

JavaScript类型转换:

原始值到原始值的转换相对简单,已经明确定义了。
原始值到对象的转换也比较简单,原始值通过调用String()、Number()、Boolean()构造函数转换为他们各自的包装对象。

3.8.1 转换和相等性

基于JavaScript灵活的类型转换,“==”相等运算符也随相等的含义多变,例如一下均返回true

null == undefined //这两个值被认为相等
"0"==0            //在比较之前字符串转为数字
0==false          //在比较之前,布尔值转换为数字
"0"==flase        //在比较之前,字符串和布尔值都转换为数字

需要注意,一个值转换为另一个值并不意味着这两个值相等,比如,在期望使用布尔值的地方使用了undefined,它将转为false,但不意味着undefined==false。if语句将undefined转换为false,但==运算符从不试图将操作数转换为布尔值。

3.8.2 显示类型转换

做显式转换最简单的方法时使用Boolean()、String()、Number()或者Object()函数。
其他的例如

  • tring()
  • Fixed():根据小数点后的指定位数将数字转为字符串,
  • toExponential():使用指数计数法转换为指数形式的字符串。其中小数点只有一位,,小数点后的位数则由参数设定
  • toPrecision():根据指定的有效数字位数将数字转为字符串。如果有效数字的位数少于整数部分,则转换成指数形式,所有三个方法都会适当的进行四舍五入或填充0
  • parseInt():第二个参数时指定转换的数字的进制数,由最终返回一个十进制的数

JavaScript中某些运算符会做隐式的类型转换,例如:+用于字符换转换,!用于将操作数转换为布尔值并取反。例如:

X+"" //转为字符串
+x //等价于Number(x),也可以写成x-0
!!x //等价于Boolean(x)
3.8.3 对象转换为原始值

对象转为布尔值:所有的对象都转换为true,new Boolean(false)也是true。因为其为一个对象
对象到字符串和到数字时通过调用转换函数完成的
1:toString()

[1,2,3].toString() //=》“1,2,3”
({x:1,y:2}).toString() //=》"[object Object]"
(function(x){f(x)}).toString() //=》"function(x){\n f(x)\n}"
/\d+/g.toString() //=》"/\d+/g"
new Date(2010,0,1).toString() //=》"fri Jan 01 2010 00:00:00: GMT=0800(pst)"

2:valueOf():如果存在原始值,它将默认将对象转换为表示它的原始值;日期类型的valueOf()方法会返回它的一个内部表示:1970年1月1日以来的毫秒值。

JavaScript中对象到字符串的转换经过了如下这些步骤:
1:具有toString()方法,首先调用这个方法
2:如果没有toString()方法,则调用valueOf()方法
3:都没有则抛出一个类型错误异常

JavaScript对象到数字的转换过程
1:具有valueOf()方法并且返回一个原始值,则将这个原始值转换为数字并返回
2:如果没有valueOf()方法,则调用toString()方法并且后者并返回一个原始值,则将这个原始值转换为数字并返回
3:都没有则抛出一个类型错误异常

1 +,== !=和关系运算符:执行对象到原始值的转换,而不是其他的运算符的方法执行对象到数字的转换。
2 其他执行对象到数字的转换即首先调用valueOf然后调用tostring,但是取得的原始值直接返回,不会被强转为字符串或数字,日期类型则是使用对象到字符串的模式,但不会被强转为字符串。

日期类型比较特殊,它定义了有意义的向字符串和数字类型的转换例如:

  • ==使用日期到字符串(原始值),其他则是日期到数值的转换(区别在于关系运算符需要转为数字)

    var now = new Date();//定义一个日期对象
    typeof(now+1);//"String" +将日期转换为字符串
    typeof(now-1);//“number”-使用对象到数字的转换
    now==now.toString();//true,隐式的和显式的字符串转换
    now>(now-1);//>将日期转换为数字

    3.9 变量声明

    变量声明采用var进行声明。如

    var i;
    var sum;

    也可以一个var关键字声明多个

    var i,sum;

    还可以将变量赋值和声明合写在一起

    var message="hello";
    var i=-0,j=0,k=1;

    如果未在var声明语句中给变量指定的初始值,在存入一个值之前,它的值就是undefined。
    可以在for和for/in语句中使用var语句。、
    重复声明且带有初始化,就和简单的赋值没有区别。
    全局变量编写代码时可以不用是var,但是不推荐。

    3.10 变量作用域

    一个变量的作用域是程序源代码中定义这个变量的区域。全局变量拥有全局作用域,在JavaScript代码中任务地方都是有定义的,然后再函数内声明的变量只在函数体内有定义,他们是局部变量,作用域是局部性的,函数参数也是局部变量,他们只在函数体内有定义。
    在函数体内,局部变量的优先级高于全局变量。如果函数内声明的一个局部变量或者函数参数中带有的变量和全局变量重名,那么全局变量就被局部变量所遮盖

    3.10.1 函数作用域和声明提前

    函数作用域:变量在声明它的函数体以及这个函数体嵌套的任意函数体内都是有定义的。

    function test(o){
     var i=0;                      //i在整个函数体内均有定义
      if(typeof o=='object'){
          var j=0;                 //j在整个函数体内均有定义,不仅仅在这个if代码段
          for(var k=0;k<10;k++){   //k在整个函数体内均有定义,不仅仅在这个循环体内
              console.log(k);      //0~9
          }
          console.log(k);          //输出10
      } 
      console.log(j);              //有定义,但可能没值
    }

    JavaScript的函数作用域是指在函数内声明的所有变量在函数体内始终可见,这意味着在声明之前已经可用,被称为声明提前。

    var scope="global";
    
    function f(){
      console.log(scope); //输出“undefined”,而不是global
      var scope="local";  //变量在这里赋初始值,但变量本身在函数体内任何地方均有定义
      console.log(scope);  //输出local
    }
    3.10.2 作为属性的变量

    当声明一个全局变量时,实际上是声明了全局对象的一个属性。JavaScript允许使用this关键字来引用全局变量,但没有方法可以引用局部变量中存放的变量。

    3.10.3 作用域链

    JavaScript是基于词法作用域的,全局变量在程序中始终有定义,局部变量在声明它的函数体内以及其所嵌套的函数内始终有定义
    每一段JavaScript代码(全局或者函数)都有一个与之关联的作用域链,这个作用域链是一个对象列表或者链表。这组对象定义了这段代码“作用域中”的变量,当JavaScript需要查找变量x的时候,他会从链中的第一个对象开始查找,如果存在则直接使用这个属性的值,不存在,继续查找链上的下一个对象,以此类推,如果都没用则报引用错误。

  • 在最顶层的代码中,作用域链由一个全局对象组成
  • 在不包含嵌套的函数体内,作用域由两个对象,第一个是定义函数参数和局部变量的对象,第二个是全局变量
  • 在嵌套的函数体内,作用域至少有三个对象。

定义一个函数时,它实际上时保存一个作用域链。当调用这个函数时,它创建一个新的对象来存储它的局部变量,并将这个对象添加至保存的那个作用域链上。同时创建一个新的更长的表示函数调用作用域的链。
对于嵌套函数来讲,每次调用外部函数时,内部函数又会重新定义一遍,因为每次调用外部函数的时候,作用域链都是不同的。

Last modification:July 10, 2020
If you think my article is useful to you, please feel free to appreciate