前端学习记录~2023.7.22~JavaScript重难点实例精讲~第1章 Javascript重点概念

第1章 Javascript重点概念

  • 前言
  • 1.1 JavaScript基本数据类型
    • 1.1.1 JS数据类型基本分类
    • 1.1.2 undefined类型
    • 1.1.3 null类型
    • 1.1.4 undefined和null两种类型的异同
    • 1.1.5 boolean类型及其他类型与boolean类型的转换
  • 1.2 number类型详解
    • 1.2.1 number类型介绍
    • 1.2.2 其他类型与number类型的数据转换
    • 1.2.3 number类型转换相关函数
      • (1)Number() 函数
      • (2)parseInt() 函数
      • (3)parseFloat() 函数
      • (4)小结
    • 1.2.4 isNaN()函数与Number.isNaN()函数对比
      • (1)isNaN()函数
      • (2)Number.isNaN()函数
      • (3)小结
    • 1.2.5 浮点型运算
      • (1)浮点型运算不准问题
      • (2)解决方案
  • 1.3 String类型详解
    • 1.3.1 String类型的定义与调用
      • (1)字符串字面量
      • (2)直接调用String()函数
      • (3)new String()构造函数
      • (4)三者在做比较时的区别
      • (5)函数的调用
    • 1.3.2 String类型常见算法
      • (1)字符串逆序输出
      • (2)统计字符串中出现次数最多的字符及出现的次数
      • (3)去除字符串中重复的字符
      • (4)判断一个字符串是否为回文字符串
  • 1.4 运算符
    • 1.4.1 等于运算符
      • (1)三等于运算符
      • (2)双等于运算符
    • 1.4.2 typeof运算符
      • (1)基本使用
      • (2)使用typeof需要注意的问题
    • 1.4.3 逗号运算符
      • (1)在for循环中批量执行表达式
      • (2)用于交换变量,无须额外变量
      • (3)用于简化代码
      • (4)用小括号保证逗号运算符的优先级
    • 1.4.4 运算符优先级
  • 1.5 toString()函数与valueOf()函数
    • 1.5.1 两种函数基本知识
      • (1)toString()函数
      • (2)valueOf()函数
      • 1.5.2 两个函数同时存在时做隐式转换的选择
        • (1)引用类型转换为String类型
        • (2)引用类型转换为Number类型
  • 1.6 JavaScript中常用的判空方法
    • (1)判断变量为空对象
      • a. 判断变量为null或者undefined
      • b. 判断变量为空对象{}
    • (2)判断变量为空数组
    • (3)判断变量为空字符串
    • (4)判断变量为0或者NaN
    • (5)!x == true的所有情况
  • 1.7 JavaScript中的switch语句


前言

从本篇开始记录《Javascript重难点实例精讲》,关于Javascript系列预计从这本书出发,接着再通过《Javascript高等程序设计》和阮一峰老师的ES6教程进行完善

本篇为第1章 Javascript重点概念


1.1 JavaScript基本数据类型

1.1.1 JS数据类型基本分类

Javascript数据类型分为基本数据类型引用数据类型

基本数据类型:

  1. undefined
  2. null
  3. boolean
  4. number
  5. string
  6. symbol(ES6 中新增)

引用数据类型(在第2和第3章再展开):

  1. object
  2. array
  3. function
  4. date等等

1.1.2 undefined类型

只有唯一字面值undefined,表示一个变量不存在

常见场景:

  1. 使用只声明而未初始化的变量时
  2. 获取一个对象的某个不存在的属性(自身属性和原型链继承属性)时
  3. 函数没有明确的返回值,却在其他地方使用了返回值时
  4. 函数定义时使用了多个形参,而在调用时传递的参数少于行参数量时

1.1.3 null类型

只有唯一字面值null,表示一个空指针对象,因此使用typeof检测null时会返回object

常见场景:

  1. 一般如果声明的变量是为了之后保存某个值,那声明时就应该赋值为null
  2. js在获取DOM元素时,如果没有获取到指定的元素对象,就会返回null
  3. 使用正则表达式进行捕获时,如果没有捕获结果,就会返回null

1.1.4 undefined和null两种类型的异同

相同点:

  • 转化为boolean类型时都转为false
  • 在需要将两者转换成对象时,都会抛出一个TypeError的异常,也就是平时最常见的引用异常
  • undefined类型派生自null类型,所以在非严格相等的情况下,两者是相等的

不同点:

  • null是JavaScript中的关键字,而undefined是JavaScript中的一个全局变量,即挂载在window对象上的一个变量,并不是关键字
  • 在使用typeof运算符检测时,undefined类型的值会返回undefined,而 null 类型的值会返回object
  • 在通过call调用toString()函数时,undefined类型的值会返回“[object Undefined]”,而null类型的值会返回“[object Null]”
  • 在需要进行字符串类型的转换时,null会转换为字符串"null",而undefined会转换 为字符串"undefined"
  • 在需要进行数值类型的转换时,undefined会转换为NaN,无法参与计算;null会转换为0,可以参与计算
  • 无论在什么情况下都没有必要将一个变量显式设置为undefined。如果需要定义某个变量来保存将来要使用的对象,应该将其初始化为null。这样不仅能将null作为空对象 指针的惯例,还有助于区分nullundefined

1.1.5 boolean类型及其他类型与boolean类型的转换

只有两个值truefalse,注意这是区分大小写的,例如 True 就不行。

Boolean类型使用最多的场景就是用于if语句判断。在JavaScript中,if语句可以接受任何类型的表达式,即if(a)语句中的a,可以是Boolean、Number、String、Object、 Function、Null、Undefined中的任何类型。
如果a不是Boolean类型的值,那么JavaScript解释器会自动调用Boolean()函数对a 进行类型转换,返回最终符合if语句判断的true或者false值。

不同类型与boolean类型的转换:

  1. string -> boolean
    • 空字符串""或者'' -> false
    • 其他的非空字符串 -> true
  2. number -> boolean
    • 0NaN -> false
    • 其他所有,包含无穷大infinity和无穷小-infinity -> true
  3. object -> boolean
    • object为null -> false
    • 不为null,包括空对象{} -> true
  4. function -> boolean:
    • 任何function类型 -> true
  5. null -> boolean:
    • null类型只有一个null值,会转换为false
  6. undefined -> boolean
    • undefined类型只有一个undefined值,会转换为false

1.2 number类型详解

1.2.1 number类型介绍

js中的number类型及包括整型数据,也包括浮点型数据

基本采用是十进制,同时也可以通过八进制或十六进制,下面是具体要求和写法:

  • 八进制:首位是0,其他位必须是0~7的八进制序列。如024就是十进制20
  • 十六进制:前两位必须是0x,其他位是十六进制序列(0~9,a~f或者A~F)。如0x3f是十进制63

1.2.2 其他类型与number类型的数据转换

  1. boolean -> number:
    • true -> 1
    • false -> 0
  2. null -> number:
    • null类型只有一个字面值null,直接转换为0
  3. undefined -> number:
    • undefined类型只有一个字面值undefined,直接转换为NaN
  4. string -> number:
    • 字符串中只包含数字,会转为十进制数,如果前面有 0 或是八进制格式,会直接省略掉 0,如 0123 会转为 123
    • 只包含有效的浮点数,同样转为十进制数
    • 只包含有效的十六进制格式,会转为十进制数,如 0x3f 会转为 63
    • 空字符串,或包含多个空格,转换为0
    • 如果包含上述格式之外的字符串,转换为NaN
  5. object -> number:
    • Object类型在转换为Number类型时,会优先调用valueOf()函数,然后通过其返回值按照上述规则进行转换。如果转换的结果是NaN,则调用 toString()函数,通过其返回值重新按照上述规则进行转换;如果有确定的number类型返回值,则结束,否则返回NaN

1.2.3 number类型转换相关函数

在 JavaScript中将其他类型的值转换为number类型一共有3个函数可以完成,分别是Number()函数、parseInt()函数、parseFloat()函数

(1)Number() 函数

Number()函数可以用于将任何类型转换为Number类型,规则如上面 1.2.2 所示

例如

Number('012'); // 12

需要注意的是如果不涉及到其他类型转number类型,而是对数字进行转换,则会按照对应的进制数据格式,同义转换为十进制并返回

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

(2)parseInt() 函数

parseInt()函数用于解析一个字符串,并返回指定的基数对应的整数值。如果该字符串无法转换成number类型,则会返回NaN

语法格式如下

parseInt(string, radix);
  • string:表示要被解析的值。如果该参数不是字符串,那么会使用toString()函数将其转换成字符串,字符串前面的空白符会被忽略。
  • radix:表示进制转换的基数,数据范围是2~36,默认值是10也就是代表十进制。因为使用不同进制会产生不同结果,因此这个值无论如何实际使用时都必须手动补充好。

有下面 5 个注意点:

1.传入参数如果是非字符串类型,需要优先转换成字符串类型,即使传入的是整型数据

parseInt('0x12', 16); // 18
parseInt(0x12, 16); // 24

第一条语句直接将字符串"0x12"转换为十六进制数,得到的结果为1×16+2=18;
第二条语句由于传入的是十六进制数,所以会先转换成十进制数18,然后转换成字符串"18",再将字符串"18"转换成十六进制数,得到的结果为1×16+8=24

2.数据截取的前置匹配原则

即从字符串的第一个字符串开始匹配,如果处于基数指定的范围,则保留并继续往后匹配满足条件的字符,直到某个字符不满足基数指定的数据范围,则从该字符开始,舍弃后面的全部字符
例如

parseInt("fg123", 16);	//15

如果遇到的字符串以"0x"开头,那按16进制处理时会正常计算,但是按十进制就会是0

parseInt('0x12',16); // 18 = 16 + 2
parseInt('0x12',10); // 0

需要注意的一点是,如果传入的字符串中涉及算术运算,则不执行,算术符号会被当作字符处理;如果传入的参数是算术运算表达式,则会先运算完成得到结果,再参与 parseInt()函数的计算

parseInt(15 * 3, 10); // 45,先运算完成得到45,再进行parseInt(45, 10)的运算
parseInt('15 * 3', 10); // 15,直接当作字符串处理,并不会进行乘法运算

3.对包含字符e的不同数据的处理差异

当传入的参数本身就是Number类型时,会将e按照科学计数法计算后转换成字符串,然后按照对应的基数转换得到最终的结果。

如果传入的字符串中直接包含e,那么并不会按照科学计数法处理,而是会判断字符 e是否处在可处理的进制范围内,如果不在则直接忽略,如果在则转换成对应的进制数。

parseInt(6e3, 10); // 6000,相当于parseInt('6000', 10)
parseInt(6e3, 16); // 24576,相当于parseInt('6000', 16)
parseInt('6e3', 10); // 6,相当于parseInt('6', 10)
parseInt('6e3', 16); // 1763,相当于6×256 +14×16 + 3 = 1763

4.对浮点型数的处理

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

parseInt('6.01', 10); // 6 
parseInt('6.99', 10); // 6

5.map()函数与parseInt()函数的隐形坑

比如想把数组中每个number类型的字符串元素都转为整数,如果使用下面方法就会出问题

var arr = ['1', '2', '3', '4'];
var result = arr.map(parseInt);
console.log(result);

因为

arr.map(parseInt);
实际与下面这个等效
arr.map(function (val, index) { 
	return parseInt(val, index);
});

实际处理过程是这样

parseInt('1', 0); // 1 
parseInt('2', 1); // NaN 
parseInt('3', 2); // NaN 
parseInt('4', 3); // 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); // [1, 2, 3, 4]

(3)parseFloat() 函数

parseFloat()函数用于解析一个字符串,并返回对应的浮点数。如果给定值不能转换为数值,则会返回NaNparseFloat()函数没有进制的概念。

有下面 4 条注意点:
1.如果在解析过程中遇到了正负号(+ / -)、数字0~9、小数点或者科学计数法 (e / E)以外的字符,则会忽略从该字符开始至结束的所有字符,然后返回当前已经解析的字符的浮点数形式。其中,正负号必须出现在字符的第一位,而且不能连续出现

parseFloat('+1.2'); // 1.2
parseFloat('-1.2'); // -1.2
parseFloat('++1.2'); // NaN,符号不能连续出现 
parseFloat('--1.2'); // NaN,符号不能连续出现 
parseFloat('1+1.2'); // 1,'+'出现在第二位,不会当作符号位处理

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

parseFloat(' 1.2'); // 1.2
parseFloat('f1.2'); // NaN

3.对于字符串中出现的合法科学运算符e,进行运算处理后会转换成浮点型数。这点与parseInt()函数的处理有很大的不同。

parseFloat('4e3'); // 4000
parseInt('4e3', 10); // 4

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

parseFloat('11.20'); // 11.2
parseFloat('11.2.1'); // 11.2

(4)小结

  • Number()函数转换的是传入的整个值,并不是像parseInt()函数和parseFloat()函数一样会从首位开始匹配符合条件的值。如果整个值不能被完整转换,则会返回NaN
  • parseFloat()函数在解析小数点时,会将第一个小数点当作有效字符,而parseInt() 函数在解析时如果遇到小数点会直接停止,因为小数点不是整数的一部分。
  • parseFloat()函数在解析时没有进制的概念,而parseInt()函数在解析时会依赖于传入的基数做数值转换。

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

NaN存在的目的:在某些异常情况下保证程序的正常执行。

NaN的 2 个特点:任何涉及NaN的操作都会返回NaN;NaN与任何值都不相等,包括与NaN本身

判断NaN时,ES5提供了isNaN()函数,ES6为Number类型增加了静态函数isNaN()函数----Number.isNaN()

(1)isNaN()函数

传递的参数如果是number类型数据那很容易判断。

如果传递的参数不是number类型,就比较麻烦。因为isNaN()函数会进行数据的类型转换,它在处理的时候会去判断传入的变量值能否转换为数字,如果能转换成数字则会返回false,如果无法转换则会返回true

NaN的产生条件:

  1. 数据运算时返回了一个无法表示的数值,例如0 / 0(其他数除以0会返回infinity
  2. 需要做强制类型转换时,某些数据不能直接转换为数值类型,就会返回NaN,例如1 - 'a' = NaN

下面是一些示例

isNaN(NaN); // true 
isNaN(undefined); // true
isNaN({});	//true
isNaN("JavaScript"); // true,字符串"JavaScript"无法转换成数字 

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

// Date类型
isNaN(new Date()); // false 
isNaN(new Date().toString()); // true

其中Date类型比较特殊,当我们调用new Date()函数生成的实例并转换为数值类型时,会转换为对应的时间戳,例如下面的代码

Number(new Date()); //1543333199705

因此isNaN(new Date())会返回false
而当我们调用了toString()函数时,返回的是一串字符串表示的时间,无法转换成数值类型,因此isNaN(new Date().toString())会返回true

(2)Number.isNaN()函数

ES6中的Number.isNaN()函数会在真正意义上去判断变量是否为NaN,不会做数据类型转换只有在传入的值为NaN时,才会返回true,传入其他任何类型的值时会返回false

(3)小结

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

  • isNaN()函数在判断是否为NaN时,需要先进行数据类型转换,只有在无法转换为数字时才会返回true
  • Number.isNaN()函数在判断是否为NaN时,只需要判断传入的值是否为NaN,并不会进行数据类型转换

1.2.5 浮点型运算

(1)浮点型运算不准问题

在JavaScript中,整数和浮点数都属于Number类型,它们都统一采用64位浮点数进行存储。

虽然它们存储数据的方式是一致的,但是在进行数值运算时,却会表现出明显的差异
性。整数参与运算时,得到的结果往往会和我们所想的一样;而对于浮点型运算,有时却会出现一些意想不到的结果

例如

0.1 + 0.2 = 0.30000000000000004
0.7 * 180 = 125.99999999999999

具体原因涉及到计算机中的存储和计算方式,比较复杂而且感觉没必要了解特别清楚就先不在这里赘述

(2)解决方案

主要思路是将浮点数先乘以一定的数值转换为整数,通过整数进行运算,然后将结果除以相同的数值转换成浮点数后返回

比如 2.1和 3.15 做运算,选其中小数点后位数更多的作为乘数因子,例如 2.1 就是10,3.15就是100,那么就选100。随后两者同时乘以最大乘数因子后再进行运算,最后再除回去


1.3 String类型详解

JavaScript中的String类型(字符串类型)既可以通过双引号""表示,也可以通过单引号''表示,而且是完全等效的

在程序处理时,我们同样不可避免地会遇到将其他类型转换为String类型的场景。如果是引用类型的数据,则在转换时总是会调用toString()函数,得到不同类型值的字符串表示;如果是基本数据类型,则会直接将字面值转换为字符串表示形式。

在将某个数据转换为字符串时,有一个简单的方法是直接使用加号+拼接一个空字符串""

console.log(123 + ''); // '123'
console.log([1, 2, 3] + ''); // '1,2,3'
console.log(true + ''); // 'true'

1.3.1 String类型的定义与调用

JS中有3种定义字符串的方式,分别是字符串字面量;直接调用String() 函数;new String()构造函数

(1)字符串字面量

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

var str = 'hello JavaScript'; // 正确写法
var str2 = "hello html"; // 正确写法

(2)直接调用String()函数

直接调用String()函数,会将传入的任何类型的值转换成字符串类型

转换规则如下:

1.如果是Number类型的值,则直接转换成对应的字符串

String(123); // '123' 
String(123.12); // '123.12'

2.如果是Boolean类型的值,则直接转换成true或者false

String(true); // 'true' 
String(false); // 'false'

3.如果值为null,则返回字符串null

String(null); // 'null'

4.如果值为undefined,则返回字符串undefined

String(undefined); // 'undefined'

5.如果值为字符串,则直接返回字符串本身

String('this is a string'); // 'this is a string'

6.如果值为引用类型,则会先调用toString()函数获取返回值,将返回值按照上述步骤1~5判断能否转换字符串类型。如果都不满足,则会调用对象的valueOf()函数获取返回值,并将返回值重新按照步骤1~5判断能否转换成字符串类型,如果也不满足,则会抛出类型转换的异常

(3)new String()构造函数

new String()构造函数使用new运算符生成String类型的实例。

对于传入的参数同样采用和上述String()函数一样的类型转换策略,最后的返回值是一个String类型对象的实例

new String('hello JavaScript'); // String {"hello JavaScript"}

(4)三者在做比较时的区别

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

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

var str2 = String(str);
var str3 = String('hello');
var str4 = new String(str); 
var str5 = new String(str); 
var str6 = new String('hello');
str === str2; // true 
str2 === str3; // true 
str3 === str4; // false 
str4 === str5; // false 
str5 === str6; // false

(5)函数的调用

在String对象的原型链上有一系列的函数,例如indexOf()函数、substring()函数、slice()函数等,通过String对象的实例可以调用这些函数做字符串的处理。

但是我们发现,采用字面量方式定义的字符串没有通过new运算符生成String对象的实例也能够直接调用原型链上的函数

'hello'.indexOf('e'); // 1
'hello'.substring(1); // 'ello'
'hello'.slice(1); // 'ello'

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

eval()函数会将基本字符串作为源代码处理,如果涉及表达式会直接进行运算,返回运算后的结果;而字符串对象则会被看作对象处理,返回对象本身

var s1 = '2 + 2'; // 创建一个字符串字面量
var s2 = new String('2 + 2'); // 创建一个对象字符串 
console.log(eval(s1)); // 4
console.log(eval(s2)); // String {"2 + 2"}

1.3.2 String类型常见算法

关于算法相关还是主要想参考《代码随想录》进行学习,因此此部分只考虑记录一下相关题目,并不记录具体分析和解决过程。

等后面专门复习算法部分时候,可以再顺便参考原书过一遍这块的相关解法。

(1)字符串逆序输出

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

(3)去除字符串中重复的字符

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


1.4 运算符

在JavaScript中描述了一组用于操作数据值的运算符,包括算术运算符(+-)、关系运算符(><)、等于运算符(=====)、位运算符(与&&、 或||、非!)等。

由于JavaScript是弱类型语言,因此在运算符的使用上更加灵活,接下来就对其中比较重要的一些运算符进行详细的讲解。

1.4.1 等于运算符

  • 双等于运算符==在比较时,会将两端的变量进行隐式类型转换,然后比较值的大小
  • 三等于运算符===在比较时,会优先比较数据类型数据类型相同才去判断值的大小, 如果类型不同则直接返回false

(1)三等于运算符

基本规则如上面所说。

额外需要注意的是:

  • 如果参与比较的值中有任何一方为NaN,则直接返回false,所以两个NaN也不能三等于
  • 如果比较的值都是null或者undefined,则返回true;如果只有一方为null或者undefined,则返回false
  • 如果比较的值都是引用类型,则比较的是引用类型的地址,当两个引用指向同一个地址时,则返回true,否则返回false
var a = []; var b = a;
var c = [];
console.log(a === b); // true
console.log(a === c); // false
console.log({} === {}); // false

实际上,如果不是通过赋值运算符=将定义的引用类型的值赋予变量,那么引用类型的值在比较后都会返回false,所以我们会发现空数组或者空对象的直接比较返回的是false

[] === []; // false 
{} === {}; // false

(2)双等于运算符

因为它不区分数据类型,而且会做隐式类型转换,所以要稍微复杂些。

下面是一些规则

  1. 如果比较的值类型相同,则采用与三等于运算符一样的规则

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

    • 如果比较的一方是null或者undefined,只有在另一方是null或者undefined的情况下才返回true,否则返回false
    • 如果比较的是字符串数值类型数据,则会将字符串转换为数值后再进行比较,如果转换后的数值相等则返回true,否则返回false。(需要注意的是,如果字符串是十六进制的数据,会转换为十进制后再进行比较。字符串并不支持八进制的数据,如果字符串以0开头,则0会直接省略,后面的值当作十进制返回)
    • 如果其中一个值是对象类型,另一个值是基本数据类型或者对象类型,则会调用对象的valueOf()函数或者toString()函数,将其转换成基本数据类型后再作比较

1.4.2 typeof运算符

(1)基本使用

typeof运算符用于返回操作数的数据类型,有以下两种使用形式

1. typeof operand
2. typeof(operand)

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

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

下面是typeof运算符在处理不同数据类型时得到的不同结果

类型 结果
Undefined “undefined”
Null “object”
Boolean “boolean”
Number “number”
String “string”
Symbol (ES6新增) “symbol”
函数对象 “function”
任何其他对象 “object”

不止是Undefined类型,一共有3种值会返回undefined

  1. 未声明的变量
  2. 已声明但未初始化的变量
  3. undefined本身

NaN也是number类型的值,因此typeof(NaN) === 'number'

对于Function类型的数据,可以概括为以下这些值,typeof运算符在处理时会返回function

  • 函数的定义,包括函数声明或者函数表达式两种形式
  • 使用class关键字定义的类(class是在ES6中新增的关键字,它不是一个全新的概念,原理依旧是原型继承,本质上仍然是一个Function)
  • 某些内置对象的特定函数,例如Math.sin()函数、Number.isNaN()函数等
  • Function类型对象的实例,一般通过new关键字得到,例如typeof new Function() === 'function'

对于Object类型的数据,可以概括为以下这些值,typeof运算符在处理时会返回object

  • 对象字面量形式。例如{name: 'kingx'}
  • 数组。例如[1, 2, 3]Array(1, 2, 3)
  • 所有构造函数通过new操作符实例化后得到的对象,例如new Date()new function(){},但是new Function(){}除外
  • 通过new操作符得到的基本数据类型的包装类型对象,如new Boolean(true)new Number(1),但不推荐这么写

(2)使用typeof需要注意的问题

typeof运算符的使用在绝大部分情况下都是安全的,但是在ES6以后情况就不一样了。大致有下面 3 种问题需要在使用typeof运算符时进行考虑:

  1. typeof运算符会区分对待Object类型和Function类型
  2. typeof运算符对null的处理(会是object
  3. typeof运算符相关语法的括号(有时括号是必要的,涉及处理优先级问题)

1.4.3 逗号运算符

逗号基本有两种用途:

  1. 函数传递多个参数时使用逗号分隔
  2. 作为运算符将多个表达式连接起来

对于第二种用途,如下所示

x = 8 * 2, x * 4

这是一个使用了逗号运算符的语句,首先执行左边的部分,x = 8×2,即x = 16,然后执行右边的语句,x×4 = 16×4 = 64,并将其返回。如果将整个语句赋值给一个 变量y,则该变量y的值为64。

(1)在for循环中批量执行表达式

比如:

for (var i = 0, j = 10; i < 10, j < 20; i++, j++) 
	{ console.log(i, j);
}

(2)用于交换变量,无须额外变量

需要交换两个变量值时的常规做法如下:


var a = 'a'; 
var b = 'b';
var c;
c = a; 
a = b; 
b = c;

如果不使用额外的变量储存,那么下面有 2 种通过,实现的方案:

var a = 'a'; 
var b = 'b';
// 方案1
a = [b, b = a][0]; 
// 方案2
a = [b][b = a, 0];

在方案1中,前一部分[b, b = a]是一个一维数组,数组第二项值是b = a,实际会将 a值赋给b,然后返回“‘a’”,因此数组最终的值为[‘b’, ‘a’],然后取索引0的值为’b’,赋 给变量a,最终实现a = ‘b’, b = ‘a’。

在方案2中,前一部分[b]是一个一维数组,后一部分[b = a, 0],实际会先执行b = a,将a值赋给b,然后返回“0”,因此后一部分实际是修改了b的值并返回索引“0”, 最终是a = [b][0],即a = b,实现了a与b的交换。

(3)用于简化代码

比如

if (x) { 
	foo();
	return bar(); 
} else {
return 1; 
}

使用,简化后

x ? (foo(), bar()) : 1;

(4)用小括号保证逗号运算符的优先级

在所有的运算符中,逗号运算符的优先级是最低的,因此对于某些涉及优先级的问题,我们需要使用到小括号,将含有逗号运算符的表达式括起来。

例如

var a = 20; 
var b = ++a, 10;
console.log(b); // Uncaught SyntaxError: Unexpected number

修改为

var a = 20;
var b = (++a, 10); 
console.log(b); // 10

1.4.4 运算符优先级

优先级决定了表达式在执行时的先后顺序,其中优先级最高的最先执行,优先级最低的最后执行。

首先可以先列出所有运算符类型,如下所示

1. 一元运算符 (newdeletetypeofvoid) 
2. 乘除法运算符 (*/%++) 
3. 加减法运算符 (+-) 
4. 关系运算符 (<><=>=instanceof) 
5. 相等运算符 (==!====!==) 
6. 逻辑运算符 (&&||!) 
7. 条件运算符 (?:) 
8. 赋值运算符 (=+=-=*=/=%=<<=>>=>>>=) 
9. 逗号运算符 (,)

优先级顺序为:

一元运算符 > 乘除法运算符 > 加减法运算符 > 关系运算符 > 相等运算符 > 逻辑运算符 > 条件运算符 > 赋值运算符 > 逗号运算符


1.5 toString()函数与valueOf()函数

在JavaScript中,toString()函数与valueOf()函数解决的是值的显示运算的问题, 所有引用类型都拥有这两个函数。

1.5.1 两种函数基本知识

(1)toString()函数

toString()函数的作用是把一个逻辑值转换为字符串,并返回结果。

Object类型数据的toString()函数默认的返回结果是[object Object],当我们自定义新的类时,可以重写toString()函数,返回可读性更高的结果。

在JavaScript中,ArrayFunctionDate等类型都实现了自定义的toString()函数:

  • ArraytoString()函数返回值为以逗号分隔构成的数组成员字符串,例如[1,2,3].toString()结果为字符串’1,2,3’
  • FunctiontoString()函数返回值为函数的文本定义,例如(function(x){return x*2;}).toString()的结果为字符串"function(x){return x * 2;}"
  • DatetoString()函数返回值为具有可读性的时间字符串,例如,new Date().toString()的结果为字符串"Sun Nov 25 2018 15:00:16 GMT+0800 (中国标准时间)"

(2)valueOf()函数

valueOf()函数的作用是返回最适合引用类型的原始值,如果没有原始值,则会返回引用类型自身

Object类型数据的valueOf()函数默认的返回结果是"{}",即一个空的对象字面量。

对于ArrayFunctionDate等类型,valueOf()函数的返回值:

  • ArrayvalueOf()函数返回的是数组本身,例如[1, 2, 3].valueOf()返回的结果为“[1,2,3]”
  • FunctionvalueOf()函数返回的是函数本身,例如(function(x){return x * 2;}).valueOf()返回的结果为函数本身“function(x){return x * 2;}”
  • DatevalueOf()函数返回的是指定日期的时间戳,例如new Date().valueOf()返回 的结果为“1543130166771”

1.5.2 两个函数同时存在时做隐式转换的选择

如果一个引用类型的值既存在toString()函数又存在valueOf()函数,那么在做隐式转换时,会调用哪个函数呢?

这里我们可以概括成 2 种场景,分别是引用类型转换为String类型,以及引用类型转换为Number类型。

(1)引用类型转换为String类型

一个引用类型的数据在转换为String类型时,一般是用于数据展示,转换时遵循以下规则:

  1. 如果对象具有toString()函数,则会优先调用toString()函数。如果它返回的是一个原始值,则会直接将这个原始值转换为字符串表示,并返回该字符
  2. 如果对象没有toString()函数,或者toString()函数返回的不是一个原始值,则会再去调用valueOf()函数,如果valueOf()函数返回的结果是一个原始值,则会将这个结果转换为字符串表示,并返回该字符串
  3. 如果通过toString()函数或者valueOf()函数都无法获得一个原始值,则会直接抛出类型转换异常

(2)引用类型转换为Number类型

一个引用类型的数据在转换为Number类型时,一般是用于数据运算,转换时遵循以 下规则:

  1. 如果对象具有valueOf()函数,则会优先调用valueOf()函数,如果valueOf()函数返回一个原始值,则会直接将这个原始值转换为数字表示,并返回该数字
  2. 如果对象没有valueOf()函数,或者valueOf()函数返回的不是原生数据类型,则会再去调用toString()函数,如果toString()函数返回的结果是一个原始值,则会将这个结果转换为数字表示,并返回该数字
  3. 如果通过toString()函数或者valueOf()函数都无法获得一个原始值,则会直接抛出 类型转换异常

事实上,对除了Date类型以外的引用类型数据转换为原生数据类型时,如果是用于数据运算,则会优先调用valueOf()函数,在valueOf()函数无法满足条件时,则会继续调用toString()函数,如果toString()函数也无法满足条件,则会抛出类型转换异常。

如果是用于数据展示,则会优先调用toString()函数,在toString()函数无法满足条 件时,则会继续调用valueOf()函数,如果valueOf()函数也无法满足条件,则会抛出类型 转换异常。


1.6 JavaScript中常用的判空方法

一个便捷判断变量是否为空的方法是,对变量取反,然后判断是否为true

if(!x){}

这是一个便捷判断变量是否为空的方法,但是其涉及的场景却很多,这里我们就分多种情况来讨论变量判空的方法:

(1)判断变量为空对象

a. 判断变量为null或者undefined

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

if(obj == null) {} // 可以判断null或者undefined 
if(obj === undefined) {} // 只能判断undefined

b. 判断变量为空对象{}

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

// 判断变量为空 
function isEmpty(obj) {
	for(let key in obj) { 
		if(obj.hasOwnProperty(key)) {
			return false; 
		}
	}
	return true;
}

(2)判断变量为空数组

判断变量是否为空数组时,首先需要判断变量是否为数组,然后通过数组的length 属性确定。当以上两个条件都满足时,变量是一个空数组。

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;

(5)!x == true的所有情况

本小节一开始就讲到 !x 为true时,会包含很多种情况,这里我们一起来总结下:

  • 变量为null
  • 变量为undefined
  • 变量为空字符串' '
  • 变量为数字0,包括+0-0
  • 变量为NaN

1.7 JavaScript中的switch语句

基本语法

switch(expression) { 
	case value1:
		statement1;
		break; 
	case value2:
		statement2;
		break; 
	default:
		statement; 
}

JavaScript中的switch特殊的点在于,switch语句可以用来判断任何类型的值,不一定是Number类型。

要注意JavaScript中对于case的比较是采用严格相等===的!

你可能感兴趣的:(前端学习记录,前端~JavaScript,前端,学习,javascript)