我们应该熟练掌握的js基础知识一语言基础(摘录自javaScript高级程序设计第4版)

335 阅读22分钟

什么是javascript?

javaScript诞生的时间是1995年,是一个名副其实的95后。当时,它的主要用途是用来代替服务器提前做一些验证,当时负责开发它的是一个网景公司的工程师,网景为了赶上发布时间,和Sun公司合作,那时,它并不叫js,而是LiveScript,当时为了蹭java语言的热度,才改名为javaScript。这波操作着实厉害,迅速提高了它的知名度。直到现在估计很多新人第一次看到js的时候还会疑惑它跟java有什么联系。其实,没啥联系,蹭热度的联系哈哈哈。不过现在的js的确是一种非常主流的语言,非常流行,深受开发者喜爱。刚开始了解js的同学可能也会有另外一个疑惑,就是ECMAScript和js有什么联系和区别。下面我以个人的角度和理解分享下我的看法,我们可以把ECMAScript看成是一个标准,规范,为了规范js的实现,世界上就有一个权威的组织编制了这套规范,目的就是想让js往统一,一致性上发展,如果各个公司实现的太五花八门的话,不利于js语言的发展和使用。虽然js和ECMAScript基本上是同义词,但js远远不限于ECMA-262所定义的那样,完整的js实现包含了以下几个部分:核心是ECMAScript,文档对象模型(DOM),浏览器对象模型(BOM)。js的这三大部分得到了五大web浏览器IE,Firefox,Chrome,Safari,Opera不同程度的支持,所有的浏览器基本上对ES5提供了完善的支持,而对ES6和ES7的支持度也在不断上升。

script元素

前端中使用script元素主要是在html文件中,在html文件中使用script元素可以引用一个外部js文件,也可以创建一个行内脚本。这个元素是由网景公司创造的,并最早在Netscape Navigator2中实现的,后来这个元素被正式的加入到HTML规范中,它包括几个常用的属性,如async,defer,src等,async表示应该立刻下载脚本,但不能阻止其他页面动作,比如下载资源和加载其他脚本,正因为如此,异步脚本不应该在加载期间修改DOM,它只对外部文件有效,当同时有多个异步的脚本加载的时候,不能保证他们的执行顺序,异步脚本保证会在页面的load事件前执行。defer表示在文档解析和显示完成后再执行脚本,同理,是该属性的脚本也不应该在加载时修改DOM,只对外部文件有效,我们可以暂时认为如果同时多个defer脚本加载的话,他们的执行顺序就是在文件中的先后顺序。注意的是,如果一个script元素定义了一个src属性,那么浏览器就会把它当作外部js文件去加载,这时候,无论我们在<script></script>中间写任何代码都不会去加载执行的。还有就是为了加快页面渲染速度,我们需要把script元素放到body元素内最后面。另外,script元素也可以通过js创建,这种方式创建的script元素,默认是异步加载的,这种方式创建的话,可能有些浏览器不支持async属性,我们可以将它设置为同步加载,如script.async = false,但是以这种方式获取资源对浏览器预加载器是不可见的,这回影响他们在资源获取队列中的优先级,所以我们可以在文档头部声明它如,

<link rel='preload' href='a.js'>

总结:
1:页面要包含外部js文件时,必须将src属性设置为要包含文件的url,文件可以跟网页在同一个服务器,也可以位于完全不同的其它域。
2:所有script元素会按照在页面中的顺序依次解释执行,前提是不使用async和defer。
3:对不推迟执行的脚本,浏览器必须先加载完该脚本,才能继续渲染剩下的页面,所以我们要把script放到body结束标签之前。
4: 可以使用defer将脚本的执行推迟到文档渲染完毕后,推迟的脚本会按照被列出的顺序执行。
5: 通过使用<noscript>元素可以指定在浏览器不支持脚本时显示的内容。如果浏览器支持脚本,则该标签内的内容不可见,不会渲染。

语言基础

区分大小写

js中无论是变量,函数名,操作符都区分大小写。

标识符

标识符就是变量,函数名,函数参数,属性的名称。标识符的命名有一套规则,如下:
1:第一个字符必须是字母,下划线,$中的任意一个。
2:推荐使用驼峰式命名。

注释

// 单行注释  
/*多行注释*/

严格模式

es5增加了严格模式,这个模式会对一些不规范的使用报错。开启严格模式需要在脚本的第一行写上

"use strict"
//如果我们想要在函数内执行严格模式,可以在函数的第一行写上"use strict" 
//所有的现代浏览器都支持严格模式

语句

js中的语句以;结尾,省略掉分号的话意味着由解析器确定语句在哪里结尾(可能会影响一点性能)。最好加上分号,养成良好的编程习惯。注意,使用if的时候即使代码块中只有一行代码,最好也加上{}。

关键字和保留字

关键字和保留字不能用来定义标识符。

变量

js中的变量可以用来保存任意类型的数据,我们可以把它理解为一个引用,指向内存中某一块地址。定义一个变量可以使用var,let,const。var在所有版本都支持,let,const只能在es6及之后的版本支持。
var:用来定义一个变量,var str = 'message'; 这样定义的变量str保存的是一个字符串,如果初始化没赋值的话,str就是一个undefined。
注意:在函数内部可以用var定义一个局部变量,如果省略掉var的话,该变量会成为一个全局变量。局部变量会在函数调用退出之后立即被销毁。不推荐省略var定义变量,可能会提高维护成本。
var str='123', a=false, b=12;一次定义三个变量。
var定义的变量存在声明提升,所有使用var定义的变量的声明都会提升到该函数作用域的顶部。
let: let跟var的作用差不多,但也存在部分区别,let定义的变量的作用域是块作用域,只有在同一个块中能访问,超出这个块则无法访问。var是函数作用域,超出函数外是无法访问的。
在同一个块中let定义的变量名不能重复。否则会报语法错误。注意的是,var和let在同一个块中定义了重名的变量也会报错。
let和var的一个重要的区别是,let定义的变量不会在作用域中有声明提升。 (暂时性死区)

  console.log(age) //报错,age未定义
  let age = 18;

与var不同的是,let在全局作用域中声明的变量不会成为window的属性。
for循环中的let声明:在let出现之前,for循环定义的变量会渗透到循环体外部,

for(var i=0; i<5; i++) {
  
}
console.log(i);//5
//let
for(let i=0; i<5; i++) {
  
}
console.log(i);//未定义
//
for(var i=0; i<5; i++) {
  setTimeout(()=>{
    console.log(i);
  },0)
}
console.log(i);//55555

for(let i=0; i<5; i++) {
  setTimeout(()=>{
    console.log(i);
  },0)
}
console.log(i);//01234

const的行为和let基本相同,唯一一个重要的区别是用它声名变量的时候必须同时初始化变量,且尝试修改const声明的变量会导致运行时错误。注意:const声明的限制只适用于它指向变量的引用,换句话说就是如果它指向的是一个对象的话,我们可以修改对象的属性,这并不会违反const的限制。

数据类型

js中六种简单数据类型:Undefined,Null,Boolean,Number,String,Symbol。Symbol是es6新增的,还有一种复杂数据类型叫Object。
因为js中的类型都是松散的,所以需要一种手段来确定任意变量的数据类型。typeof操作符就是为此而生的。对一个值使用typeof操作符会返回下列字符串之一:
undefined,boolean,string,number,object,function,symbol。

//因为typeof是一个操作符而不是函数,所以不需要传参数,但也可以传参数。
let str= 'a'
console.log(typeof str); //string
console.log(typeof(str)); //string
console.log(typeof 10); //number

注意 严格来讲,函数在ECMAScript中被认为是对象,并不代表一种数据类型,可是函数也有自己特殊的属性,因此,有必要通过typeof操作符来区分函数和其他对象。

Undefined类型

Undefined类型只有一个值,就是特殊值undefined,当使用var或者let声明了变量但没有初始化时,就相当于给变量赋于了undefined。此时typeof这个变量的时候,返回的也是undefined。注意,包含undefined的变量跟未定义的变量是有区别的。

let str;
console.log(str); //undefined
console.log(str === undefined); //true
console.log(age); //报错
console.log(typeof str); //undefined
console.log(typeof age); //undefined
注意:对未声明的变量使用typeof也会返回undefined

Boolean类型

有两个值,true和false。如果想要将一个其他类型的值转换为布尔值,可以调用特定的Boolean()转型函数。转换规则如下:

数据类型  转为true的值   转为false的值
Boolean     true         false
String     非空字符串      空字符串
Undefined   不存在        undefined
Number0数值。      0NAN
Object     任意对象        null

Number类型

整数也可以用八进制或者十六进制表示。

let num1 = 070;//八进制的56
let num2 = 079;//无效的八进制数值,当成79处理
//八进制必须以0开头,在严格模式下是无效的,会导致js引擎抛出语法错误。
let num3 = 0xa; //十六进制10
let num4 = 0x1F; //十六进制31
//十六进制中的字母大小写均可。

js中+0和-0在所有情况下都被认为是等同的。
浮点值要定义浮点值,数值中必须包含小数点,而且小数点后面必须至少有一位数字。虽然小数点前面不是必须有整数,但推荐加上去。

let float1 = 1.1;
let float2 = .1; //有效但不推荐
//因为浮点数使用的内存空间是存储整数的两倍,所以ECMAScript总是想方设法的把值转为整数。如下所示:
let float3 = 1.; //小数点后没有数字,当成整数1处理。
let float4 = 10.0; //小数点后是0,当成10处理

浮点值的精确度最多可以达到17位小数,但在算数计算中不如整数精确。例如,最知名的例子就是0.1+0.2得到的不是0.3,而是0.30000000000000004,由于这种微小的舍入错误,导致很难测试特定的浮点值。
值的范围由于内存的限制,ECMAScript可以表示的最小值保存在Number.MIN_VALUE中,这个值在多数浏览器中是5e-324,最大值保存在Number.MAX_VALUE中,这个值在多数浏览器中是1.797 693 134 862 315 7e+308。如果某个计算得到的数值超出了可以表示的最大范围,那么这个数值会被转换为一个特殊的Infinity值。正无穷用Infinity表示,负无穷用-Infinity表示。要确定一个值是否在可表示的范围可以使用isFinite()函数表示。在范围内返回true,否则返回false。
NaN表示不是数值,用于表示本来要返回数值的操作失败了,比如,用0除以任意数值在其他语言中通常都会导致错误,从而终止代码执行。但在ECMAScript中,0,—0,+0相除会返回NaN。NaN不等于任意值,包括它自己。isNaN()函数接受一个参数,然后会判断这个值是否可以转为数值或者就是数值,如果既不是又不能转,则返回true。

console.log(0/0);//NaN
console.log(-0/+0);//NaN
//如果分子是非0值,分母是有符号0或者无符号0,则会返回Infinity或者-Infinity.
console.log(5/0);//Infinity
console.log(5/-0);//-Infinity  

数值转换:有三个函数可以将非数值转化为数值,Number(),parseInt(),parseFloat().Number()是转型函数,可以用于所有数据类型。后两个则主要用于将字符串转化为数值。
Number():true转为1,false转为0. 数值直接返回。null转为0.undefined转为NaN,字符串的话,分为下面几种情况,1,当字符串中只包含数值字符,或者数值字符前面带一个加减号的话,则转换为一个十进制数值,Number('011')输出11,忽略前面的0.
2:如果字符串中包含有效的浮点值格式如'0xf',则转换为相应的浮点值。Number(‘01.1’)输出1.1
3:如果字符串中包含有效的十六进制格式如‘0xf’,则会转为与该十六进制对应的十进制值。
4:如果是空字符串,则返回0.
5:如果字符串中包含了除上述情况之外的其他字符,则返回NaN。
如果是对象的话,先调用valueof方法,并按照上述规则转换返回的值,如果转换结果是NaN,则调用toString方法,再按照转换字符串的规则转换。
parseInt(): 该方法主要用来检测字符串中的数值字符串,字符串中最前面的空格会被忽略,从第一个非空格字符开始转换,如果第一个字符不是数值字符,加减号字符,会立即返回NaN。这意味着空字符串也会返回NaN(注意跟Number()有不同)。如果第一个字符是数值字符,加减号,则继续向后检测每个字符,直到字符串末尾,或者碰到非数值字符。parseInt(‘123blue’)返回123,parseInt(‘12.12’)返回12,注意,该方法不会四舍五入。除此之外,它还能识别八进制和十六进制格式的数据,如字符串以‘0x’开头,就会被解释为十六进制,如果以字符串0开头,并且紧跟着数值字符,会被解释为八进制整数。
它还可以传第二个参数,用于指定被转换的字符串是多少进制的,从而按照某一进制的规则将该字符串解释为相应的十进制数。
如:

    parseInt('10', 2) // 2 
    parseInt('10', 8) // 8
    parseInt('10', 10) // 10
    parseInt('10', 16) // 16

parseFloat():该函数只用来解析十进制的值,工作方式类似于parseFloat,都是从位置0开始检测整个字符串,也是解析到字符串末尾或者是解析到第一个无效的浮点数值字符为止,这意味着第一次出现的小数点字符是有效的,之后的小数点字符都是无效的。如:22.23.3会被转换为22.34
注意的是,如果字符串格式是十六进制的,则始终返回0 。 不能指定底数,只解释十进制。 如果字符串表示整数,没有小数点或者小数点后面是一个0,则它返回整数。如下:

parseFloat('123sfa') // 123
parseFloat('oxA') //0 
parseFloat('22.5') //22.5
parseFloat('22.21.1')// 21.21
parseFloat('0098.2') // 98.2
parseFloat('3.125e7') // 31250000

String类型:表示零或多个16位Unicode字符序列,定义字符串的时候用什么引号开始也要用什么引号结尾。
字符串的特点:一旦创建,值就不能改变。我们可以重新为某个变量赋值一个新的字符串达到改变的目的。
转化为字符串:有两种方式把一个值转为字符串,一个是toString方法,一个是String()函数。toString方法可见于数值,布尔值,对象和字符串值,null和undefined值不能调用toString方法。当你不确定一个值是不是null或者undefined的时候,最好使用String()方法将他转为相应字符串,它遵循如下规则:如果值有toString方法,则调用它并返回结果,如果值是null,返回'null', 如果是undefined,返回'undefined'。
注意:用加号操作符给一个值加上一个空字符串也可以将其转为字符串。
Object类型:每个Object实例都有如下方法和属性:
constructor:用于创建当前对象的函数。
hasOwnProperty(propertyName):用于判断当前对象实例(不是原型)上是否存在给定的属性。要检查的属性名必须是字符串。
isPrototypeof(object):用于判断当前对象是否为另一个对象的原型。
propertyIsEnumerable(propertyName): 用于判断给定的属性是否可以使用,属性名必须是字符串
toLocaleString():返回对象的字符串表示,该字符串反映对象所在的本地化执行环境。
toString(): 返回对象的字符串表示。
valueOf(): 返回对象对应的字符串,数值或布尔值表示,通常与toString()的返回值相同。
因为在ECMAScript中Object是所有对象的基类,所以任何对象都有这些属性和方法。但是不一定适合BOM和Dom对象。
操作符:ECMA-262描述了一组可用于操作数据值的操作符,包括数学操作符、位操作符、关系操作符和相等操作符,他们可以用于各种值,包括字符串、数值、布尔值、甚至还有对象。在应用给对象的时候,操作符通常会调用valueof或toString方法。
一元操作符:只操作一个值的操作符。包括递增,递减,加和减操作符。
递增和递减操作符分为前缀版和后缀版。前缀版的话,变量的值都会在语句被求值之前改变。后缀版相反。
一元加和一元减分别由一个加号和一个减号表示,他们放到变量前面的话,如果变量是数值,加对数值没影响,减会把数值变成负值,如果变量是非数值,那么会执行类似Number()类型转换。

布尔操作符:一共有三个,逻辑非,逻辑或,逻辑与。

  1. 逻辑非:由一个!表示,可以引用给ECMAScript中的任何值,这个操作符始终返回布尔值,无论应用到的是什么数据类型,逻辑非操作符都会先将操作数转换为布尔值,然后在对其取反。它遵循如下规则:
    如果操作数是对象,返回false。
    空字符串,返回true。
    非空字符串,false。
    数值0,true。
    非0数值,包括Infinity,false。
    null, true。
    NaN,true。
    undefined,true。

  2. 逻辑与:由&&表示。如果操作的两个值都是布尔值的话,只有两个值都是true才返回true。如果有一个值不是布尔值,则改操作不一定返回布尔值,而是遵循如下规则:
    如果第一个操作数是对象,则返回第二个操作数。
    如果第二个操作数是对象,则只有第一个数求值是true才会返回该对象。
    如果两个操作数都是对象,则返回第二个操作数。
    如果有一个操作数是null,则返回null。
    如果有一个操作数是NaN,返回NaN。
    如果有一个是undefined,返回undefined。

  3. 逻辑或:由||表示。如果都是布尔值,只要有一个true就返回true,否则返回false。如果有一个值不是布尔值,那么逻辑或操作符也不一定返回布尔值。它遵循如下规则。
    如果第一个操作数是对象,则返回第一个操作数。
    如果第一个操作数求值为false,则返回第二个操作数。
    如果两个操作数都是null,则返回null。
    如果两个操作数都是NaN,则返回NaN。
    如果都是undefined,则返回undefined。
    乘性操作符:由一个*表示,在处理数值时,执行常规的运算,如果ECMAScript不能表示乘积,则返回Infinity或-Infinity。
    如果有任一操作数是NaN,则返回NaN。
    如果是Infinity乘以0,则返回NaN。
    如果是Infinity乘以非0的有限数值,则根据第二个操作数的符号返回Infinity。
    如果Infinity乘以Infinity,返回Infinity。
    如果不是数值的操作数,则先在后台用Number()将其转换为数值,然后再应用上述规则。

除法操作符:由一个/表示,如果操作数是数值,执行常规的除法运算。
如果有任一操作数是NaN,则返回NaN。 如果Infinity除以Infinity,则返回NaN。
如果是0除以0,则返回NaN.
如果是非0的有限数值除以0,则根据第一个操作数的符号返回Infinity或-Infinity。
如果是Infinity除以任何数值,则根据第二个操作数的符号返回Infinity或者-Infinity。 如果有不是数值的操作数,则先在后台用Number()将其转换为数值,然后再应用上述规则。

取模操作符:由一个%表示,如果操作数都是数值,执行常规除法运算,返回余数。
如果被除数是无限值,除数是有限值,则返回NaN.
如果被除数是有限值,除数是0,则返回NaN。
如果Infinity除以Infinity,则返回NaN。
如果被除数是有限值,除数是无限值,则返回被除数。
如果被除数是0,除数不是0,则返回0。
如果有不是数值的操作数,则先在后台用Number()将其转换为数值,然后再应用上述规则。

指数操作符:ES7新增,由** 表示。例如3 ** 2 = 9 。

加法操作符:如果都是数值,则执行常规运算。
如果有任一操作数是NaN,则返回NaN。
如果是Infinity加Infinity,则返回Infinity。
如果是-Infinity加-Infinity,返回-Infinity。
如果是Infinity加-Infinity,返回NaN。
如果是+0加+0,返回 +0,
-0+ -0 , -0,
-0+ 0, 0,
-0 === +0 true, -0和+0在js中全等。
如果有一个操作数是字符串,则将另一个操作数转换为字符串,再将两个字符串拼接在一起。
如果两个操作数都是字符串,则将第二个字符串拼接到第一个字符串后面。

减法操作符:如果都是数值,则执行常规运算。
如果有任一操作数是NaN,则返回NaN。
如果是Infinity减Infinity,则返回NaN。
如果是-Infinity减Infinity,返回-Infinity。
如果是Infinity减-Infinity,返回Infinity。

如果是+0减+0,返回 +0,
-0 - -0 , 0,
0 - -0,-0,
如果有任一操作数是字符串,布尔值,null和undefined,后台会先使用Number()将其转换为数值,再根据前面的规则执行数学运算。
如果有任一操作数是对象,则先调用valueOf方法取得表示它的数值,如没有这个方法,调用toString得到表示它的字符串,再转为数值进行计算。

关系操作符: 小于,大于,小于等于,大于等于。这几个操作符都返回布尔值。
如果操作数都是数值,则执行数值比较。
如果操作数都是字符串,则逐个比较字符串中对应字符的编码。 (小写字母的编码都大于大写字母的编码)
如果有任一操作数是数值,则将另一个操作数转换为数值,执行数值比较。
如果有任一操作数是对象,先调用起valueOf方法,获取值再根据前面规则比较,如没有该方法,调用toString方法获取值再根据前面规则进行比较。
如果有任一操作数是布尔值,则将其转化为数值在进行比较。
任何关系操作符在比较NaN时都会返回false。

相等操作符:如果任一操作数是布尔值,则将其转为数值再比较是否相等,false转为0,true转为1。
如果一个操作数是字符串,另一个操作数是数值,则尝试将字符串转换为数值,再比较是否相等。
如果一个操作数是对象,另一个不是,则调用对象的valueOf方法取得其原始值,再根据前面的规则比较。
null和undefined相等。
null和undefined不能转换为其他类型的值再进行比较。 null == 0 false 同理undefined。 如果有任一操作数是NaN,则相等操作符返回false,不相等操作符返回true,NaN不等于NaN。
如果两个操作数都是对象,则比较它们是不是同一个对象,如果它们指向同一个对象,则相等操作符返回true,否则不相等。

for-in语句: 是一种严格的迭代语句,用于枚举对象中的非符号键属性,ECMAScript中的对象的属性是无序的,因此for-in语句不能保证返回对象属性的顺序。如果for-in循环要迭代的变量是null或者undefined,则不执行循环体。

for-of:是一种严格的迭代语句,用于遍历可迭代对象的元素。不能遍历对象,否则会报错。

switch语句:在比较每个条件的值时会使用全等操作符,因此不会强制转换数据类型,比如字符串’10‘不等于数值10。