爬虫不得不学之 JavaScript 函数对象篇

803 阅读11分钟

今天好像是情人节?所以最适合面向对象,JavaScript 也有对象,我们也可以随时面向对象,方便得很,那怎样才有对象呢?下面告诉你!


1. 数组

数组,字面意思就是一堆数的组合,但是它是有顺序的,学了数组就不仅可以存储一个数据,还可以存储一堆数据,这就是我们为什么学了简单数据类型之后还要学数组的原因。

1.1 声明数组

可以看到,数组里面的定义和 python 里的差不多,也可以存储不同数据类型。获取数组元素也是一样通过下标获取,下标从 0 开始,而且 JavaScript 的数组可以随意根据下标进行赋值,不管你的数组长度,因为 JavaScript 的数组长度是动态的。

1.2 遍历数组

遍历数组,根据数组长度可以轻易知道循环次数,所以可以使用 for 循环,获取数组的长度可以通过 length 属性进行获取。

这里有个提高效率的地方,就是在获取数组的长度时放在了 for 语句的初始化表达式里,而不是放在判断表达式里,当你这个值需要运算才能获得的时候,这样做就可以不用在每次判断时都需要通过运算获得,减少运算,也就提高效率了,当然,数组的长度在这里只是一个属性,不需要运算,放不放在初始化表达式都差不多。

1.3 清空数组

JavaScript 这里清空数组简单粗暴,直接将长度赋值为 0 即可。

1.4 数组小练习

  • 找出数组中最大的值

这个直接通过遍历数组,然后将每个值进行比较即可,很容易。

  • 翻转数组

这个就是将数组中的元素前后互相替换,也不多说了。


2. 函数

当我们需要在对多个数组进行上面的其中练习之一,比如进行寻找最大值,我们总不能每个数组都各自写一段寻找最大值的代码,否则这样子的话代码的复用性太低了。

函数的出现就是解决这个问题的,函数就是把一段相对独立的具有特定功能的代码抽取出来进行封装,形成一个独立的个体。当需要多次使用的时候,我们只需要使用函数名调用即可

2.1 函数的定义

函数定义这里有两种方法,如下:

  • 使用函数声明,语法为

  • 使用函数表达式,语法为:

上面只是函数的定义而已,并不会去执行,只有你调用函数的时候才会去执行。

2.2 函数调用

调用函数的语法也比较简单,就是函数加上一个括号就行了。

所以当需要多次使用这段功能的时候,就多次调用即可,不需要每次都写一段相同的代码。

2.3 函数参数

不需要多次写同一段代码解决了,但是当有不同的数据内容参与运算时,好像我还需要重复写呀!就比如前面说的求数组最大值。这个时候就需要我们的函数参数了,函数参数就是解决这个不确定的数据内容的。当我们需要对不确定数据内容进行操作时,只需要在调用函数的时候把数据内容当作参数传进去即可。

函数的参数定义与调用语法:

  • 形参:在声明函数时,有些值是固定的,而有些值不是固定的,对于这些不固定的值,我们可以给它们设置参数,但是这个参数不是具体的值,只是一个形式而已,所以叫做形参

  • 实参:在函数声明设置的形参,我们调用函数就需要传入对应的参数,而这个参数就是实参。

了解了这个之后,是不是很容易就可以写出一个求数组最大值的函数了?

咦?上面的我都看明白了,但是 return 那个语句又是什么?return 后面跟的内容就是函数的返回值,当函数运行到这里的时候就会结束函数并且把该值返回给调用处,就相对于一段代码执行之后的反馈所以 return 语句也会常常用于终止函数的运行,还有也可以不写 return 语句,但是会默认返回 undefined

2.4 函数内部的 arguments 对象

JavaScript 中,函数的内部都有一个 arguments 对象,用来记录在调用函数时所传进来的参数,可以说是一个伪数组。

这个对象可以用于当我们需要传进来的参数个数不确定时就可以使用这个,就比如求一堆数的和。

2.5 匿名函数

匿名函数就是没有名字的函数,当我们只需要只需要调用一次的话就可以使用匿名函数,或者需要回调函数的时候就会使用匿名函数,至于什么是回调函数,以后遇到了就说,匿名函数声明如下:

这是将匿名函数赋值给一变量,然后可以通过该变量进行调用,也可以传参的,除了这样子调用匿名函数,匿名函数还可以进行自调用。

这里需要注意的是在自调用的时候别忘了定义函数的部分需要加括号括起来。这自调用的匿名函数就常用于防止全局被污染,就是当你写的代码量大了,难免会有些全局变量会有重名的可能,这时候使用匿名函数自调用就可以新开辟了一个作用域,不同作用域的变量就算同名也不怕了,至于具体的后面我也会应用到,到时再详讲。

2.6 函数其他

  • 函数也是一种数据类型,可以说是一个对象吧,至于具体的后面再详讲,现在了解就好。

  • 函数不仅可以作为参数进行传递,还可以作为返回值,毕竟函数也是一种数据类型。作为参数传递主要就是我们所说的回调函数,遇到就会说,作为返回值的应用,闭包就是一个应用,也不多说,以后会讲。

2.7. 作用域

作用域就是变量可以起作用的范围,在 JavaScript 中定义的变量符合词法作用域,就是说变量的作用域是在定义时决定的,不是在执行时决定的,即变量作用域只需要通过源码分析就知道了。

1. JavaScript 中 词法作用域的规则为:

  • 函数内部的变量允许访问函数外部的。

  • 整个代码结构只能函数限定作用域,这就是为什么上文说使用自调用函数来开辟新的作用域的原因了。

  • 作用域规则首先使用提升规则分析,下文说的预解析就是这个

  • 如果当前作用域有该变量了,就不会考虑外面的了。

2. 下面再看看 JavaScript 中三种作用域

  • 全局作用域:JavaScript 中认为在函数外部定义的变量就是全局变量,而这个全局变量所在的作用域就是全局作用域。

  • 局部作用域:在函数内部就是局部作用域,在这里定义的内部变量也就是局部变量。

  • 块级作用域:这个是 ES6 才有的,简单说下,就是只使用一对大括号{} 括起来的就是块级作用域。

3. 作用域链

只有函数才可以限定作用域,那么在要有代码,这里就至少存在一个全局作用域,而写代码难免又会有函数,这里的函数就会构成另一个作用域,如果函数中还有函数,则他还会构成一个新的作用域,等等。将上面的这些作用域列出来,就会形成一个结构,这个结构就是作用域链。如下面代码:

按照全局作用域就是 0 级链,函数就是 1 级链,函数的函数就是 2级链,就会有下图:


2.8 预解析

JavaScript 的解释器在执行代码的时候有两个过程,就是预解析和再从上往下执行代码过程。预解析就是先把代码中的变量提升,然后函数提升,接着再执行代码。

  • 变量提升:变量的声明会被提升到作用域的最上面,注不会将赋值提升。

  • 函数提升:把当前作用域的函数声明提升到当前作用域的最上面。

如果你懂了再看看下面几段代码会不会报错?

1.

解答:不会报错,因为经过预解析后代码成这样

2.

解答:也不会报错,不过 a 打印的值为 undefined

3.

解答:会报错,原因可以结合上下两张图看即可。


3. 对象 object

对象是一个具体的事物,比如你和我都是对象,但是汽车和手机不是事物,可以说它们是一个类别。

JavaScript 中的对象可以说是一个无序的属性的集合,属性可以包括基本值、对象或函数,也可以把 JavaScript 中的对象想像为一组键值对。

把现实中的事物抽象为代码中的对象,其的特征可以作为对象的属性,其的行为可以作为方法。

3.1 创建对象

JavaScript 中创建对象的方法有四种,并不像其他语言中只能通过 new 来创建。

  • 直接声明一个键值对的集合

这个 obj 变量就是一个对象了里面有两个属性和一个方法。使用这种方法也只适合创建一个类,因为当需要创建大量同类型的对象时,使用这个方法就需要写大量的方法。

  • 使用 new Object() 创建

这个是先创建一个空对象,然后动态增加对象的属性和方法,也是只适合创建只有一个对象的类型,还不如第一种,也不推荐。

  • 使用工厂模式创建

这种方法就是使用一个模板函数,就相当于一个工厂,还有记得返回创建的对象。当需要创建对象的时候只需要调用一下函数传参就可以了,就比上面两种代码的复用性提高了。

但是这有一个问题,我们在判断对象类型的时候,结果都是 Object

判断对象类型使用 instanceof,而使用 typeof 判断对象,无论什么对象的结果都是 Object

  • 自定义构造函数来创建

这个自定义构造函数名字需要首字母大写,当然这只是个规范而已

里面使用了 this 关键字,这个 this 的指向就是使用构造函数创建的对象,也不需要返回 对象了。

注意:创建对象也需要使用 new 关键字,如上图,通过这种方法就既可以创建大量同类型的对象,也可以判断所属类型,非常方便。

这个 new 创建对象的过程为:

  1. 在内存中先创建一个空的对象

  2. 让构造函数的 this 指向刚刚创建的对象

  3. 执行构造函数内部的属性和方法定义

  4. 返回当前对象

3.2 对象属性和方法的相关操作

  • 访问属性语法为对象.属性,还可以 对象['属性名'] 这样, 当然也可以通过这两个来修改对象属性的值

当然,当对一个不存在的属性访问的时候就会返回 undefined,若是修改一个不存在的属性就是向该对象动态增加一个新的属性。

这两种方法推荐第二种,因为有时我们得到的属性是一个变量名,并不知道具体的名字,这时候就只能使用第二种方法

  • 访问方法直接使用 对象.函数名() 即可

  • 遍历对象成员

可以使用 for...in... 语句

  • 删除对象成员

使用 delete 关键字

3.3 简单数据类型和复杂数据类型的区别

基本类型又叫做值类型,复杂类型又叫做引用类型

值类型:简单数据类型,基本数据类型,在存储时,变量中存储的是值本身,因此叫做值类型。

引用类型:复杂数据类型,在存储时,变量中存储的仅仅是地址(引用),因此叫做引用数据类型。

终于写完了,下一篇常用内置对象走起。