第1章 Javascript重点概念
前言
从本篇开始记录《Javascript重难点实例精讲》,关于Javascript系列预计从这本书出发,接着再通过《Javascript高等程序设计》和阮一峰老师的ES6教程进行完善
本篇为第1章 Javascript重点概念
1.1 JavaScript基本数据类型
1.1.1 JS数据类型基本分类
Javascript数据类型分为基本数据类型和引用数据类型
基本数据类型:
- undefined
- null
- boolean
- number
- string
- symbol(ES6 中新增)
引用数据类型(在第2和第3章再展开):
- object
- array
- function
- date等等
1.1.2 undefined类型
只有唯一字面值undefined,表示一个变量不存在
常见场景:
- 使用只声明而未初始化的变量时
- 获取一个对象的某个不存在的属性(自身属性和原型链继承属性)时
- 函数没有明确的返回值,却在其他地方使用了返回值时
- 函数定义时使用了多个形参,而在调用时传递的参数少于行参数量时
1.1.3 null类型
只有唯一字面值null,表示一个空指针对象,因此使用typeof检测null时会返回object
常见场景:
- 一般如果声明的变量是为了之后保存某个值,那声明时就应该赋值为
null - js在获取DOM元素时,如果没有获取到指定的元素对象,就会返回
null - 使用正则表达式进行捕获时,如果没有捕获结果,就会返回
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作为空对象 指针的惯例,还有助于区分null和undefined
1.1.5 boolean类型及其他类型与boolean类型的转换
只有两个值true和false,注意这是区分大小写的,例如 True 就不行。
Boolean类型使用最多的场景就是用于if语句判断。在JavaScript中,if语句可以接受任何类型的表达式,即if(a)语句中的a,可以是Boolean、Number、String、Object、 Function、Null、Undefined中的任何类型。
如果a不是Boolean类型的值,那么JavaScript解释器会自动调用Boolean()函数对a 进行类型转换,返回最终符合if语句判断的true或者false值。
不同类型与boolean类型的转换:
string->boolean:- 空字符串
""或者''->false - 其他的非空字符串 ->
true
- 空字符串
number->boolean:0和NaN->false- 其他所有,包含无穷大
infinity和无穷小-infinity->true
object->boolean:- object为
null->false - 不为
null,包括空对象{}->true
- object为
function->boolean:- 任何function类型 ->
true
- 任何function类型 ->
null->boolean:- null类型只有一个null值,会转换为
false
- null类型只有一个null值,会转换为
undefined->boolean- undefined类型只有一个undefined值,会转换为
false
- undefined类型只有一个undefined值,会转换为
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类型的数据转换
boolean->number:true->1false->0
null->number:- null类型只有一个字面值null,直接转换为
0
- null类型只有一个字面值null,直接转换为
undefined->number:- undefined类型只有一个字面值undefined,直接转换为
NaN
- undefined类型只有一个字面值undefined,直接转换为
string->number:- 字符串中只包含数字,会转为十进制数,如果前面有 0 或是八进制格式,会直接省略掉 0,如 0123 会转为 123
- 只包含有效的浮点数,同样转为十进制数
- 只包含有效的十六进制格式,会转为十进制数,如 0x3f 会转为 63
- 空字符串,或包含多个空格,转换为
0 - 如果包含上述格式之外的字符串,转换为
NaN
object->number:- Object类型在转换为Number类型时,会优先调用
valueOf()函数,然后通过其返回值按照上述规则进行转换。如果转换的结果是NaN,则调用toString()函数,通过其返回值重新按照上述规则进行转换;如果有确定的number类型返回值,则结束,否则返回NaN
- Object类型在转换为Number类型时,会优先调用
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()函数用于解析一个字符串,并返回对应的浮点数。如果给定值不能转换为数值,则会返回NaN。parseFloat()函数没有进制的概念。
有下面 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的产生条件:
- 数据运算时返回了一个无法表示的数值,例如
0 / 0(其他数除以0会返回infinity) - 需要做强制类型转换时,某些数据不能直接转换为数值类型,就会返回
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时,需要先进行数据类型转换,只有在无法转换为数字时才会返回trueNumber.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)双等于运算符
因为它不区分数据类型,而且会做隐式类型转换,所以要稍微复杂些。
下面是一些规则
-
如果比较的值类型相同,则采用与三等于运算符一样的规则
-
如果比较的值类型不同,则会按照下面的规则进行转换后再进行比较
- 如果比较的一方是
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:
- 未声明的变量
- 已声明但未初始化的变量
- 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运算符时进行考虑:
- typeof运算符会区分对待Object类型和Function类型
- typeof运算符对null的处理(会是
object) - typeof运算符相关语法的括号(有时括号是必要的,涉及处理优先级问题)
1.4.3 逗号运算符
逗号基本有两种用途:
- 函数传递多个参数时使用逗号分隔
- 作为运算符将多个表达式连接起来
对于第二种用途,如下所示
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. 一元运算符 (new、delete、typeof、void)
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中,Array,Function,Date等类型都实现了自定义的toString()函数:
Array的toString()函数返回值为以逗号分隔构成的数组成员字符串,例如[1,2,3].toString()结果为字符串’1,2,3’Function的toString()函数返回值为函数的文本定义,例如(function(x){return x*2;}).toString()的结果为字符串"function(x){return x * 2;}"Date的toString()函数返回值为具有可读性的时间字符串,例如,new Date().toString()的结果为字符串"Sun Nov 25 2018 15:00:16 GMT+0800 (中国标准时间)"
(2)valueOf()函数
valueOf()函数的作用是返回最适合引用类型的原始值,如果没有原始值,则会返回引用类型自身。
Object类型数据的valueOf()函数默认的返回结果是"{}",即一个空的对象字面量。
对于Array、Function、Date等类型,valueOf()函数的返回值:
Array的valueOf()函数返回的是数组本身,例如[1, 2, 3].valueOf()返回的结果为“[1,2,3]”Function的valueOf()函数返回的是函数本身,例如(function(x){return x * 2;}).valueOf()返回的结果为函数本身“function(x){return x * 2;}”Date的valueOf()函数返回的是指定日期的时间戳,例如new Date().valueOf()返回 的结果为“1543130166771”
1.5.2 两个函数同时存在时做隐式转换的选择
如果一个引用类型的值既存在toString()函数又存在valueOf()函数,那么在做隐式转换时,会调用哪个函数呢?
这里我们可以概括成 2 种场景,分别是引用类型转换为String类型,以及引用类型转换为Number类型。
(1)引用类型转换为String类型
一个引用类型的数据在转换为String类型时,一般是用于数据展示,转换时遵循以下规则:
- 如果对象具有
toString()函数,则会优先调用toString()函数。如果它返回的是一个原始值,则会直接将这个原始值转换为字符串表示,并返回该字符 - 如果对象没有
toString()函数,或者toString()函数返回的不是一个原始值,则会再去调用valueOf()函数,如果valueOf()函数返回的结果是一个原始值,则会将这个结果转换为字符串表示,并返回该字符串 - 如果通过
toString()函数或者valueOf()函数都无法获得一个原始值,则会直接抛出类型转换异常
(2)引用类型转换为Number类型
一个引用类型的数据在转换为Number类型时,一般是用于数据运算,转换时遵循以 下规则:
- 如果对象具有
valueOf()函数,则会优先调用valueOf()函数,如果valueOf()函数返回一个原始值,则会直接将这个原始值转换为数字表示,并返回该数字 - 如果对象没有
valueOf()函数,或者valueOf()函数返回的不是原生数据类型,则会再去调用toString()函数,如果toString()函数返回的结果是一个原始值,则会将这个结果转换为数字表示,并返回该数字 - 如果通过
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中的基本数据类型,包括undefined、null、boolean、number、string和symbol。探讨了数据类型的转换,特别是number类型转换的函数Number()、parseInt()和parseFloat(),以及浮点型运算的精度问题。此外,还讨论了字符串的定义、操作和常用算法,以及运算符的使用,如等于运算符和typeof运算符。最后提到了JavaScript中的switch语句和判空方法。

被折叠的 条评论
为什么被折叠?



