likes
comments
collection
share

你不会连这些JavaScript基础都不知道吧

作者站长头像
站长
· 阅读数 12

一、JavaScript基础

1、基本数据类型介绍

所有的编程语言都有数据类型的概念。

JavaScript中,数据类型可以分为基本数据类型引用数据类型。其中基本数据类型包括Undefined,Null,Boolean,Number,String5种类型。在ES6中新增了一种基本的数据类型Symbol.

引用类型有Object,Function,Array,Date等。

问题:两种类型有什么区别?

存储位置和占据的空间大小不同

区别基本数据类型引用数据类型
存储位置栈(stack)堆(heap)
占据空间小,大小固定大,大小不固定

引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址。当解释器寻找引用值时,会首先检索其在栈中的地址,取得地址后从堆中获得实体。

你不会连这些JavaScript基础都不知道吧

下面我们先来回顾基本数据类型的内容,后面再复习引用类型的内容,以及看一下对应的常见的面试题。

1.1 Undefined类型

Undefined类型只有一个唯一的字面值undefined,表示的含义是一个变量不存在

问题:哪些场景中会出现undefined?

第一:使用只声明而未初始化的变量时,会返回undefined

var a
console.log(a) //undefined

第二:获取一个对象的某个不存在的属性时,会返回undefined

var obj={
    userName:'zhangsan'
}
console.log(obj.age)//undefined

第三:函数没有明确的返回值,却对函数的调用结果进行打印

function fn(){}
console.log(fn()) //undefined

第四:函数定义的时候,使用了多个形参,但是在调用的时候传递的参数的数量少于形参数量,那么没有匹配上的参数就为undefined

function fn(p1,p2,p3){
    console.log(p3) //undefined
}
fn(1,2)

1.2 Null类型

Null类型只有一个唯一的字面值null,表示一个空指针的对象这也是在使用typeof运行符检测null值时会返回object的原因

问题:哪些场景中会出现null

第一:一般情况下,如果声明的变量是为了以后保存某个值,则应该在声明时就将其赋值为null

var obj=null
function foo(){
    return {
        userName:'zhangsan'
    }
}
obj=foo();

第二:JavaScript在获取DOM元素时,如果没有获取到指定的元素对象,就会返回null

document.querySelector('#id') //null

第三:在使用正则表达式进行匹配的时候,如果没有匹配的结果,就会返回null

'test'.match(/a/);// null

1.3 Undefined与null比较

UndefinedNull虽然是两种不同的基本数据类型,但是在某些情况也存在相同之处,下面看一下它们两者相同点和不同点。

(1)相同点

第一:UndefinedNull两种数据类型都只有一个字面值,分别是undefinednull.

第二:UndefinedNull类型在转换为Boolean类型的值时,都会转换为false.

第三:在需要将两者转换成对象的时候,都会抛出一个TypeError的异常。

var a;
var b=null;
cosnole.log(a.name);//Cannot read property 'name' of undefined
cosnole.log(b.name);//Cannot read property 'name' of undefined

第四:Undefined类型派生自Null类型,所以在非严格相等的比较下,两者是相等的。如下面代码所示:

null==undefined //true

(2)不同点

第一:nullJavaScript的关键字,而undefinedJavaScript的一个全局变量,也就是挂载在window对象上的一个变量,并不是关键字。

第二:在使用typeof运算符进行检测时,Undefined类型的值会返回undefined.而Null类型的值返回为object

typeof undefined ;//undefined
typeof null ;//object

第三:在需要进行字符串类型的转换时,null会转换成字符串null,而undefined会转换字符串undefined.

undefined+" abc" //"undefined abc"
null+" abc" //"null abc"

第四:在进行数值类型的转换时,undefined会转换为NaN,无法参与计算,而null会转换为0,可以参与计算。

undefined +0;// NaN
null+0 ;// 0

第五:建议:无论在什么情况下都没有必要将一个变量显示的赋值为undefined。如果需要定义某个变量来保存将来要使用的对象,应该将其初始化为null.

1.4 Boolean类型

Boolean类型(布尔类型)的字面量只有两个,分别是truefalse,它们是区分大小写的。

Boolean类型使用最多的场景就是用于if语句的判断。在JavaScript中,if语句可以接受任何类型的表达式,即if(a)语句中的a,可以是Boolean,Number,String,ObjectNull,Undefined等类型。

如果a不是Boolean类型的值,那么JavaScript解析器会自动调用Boolean( )函数对a进行类型的转换,返回最终符合if语句判断的true或者是false值。

不同类型与Boolean类型的值的转换是Boolean类型的重点。

第一:String类型转换为Boolean类型

空字符都会转换成false,而任何非空字符串都会转换为true

第二:Number类型转换为Boolean类型

0NaN都会转换为false.而除了0NaN以外都会转换true.

第三:Object类型转换Boolean类型

如果objectnull时,会转换为false,如果object不为null,则都会转换成true.

var obj={}
Boolean(obj) //true

var obj=null
Boolean(obj)//false

第四:Function类型转换Boolean类型

任何Function类型都会转换为true

var fn=function(){
}
Boolean(fn)//true

第五:Null类型转换为Boolean类型,我们知道Null类型只有一个null值,会转换为false.

第六:Undefined类型转换Boolean类型,我们知道Undefined类型只有一个undefined值,会转换为false.

1.5 Number类型

JavaScript中,Number类型的数据包括了整型数据,也包括了浮点型数据

我们先来看一下整型的处理。整型可以是十进制,也可以通过八进制或者是十六进制来表示。

(1)八进制:

如果想要用八进制来表示一个数值,那么首位必须是0,其它位必须是0--7的数字,如果后面的数字大于7,则破坏了八进制的规则,这时会被当作十进制数来处理。

var num1=024
console.log(num1) //20
var num2=079
console.log(num2) //79

num1第一位是0表示八进制,后面每位数字都是在0--7之间的,所以符合八进制规则,最终转换为十进制为20

num2的第一位也是0,但是最后一位已经超过了7,所以不属于八进制,这里直接作为十进制来处理,最终输出的结果为79.

(2)十六进制:

如果想用十六进制表示一个数值,那么前面两位必须是0x,其它的位必须是(0--9,a--f或者A--F).如果超出了这个范围,则会抛出异常。

var num1=0x5f //95
var num2=Ox5h //Uncaught SyntaxError: Invalid or unexpected token

Boolean类型一样,当其它类型在与Number类型进行数据转换时,也会遵守一定的规则。

1.5.1 Number类型转换

在实际开发中,我们经常会遇到将其他类型的值转换为Number类型的情况。在JavaScript中,一共有3个函数可以完成这种转换,分别是Number()函数,parseInt( )函数,parseFloat( )函数。下面我们看一下这些函数需要注意的事项。

Number( )函数

Number( )函数可以用于将任何类型转换为Number类型,它在转换时遵循如下规则:

第一:如果是数字,会按照对应的进制数据格式,统一转换为十进制返回。

Number(10) //10
Number(010) // 8, 010是八进制的数据,转换成十进制是8
Number(0x10) // 16,0x10是十六进制的数据,转换成十进制是16

第二:如果是Boolean类型的值,true返回1,false返回是的0

Number(true) //1
Number(false) //0

第三:如果值为null,则返回0

Number(null) //0

第四:如果值为undefined,则返回NaN

Number(undefined) //NaN

第五:如果值为字符串类型,需要遵循如下规则

(1)如果该字符串只包含了数字,则会直接转换成十进制数;如果数字前面有0,则会直接忽略掉这个0。

Number('21') //21
Number('012') //12

(2) 如果字符串是有效的浮点数形式,则会直接转成对应的浮点数,前置的多个重复的0会被删除,只保留一个。

Number('0.12') //0.12
Number('00.12') //0.12

(3)如果字符串是有效的十六进制形式,则会转换为对应的十进制数值

Number('0x12') //18

(4) 如果字符串是有效的八进制,则不会按照八进制转换,而是直接按照十进制转换并输出,因为前置的0会被直接忽略掉。

Number('010') //10
Number('0020') //20

(5)如果字符串为空,即字符串不包含任何字符,或为连续多个空格,则会转换为0.

Number('') //0
Number('     ')//0

(6)如果字符串中包含了任何不是以上5种情况的其它格式内容,则会返回NaN

Number('123a') //NaN
Number('abc') //NaN

第六:如果是对象类型,则会调用对象的valueOf( )函数获取返回值,并且判断返回值能否转换为Number类型,如果不能,会调用对象的toString( )函数获取返回值,并且判断是否能够转换为Number类型。如果也不满足,则返回NaN.

以下是通过valueOf( )函数将对象转换成Number类型。

var obj={
    age:'12',
    valueOf:function(){
        return this.age
    },
   
  }
Number(obj) //12

以下是通过toString( )函数将对象转换成Number类型。

var obj={
    age:'21',
    toString:function(){
        return this.age
    }
    
}
Number(obj)

parseInt( )函数

parseInt()函数用于解析一个字符串,并返回指定的基数对应的整数值。

语法格式:

parseInt(string,radix)

其中string参数表示要被解析的值,如果该参数不是一个字符串,那么会使用toString( )函数将其转换成字符串。并且字符串前面的空白符会被忽略。

radix表示的是进制转换的基数,可以是二进制,十进制,八进制和十六进制。默认值为10.

因为对相同的数采用不同进制进行处理时可能会得到不同的结果,所以在任何情况下使用parseInt函数时,建议都手动补充第二个参数。

parseInt( )函数会返回字符串解析后的整数值,如果该字符串无法转换成Number类型,则会返回NaN.

parseInt('aaa')//NaN

在使用parseInt函数将字符串转换成整数时,需要注意的问题:

第一:如果遇到传入的参数是非字符串类型的情况,则需要将其优先转换成字符串类型。即使传入的是整型数据。

第二:parseInt( )函数在做转换时,对于传入的字符串会采用前置匹配的原则。

parseInt("fg123",16)

对于字符串fg123,首先从第一个字符开始,f是满足十六进制的数据的,因为十六进制数据的范围是0--9,a--f,所以保留f,然后是第二个字符g,它不满足十六进制数据范围,因此从第二个字符都最后一个字符全部舍弃,最终字符串只保留了字符f,然后将字符f转换成十六进制的数据,为15,因此最终返回的结果为15.

还要注意的一点就是,如果传入的字符串中涉及到了算术运算,则不会执行,算术符号会被当作字符处理。

parseInt('16*2')// 16,这里直接当作字符串处理,并不会进行乘法的运算
parseInt(16*2) // 32 

第三:对浮点数的处理

如果传入的值是浮点数,则会忽略小数点以及后面的数,直接取整。

parseInt(12.98) //12

第四:map( )函数与parseInt( )函数的问题

我们这里假设有一个场景,存在一个数组,数组中的每个元素都是数字字符串,['1','2','3','4'],如果将这个数组中的元素全部转换成整数,应该怎样处理呢?

这里我们可能会想到使用map( )函数,然后在该函数中调用parseInt( )函数来完成转换。所以代码如下:

 <script>
      var arr = ["1", "2", "3", "4"];
      var result = arr.map(parseInt);
      console.log(result);
    </script>

执行上面程序得到的结果是:[1,NaN,NaN,NaN]

为什么会出现这样的问题呢?

上面的代码等效如下的代码

 var arr = ["1", "2", "3", "4"];
      //   var result = arr.map(parseInt);
      var result = arr.map(function (val, index) {
        return parseInt(val, index);
      });
      console.log(result);

通过以上的代码,可以发现,parseInt函数第二个参数实际上就是数组的索引值。所以,整体的形式如下所示:

parseInt('1',0) // 任何整数以0为基数取整时,都会返回本身,所以这里返回的是1
parseInt('2',1) //注意parseInt第二个参数的取值范围为2--36,所以不满足条件,这里只能返回NaN
parseInt('3',2) // 表示将3作为二进制来进行处理,但是二进制只有0和1,所以3超出了范围,无法转换,返回`NaN`
parseInt('4',3) //将4作为三进制来处理,但是4无法用三进制的数据表示,返回NaN

所以当我们在map( )函数中使用parseInt( )函数时,不能直接将parseInt( )函数作为map( )函数的参数,而是需要在map( )函数的回调函数中使用,并尽量指定基数。代码如下所示:

      var arr = ["1", "2", "3", "4"];
      var result = arr.map(function (val) {
        return parseInt(val, 10);
      });
      console.log(result);

parseFloat( )函数

parseFloat函数用于解析一个字符串,返回对应的浮点数,如果给定值不能转换为数值,则返回NaN

parseInt( )函数相比,parseFloat( )函数没有进制的概念。

注意:

第一:如果字符串前面有空白符,则会直接忽略掉,如果第一个字符就无法解析,则会直接返回NaN

parseFloat('  2.6')// 2.6
parseFloat('f2.6') //NaN

第二:对于小数点,只能正确匹配第一个,第二个小数点是无效的,它后面的字符也都将被忽略。

parseFloat('12.23')// 12.23
parseFloat('12.23.39')//12.23

总结:

虽然Number( ),parseInt( ),parseFloat( )函数都能用于Number类型的转换,但是他们之间还是有一定的差异

第一:Number( ) 函数转换的是传入的整个值,并不是像parseInt( )函数和parseFloat( )函数一样会从首位开始匹配符合条件的值。如果整个值不能被完整转换,则会返回NaN

第二:parseFloat( )返回对应的浮点数,parseInt( )返回整数,并且parseFloat( )函数在解析时没有进制的概念,而parseInt() 函数在解析时会依赖于出入的第二个参数来做值的转换。

1.5.2 isNaN( )函数与Number.isNaN( )函数对比

Number类型数据中存在一个比较特殊的值NaNNot a Number),它表示应该返回数值却并未返回数值的情况。

NaN存在的目的是在某些异常情况下保证程序的正常执行。例如0/0,在其他的语言中,程序会直接抛出异常,而在JavaScript中会返回NaN,程序可以正常运行。

NaN有两个很明显的特点,第一个是任何涉及NaN的操作都会返回NaN,第二个NaN与任何值都不相等,即使是与NaN本身相比。

NaN==NaN //false

在判断NaN时,ES5提供了isNaN函数,ES6Number类型增加了静态函数isNaN( ).

问题:既然在ES5中提供了isNaN函数,为什么要在ES6中专门增加Number.isNaN( )函数呢?两者在使用上有什么区别?

我们先来看一下isNaN( )函数

isNaN( )函数的作用是用来确定一个变量是不是NaN,NaN是一个Number类型的数值,只不过这个值无法用真实的数字表示。

isNaN检测的机制:它在处理的时候会去判断传入的变量值能否转为数字,如果能转换成数字则会返回false,如果无法转换则会返回true.

isNaN(NaN)//true
isNaN(undefined) //true
isNaN({})//true

isNaN(true)// false ,Number(true)会转换成数字1
isNaN(false)// false,Number(false)会转换成数字0
isNaN(null) // false,Number(null)会转换成数字0

isNaN(1) //false
isNaN('aaa') //true 字符串aaa无法转换成数字
isNaN('1') //false 字符串“1”可以转换成数字1.

Number.isNaN( )函数

既然在全局的环境中有了isNaN( )函数,为什么在ES6中会专门针对Number类型增加一个isNaN函数呢?

这是因为全局的isNaN函数本身存在误导性,而ES6中的Number.isNaN( )函数会在真正意义上去判断变量是否为NaN,不会做数据类型转换。只有在传入的值为NaN,才会返回true,传入其它类型的值时会返回false.

Number.isNaN(NaN)// true
Number.isNaN(1) //false
Number.isNaN(null) //false
Number.isNaN(undefined) //false

如果在非ES6环境中想用ES6中的isNaN( )函数,怎样处理呢?

if(!Number.isNaN){
    Number.isNaN=function(n){
        return n!==n
    }
}

在所有类型的数据中,如果一个变量和自身进行比较,只有在变量为NaN时才会返回false(NaN===NaN //false),其它情况都是返回的true.

所以n!==n返回true,也只有在n的值为NaN的时候才会成立。

总结:

isNaN( )函数与Number.isNaN( )函数的区别如下:

第一:isNaN( )函数在判断是否为NaN时,需要进行数据类型转换,只有在无法转换为数字时才会返回true

第二:Number.isNaN( )函数在判断是否为NaN时,只需要判断传入的值是否为NaN,并不会进行数据类型转换

1.6 String类型

JavaScript中的String类型可以通过双引号表示,也可以通过单引号表示,并且这两种方式是完全等效的。

1.6.1 String类型定义

JavaScript中有3种方式来创建字符串,分别是字符串字面量,直接调用String( )函数,还有就是通过new String( )构造函数的方式

字面量

字符串字面量就是直接通过单引号或者是双引号定义字符串的方式。

注意:单引号和双引号是等价的。

var str='hello'
var str2="JavaScript"

直接调用String( )函数

直接调用String( )函数,会将传入的任何类型的值转换成字符串类型。在转换的时候,需要遵循如下的规则:

第一:如果是Number类型的值,则直接转换成对应的字符串。

String(123) // '123'
String(123.56) // "123.56"

第二:如果是Boolean类型的值,则直接转换成字符串的"true"或者是"false"

String(true)// "true"
String(false) // "false"

第三:如果值为null,直接转换成字符串的"null"

String(null) // "null"

第四:如果值为undefined,则转换成字符串的undefined

String(undefined) //"undefined"

new String( )构造函数

这种方式是使用new运算符来创建一个String的实例。转换的规则和String( )函数是一样的,最后返回的是一个String类型的对象实例。

new String(678) //返回的对象中有length属性,并且可以通过下标获取对应的值。

三种创建方式的区别

使用字符串字面量方式和直接调用String( )函数的方式得到的字符串都是基本字符串,而通过new String( )方式生成的字符串是字符串对象

基本字符串在比较的时候,只需要比较字符串的值即可,而在比较字符串对象时,比较的是对象所在的地址。

var str='hello'
var str2=String('hello')
str===str2 //true

var str3=new String('hello')
var str4=new String('hello')
str3===str4 //false

对于strstr2都是基本字符串,只是比较字符串的值就可以了,所以两者是相等的。

而对于str3str4都是通过String类型的实例,所以在比较的时候需要判断变量是否指向了同一个对象,也就是内存地址是否相同,很明显,str3str4都是在内存中新生成的地址,彼此各不相同。

函数调用

String对象的原型链有一系列的函数,例如indexOf( ),substring()等等。

通过String对象的实例可以调用这些函数做字符串的处理。

但是,我们发现了一个问题,就是采用字面量方式定义的字符串也能够直接调用原型链上的这些函数。

'hello'.indexOf('o') //4

这是为什么呢?

实际上基本字符串本身是没有字符串对象上的这些函数的,而在基本字符串调用字符串对象才有的函数时,JavaScript会自动将基本字符串转换为字符串对象,形成一种包装的类型,这样基本字符串就可以正常调用字符串对象的方法了。

1.6.2 字符串常见算法

我们来看一下常见的String类型中的算法,这些在面试的时候也是经常被问到的。

第一:字符串逆序输出

字符串逆序输出就是将一个字符串以相反的顺序进行输出。

例如abcdef输出的结果是fedcba

第一种算法

这里我们是借助与数组的reverse()函数来实现。

function reverseString(str) {
        return str.split("").reverse().join("");
      }
      console.log(reverseString("abcdef"));

第二种算法:

var arr=Array.from('abcdef') //转换成数组,这里比第一种方式简单
console.log(arr.reverse().join(""))

第三种算法:

这里可以通过字符串本身提供的chartAt函数来完成。

  function reverseString2(str) {
        var result = "";
        for (var i = str.length - 1; i >= 0; i--) {
          result += str.charAt(i);
        }
        return result;
      }
      console.log(reverseString2("abcdef"));

统计字符串中出现次数最多的字符及出现的次数

假如有一个字符串javascriptjavaabc,其中出现最多的字符是a,出现了5次。

算法1

思想:通过key-value形式的对象存储字符串以及字符串出现的次数,然后逐个判断出现次数最大的值,同时获取对应的字符。

<script>
      function getMaxCount(str) {
        var json = {}; //表示key-value结构的对象
        //遍历str的每一个字符得到key-value形式的对象
        for (var i = 0; i < str.length; i++) {
          //判断json对象中是否有当前从str字符串中取出来的某个字符。
          if (!json[str.charAt(i)]) {
            //如果不存在,把当前字符作为key添加到json对象中,值为1
            json[str.charAt(i)] = 1;
          } else {
            //如果存在,则让value值加1
            json[str.charAt(i)]++;
          }
        }
        //存储出现次数最多的字符
        var maxCountChar = "";
        //存储出现最多的次数
        var maxCount = 0;
        //遍历json对象,找出出现次数最大的值
        for (var key in json) {
          if (json[key] > maxCount) {
            maxCount = json[key];
            maxCountChar = key;
          }
        }
        return (
          "出现最多的字符是" + maxCountChar + ",共出现了" + maxCount + "次"
        );
      }
      var str = "javascriptjavaabc";
      console.log(getMaxCount(str));
    </script>

算法2

思路:这里主要是对字符串进行排序,然后通过lastIndexOf()函数获取索引值后,判断索引值的大小以获取出现的最大次数。

 function getMaxCount(str) {
        //定义两个变量,分别表示出现最大次数和对应的字符。
        var maxCount = 0,
          maxCountChar = "";
        //处理成数组,调用sort()函数排序,再处理成字符串
        str = str.split("").sort().join("");
        for (var i = 0, j = str.length; i < j; i++) {
          var char = str[i];
          //计算每个字符出现的次数
          var charCount = str.lastIndexOf(char) - i + 1;
          //与次数最大值进行比较
          if (charCount > maxCount) {
            //更新maxCount与maxCountChar的值
            maxCount = charCount;
            maxCountChar = char;
          }
          //变更索引为字符出现的最后位置
          i = str.lastIndexOf(char);
        }
        return "出现最多的字符是" + maxCountChar + ",出现次数为" + maxCount;
      }
      console.log(getMaxCount("caa"));

去除字符串中重复的字符

假如存在一个字符串"javascriptjavaabc",其中存有重复的字符,现在需要将这些重复的字符去掉,只保留一个。

 function removeStringChar(str) {
        //结果数组
        var result = [];
        //key-value形式的对象
        var json = {};
        for (var i = 0; i < str.length; i++) {
          //当前处理的字符
          var char = str[i];
          //判断是否在对象中
          if (!json[char]) {
            //将value值设置为true
            json[char] = true;
            //添加到结果数组中
            result.push(char);
          }
        }
        return result.join("");
      }
      var str = "javascriptjavaabc";
      console.log(removeStringChar(str));

算法2

这里可以使用ES6中的Set数据结构,可以结构具有自动去重的特性,可以直接将数组元素去重。

下面先来看一下Set的基本使用方式

const set = new Set([1,2,3,4,4,]);
//console.log(set)  // Set(4) {1, 2, 3, 4}
[...set] // [1, 2, 3, 4] 通过扩展运算符将set中的内容转换成数组,同时可以看到已经去重。

基本思路:

(1)将字符串处理成数组,然后作为参数传递给Set的构造函数,通过new运算符生成一个Set实例。

(2) 将Set通过扩展运算符(...)转换成数组的形式,最终转换成字符串获得需要的结果。

 function removeStringChar(str) {
        let set = new Set(str.split(""));
        return [...set].join("");
      }
      var str = "javascriptjavaabc";
      console.log(removeStringChar(str));

判断一个字符串是否为回文字符串

回文字符串指的是一个字符串正序和倒序是相同的,例如字符串abcdcba是一个回文字符串,而字符串abcedba就不是一个回文字符串。

需要注意的是,这里不区分字符的大小写,即aA在判断的时候是相等的。

算法1

主要思想是将字符串按从前往后顺序的字符与按从后往前顺序的字符逐个进行比较,如果遇到不一样的值则直接返回false,否则返回true.

 function isEequStr(str) {
        //空字符串则直接返回true
        if (!str.length) {
          return true;
        }
        //统一转换成小写,同时再将其转换成数组
        str = str.toLowerCase().split("");
        var start = 0,
          end = str.length - 1;
        //通过while循环,判断正序和倒序的字母
        while (start < end) {
          // 如果相等则更改比较的索引
          if (str[start] === str[end]) {
            start++;
            end--;
          } else {
            return false;
          }
        }
        return true;
      }
      var str = "abcdcba";

算法2

思想:将字符串进行逆序的处理,然后与原来的字符串进行比较,如果相等则表示是回文字符串,否则不是回文字符串。

function isEequStr(str) {
        //字符串统一转换成小写的形式
        str = str.toLowerCase();
        //将字符串转换成数组
        var arr = str.split("");
        //将数组逆序并转换成字符串
        var reverseStr = arr.reverse().join("");
        return str === reverseStr;
      }
      console.log(isEequStr("abccba"));

2、运算符

JavaScript中的运算符包括:算术运算符,关系运算符,等于运算符,位运算符(与、或、非)等

2.1 等于运算符

JavaScript中等于分为双等(==)比较,和三等于(===)比较。

2.1.1 三等于运算符

(1)如果比较的值类型不相同,则直接返回false

1==='1' //false
true==='true' //false

这里还需要注意的一点就是,基本数据类型存在包装类型,在没有使用new操作符时,简单类型的比较实际上就是值的比较,而使用了new操作符以后,实际得到的是引用类型的值,在判断时会因为类型不同而直接返回false

1===Number(1) //true
1===new Number(1) //false
'hello'===String('hello') //true
'hello'===new String('hello') //false

(2) 如果比较的值都是数值类型,则直接比较值的大小,相等则返回true,否则返回false,需要注意的是,如果参与比较的值中有任何一方为NaN,则返回false

26===26 //true
34===NaN //false

(3)如果比较的值是字符串类型,则判断每个字符是否相等,如果全部相等,返回true,否则返回false

'abc'==='abc' //true
'abc'==='abd' //false

(4)关于nullundefined比较

null===null //true
undefined===undefined //true
undefined===null //false

(5)如果比较的值都是引用类型,则比较的是引用类型的地址,当两个引用指向同一个地址时,则返回true,否则返回false

var a=[]
var b=a
var c=[]
console.log(a===b) //true
console.log(a===c) //false

new String('hello')===new String('hello')//false 两个不同对象,地址不相同
//创建构造函数

 function Person(userName) {
        this.userName = userName;
      }
      var p1 = new Person("wangwu");
      var p2 = new Person("wangwu");
      console.log(p1 === p2);//false  两个不同对象,地址不相同

2.1.2 双等于运算符

相比于三等于运算符,双等于运算符在进行相等比较的时候,要复杂一点。因为它不区分数据类型,而且会做隐式类型的转换

双等于在进行比较的时候要注意的点:

如果比较的值类型不相同,则会按照下面的规则进行转换后再进行比较

(1) 如果比较的一方是null或者是undefined,只有在另一方是null或者是undefined的情况下才返回true,否则返回false

null==undefined //true
null==1 //false
undefined==2 //false

(2)如果比较的是字符串和数值类型数据,则会将字符串转换为数值后再进行比较,如果转换后的数值是相等的则返回true,否则返回false.

1=='1' //true
'222'==222 //true

(3)如果比较的时候,有一方的类型是boolean类型,会将boolean类型进行转换,true转换为1,false转换0,然后在进行比较。

'1'==true
'2'==true //false
'0'==false //true

2.2 typeof运算符

typeof运算符用于返回对应的数据类型,

基本的使用方式

typeof operator
typeof (operator)

operator表示要返回类型的操作数,可以是引用类型,也可以是基本数据类型。

括号有时候是必须的,如果不加上括号将会因为优先级的问题,而得不到我们想要的结果。

下面我们看一下typeof的使用场景

(1)处理Undefined类型

我们知道Undefined类型的值只有一个undefined,typeof运算符在处理如下情况的时候,返回的结果都是undefined

处理undefined本身
未声明的变量
已经声明但是没有初始化的变量
typeof undefined //"undefined"
typeof abc //"undefined" ,未声明的变量abc,通过typeof返回的是undefined

var sum
typeof sum //undefined  已经声明但是没有初始化的变量

(2)处理Boolean类型的值

Boolean类型的值有两个,分别是truefalse,typeof运算符在处理这两个值的时候返回都是boolean

var b=true
typeof b //"boolean"

(3) 处理Number类型的值

对于Number类型的数,typeof运算符在处理时会返回number

typeof 666 //number
typeof 66.66 //number

(4)处理String类型的值

字符串类型,typeof返回的是string,包括空字符串。

typeof 'aaa' //string
typeof '' //string

(5)处理Function类型的值

函数的定义,包括函数的声明,typeof返回的值function

function fun(){}
typeof fun // "function"

var fun2=function(){}
typeof fun2 // "function"

关于通过class关键字定义的类,通过typoef计算返回的值也是function

class Obj{
}
typeof Obj // "function"

class是在ES6中新增的一个关键字,原理依旧是原型继承,也就是说本质上仍然是一个Function

(6) 处理Object类型的值

对象字面量的形式,返回的是object

var obj={userName:'zhangsan'}
typeof obj //"object"

数组,通过typeof计算返回的值是object

var arr=[1,2,3]
typeof arr // "object"

var arr2=new Array()
typeof arr2 //"object"

(7) typeof运算符对null的处理

typeof运算符对null的处理,返回的是object

typeof null //object

注意:在前面我们提到过,在使用typeof的时候,括号有时候是必须的,如果不加上括号会因为优先级问题,得不到我们想要的结果。

例如如下代码所示:

var num=123
typeof (num + 'hello')// string
typeof num + " hello"  //"number hello"

通过上面的代码,我们知道typeof运算符的优先级要高于字符串的拼接运算符(+),但是优先级低于小括号,所以在未使用括号时,会优先处理typeof num, 返回的是number,然后与hello字符串进行拼接,得到的最终的结果就是number hello

下面,我们再来看一段代码

typeof 6/2 // NaN

在上面的代码中,会先执行typeof 6 得到的结果为number,然后除以2,一个字符串除以2,得到的结果为NaN

typeof (6/2) //"number"

这里会先计算括号中的内容,然后在通过typeof进行计算。

3、常用的判空方法

JavaScript中判断一个变量是否为空,我们往往会想到对变量取反,然后判断是否为true

if(!x){ }

这是一个非常简单的判断变量是否为空的方法,但是其实涉及到的场景却很多,这里我们就分情况来看一下。

(1)判断变量为空对象

判断变量为null或者为undefined

判断一个变量是否为空时,可以直接将变量与null或者是undefined进行比较,需要注意的是双等号和三等好直接的区别。

if(obj==null) //可以判断null或者是undefined的情况
if(obj===undefined) //只能判断undefined的情况    

判断变量为空对象{ }

判断一个变量是否为空对象时,可以通过for...in语句遍历变量的属性,然后调用hasOwnProperty( )函数,判断是否有自身存在的属性,如果存在就不是空对象,如果不存在自身的属性(不包括继承的属性),那么变量为空对象。

function isEmpty(obj) {
        for (let key in obj) {
          if (obj.hasOwnProperty(key)) {
            return false;
          }
        }
        return true;
      }

 var obj = {
        username: "zhangsan",
      };
      console.log(isEmpty(obj));// false,表明obj这个对象是有自己的属性,所以不是空对象
 var obj = {};
      console.log(isEmpty(obj));//true,这里将obj对象的属性去掉了,返回的值为true,表明没有自己的属性,表示空对象
//这里通过构造函数的形式创建对象,并且指定了age属性
      function Person() {
        this.age = 20;
      }

      var p = new Person();
      console.log(isEmpty(p));//false

下面看一下另外一种情况

function Person() {}
      Person.prototype.userName = "zhangsan";
      var p = new Person();
      console.log(isEmpty(p)); //true

在上面的代码中,变量p是通过new操作符得到的Person对象的实例,所以p会继承Person原型链上的userName属性,但是因为不是自身的属性,所以会被判断为空,所以返回true.

(2)判断变量为空数组

判断变量是否为空数组时,首先要判断变量是否为数组,然后通过数组的length属性确定。(instanceof 用于判断一个变量是否某个对象的实例)

var arr=new Array()
arr instanceof Array && arr.length===0

以上两个条件都满足时,变量就是一个空数组。

(3) 判断变量为空字符串

判断变量是否为空字符串时,可以直接将其与空字符串进行比较,或者调用trim()函数去掉前后的空格以后,在去判断字符串的长度。

str==''||str.trim().length==0

当满足以上两个条件中的任意一个时,变量就是一个空字符串。

(4)判断变量为0或者NaN

当一个变量为Number类型时,判断变量是否为0或者NaN,因为NaN与任何值比较都是false,所以这里我们通过取非来完成判断。

!(Number(num)&&num)==true

当上述代码返回的结果为true,表明变量为0或者是NaN

(5)

在最开始的时候,我们提到的

JavaScript中判断一个变量是否为空,我们往往会想到对变量取反,然后判断是否为true

if(!x){
    
}

这种方式会包含多种情况,下面我们总结一下:

变量为null
变量为undefined
变量为空字符串''
变量为数字0
变量为NaN

4、流程控制

关于流程控制这块内容,这里我们重点看一下Switch结构

看一下如下代码执行的结果

  <script>
      function getStringValue(str) {
        switch (str) {
          case "1":
            console.log("a");
            break;
          case "2":
            console.log("b");
            break;
          case "3":
            console.log("c");
            break;
          default:
            console.log("d");
        }
      }
      getStringValue("2"); //b
      getStringValue("5"); //d
    </script>

以上的代码非常简单。分别输出的是bd

但是,这里我们把对getStringValue函数的调用修改成如下的形式:

getStringValue(3) //d

这里将参数修改成数字3,得到的结果是d.原因是:在JavaScript中的关于case的比较是采用严格相等的方式(===)。在上面的函数调用中,传递的是数字类型的3,而在case中比较的是String字符串的'3',两者按照严格方式进行对比,是不相等的。所以只能执行default,输出字母d.

下面,再来看如下的调用

 getStringValue(String("3")); //c

上面调用的结果是c.

字符串的字面量和直接调用String( )函数生成的字符串都是基本的字符串,它们在本质上都是一样的。

所以在严格模式下进行比较是相等的。

String('3')==='3' //true

下面再来看另外一种调用方式

 getStringValue(new String("3")); //d

通过new关键字创建的是字符串对象,这里采用严格模式进行比较,比较的是字符串对象的内存地址是否相同。而当与字符串的字面量进行比较时,会返回false.

new String('3')==='3' //false

所以在运行整个getStringValue整个函数的时候,得到的结果为d.

转载自:https://juejin.cn/post/7265241723193344058
评论
请登录