一、数据类型概述
重学JavaScript基础!!之前跟着视频学习,稀里糊涂的,还是看文档好一些。
1、简介、
- 数值(number):整数和小数
- 字符串(string):文本
- 布尔值(boolean):
true
、false
- undefined:表示未定义或不存在
- null:表示空值;
null
是object的一种特殊值,通常用来表示未知空对象
var a = null
// 操作...
a = () => {}
- 对象:各种值的组成集合。其中对象分为三类:
- 狭义的对象
- 数组
- 函数:JavaScript把函数作为一种数据类型,可赋值给变量。(函数式编程)
2、typeof运算符
JavaScript有三种方法,可以确定一个值是声明类型。
- typeof
typeof 123 // number
typeof '123' // string
typeof false // boolean
typeof undefined //undefined
function fn () {}
typeof fn // function
typeof null // object
typeof window // object
typeof {} // object
typeof [] // object
- instanceof
// instanceof可以区分数组和对象
console.log([] instanceof Array) // true
console.log({} instanceof Array) // false
- Object.prototype.toString
二、null、undefined、布尔值
1、null和undefined
- 转为Boolean类型时,两者都是
false
console.log(Boolean(null)) // false
console.log(Boolean(undefined)) // false
- 使用==运算符,两值是相等的
if (null == undefined) { // true
console.log('null == undefined')
}
-
null
表示一个空对象,转为number为0
;undefined
为无定义的原始值,转为number为NaN
console.log(Number(null)); // 0
console.log(Number(undefined)); // NaN
2、布尔值
JavaScript如果预期某个位置应该是布尔值,则该位置现有的值会自动转换为布尔值。
转换规则:下面6个值会被转换为false
,其余值都为true
- 0
- NaN
- ''
- false
- nudefined
- null
三、数值
1、整数与浮点数
在JavaScript内部,所有数字都是以64位浮点数形式存储的。也就是说,JavaScript底层没有整数,所有数字都是小数。
整数部分运算,JavaScript会把64位浮点数转换为32位整数。
1 === 1.0 // true
由于浮点数不是精确的值,所以在进行小数比较和运算需特别小心。
0.1 + 0.2 === 0.3 // false
0.3 / 0.1 // 2.9999999999999996
(0.3 - 0.2) === (0.2 - 0.1) // false
// 0.09999999999999998 !== 0.1
2、数值精度和范围
2.1、数值精度:
国际IEEE754,JavaScript浮点数的64个二进制位,从左边开始,依次为:
- 第1位:符号位,0表示正数,1表示负数;
- 第2位 ~ 第12位(共11位)表示指数部分,指数部分的值在0到2047之间,不包含两个端点。
- 第13位 ~ 第64位(共52位)表示小数部分(即有效数字)
二进制数:(1)^符号位 * 1.xx...xx * 2^指数部分
由于指数部分的值在(0,2047)范围内,所以小数部分第一位默认为1,不保存在64为二进制数中,
所以小数部分最多能存储到53位二进制数。
2.2、数值范围:
Number.MAX_VALUE // 1.7976931348623157e+308
Number.MIN_VALUE // 5e-324
- 数值大于等于2的1024次方就会发生“正向溢出”,JavaScript无法表示这么大的数,就会返回
Infinity
。
Math.pow(2, 1024) // Infinity
- 数值小于等于2的-1075次方就会发生“负向溢出”,JavaScript无法表述这么小的数,就会直接返回
0
。
Math.pow(2, -1075) // 0
3、数值的表示法
var a = 22 // 十进制表示
a = 0xFF // 十六进制
// 科学计数法允许字母e或E的后面跟着一个整数,表示这个数的指数部分。
a = 123e3 // 科学计数法123000
a = 123e-3 // 科学计数法0.123
a = 3.1E+3 // 科学计数法3100
a = -3.1E+3 // 科学计数法-3100
以下两种情况JavaScript会自动将数值转为科学计数法表示,其余情况都将使用字面形式直接表示。
- 小数点前的数字多于21位
- 小数点后的0超过5位
4、数值的进制
使用字面量表示一个数值时,JavaScript提供四种进制:
- 二进制:有前缀
0b
或0B
的数值。 - 八进制:有前缀
0o
或0O
的数值,或者有前导0
、且只用到0-7
的八个阿拉伯数字的数值。 - 十进制:如果数字前面出现
0
会被视为八进制,若0
后面的数字为8
或9
时,会被视为十进制数。
前导0表示八进制,处理时很容易造成混乱。
ES5 的严格模式和 ES6,已经废除了这种表示法,
但是浏览器为了兼容以前的代码,目前还继续支持这种表示法。
- 十六进制:有前缀
0x
或0X
的数值。
默认情况下,JavaScript会将二进制、八进制、十六进制转换为十进制数表示。若出现不属于该进制的数字时,会报错。
5、特殊数值
5.1、正零和负零
区别:
- 64位浮点数的符号位不一样,但它们是等价的。
+0 // 0
-0 // 0
(+0).toString() // '0'
(-0).toString() // '0'
-0 === +0 // true
-0 === 0 // true
+0 === 0 // true
- 做分母时返回的值是不相等的。
(1 / -0) === (1 / +0) // false
(1 / -0) // -Infinity
(1 / +0) // Infinity
5.2、NaN
NaN是JavaScript的特殊值,表示”非数字“(Not a Number);主要出现在字符串转数字出错的场合。
NaN
不是独立的数据类型,是number类型中的一个特殊值。
typeof NaN // number
5 - 'x' // NaN
// 一些数学函数运算也会得到NaN
Math.acos(2) // NaN
Math.log(2) // NaN
Math.sqrt(2) // NaN
// 0除以0也会得到NaN
0 / 0 // NaN
运算规则:
- (1)、
NaN
不等于任何值,包括它本身
NaN === NaN // false
- (2)、数组的indexOf方法内部使用的是严格相等运算符,所以该方法对NaN不成立
[NaN].indexOf(NaN) // 结果位-1
- (3)、布尔值运算时被当作
false
Boolean(NaN) // false
- (4)、
NaN
与任何数运算,得到的都是NaN
NaN + 3 // NaN
NaN - 3 // NaN
NaN * 3 // NaN
NaN / 3 // NaN
- (5)、
NaN
与其他类型的值运算
// string
var a = NaN + '12' // 字符串拼接:NaN12
a = NaN - '12' // NaN,与字符串乘法、除法运算也是NaN
a = '12' - NaN // NaN
// boolean
a = NaN - false // NaN
a = NaN - true // NaN
// 与null、undefined加减乘除都能得到NaN
a = null + NaN // NaN
a = undefined + NaN // NaN
5.3、Infinity
和-Infinity
运算规则
Infinity === -Infinity // false
6、与数值相关的全局方法
6.1、parseInt()
parseInt(x, base)
:将字符串转为数字。
x
:待解析的字符串
base
:可选参数,默认为10;2~36之间的整数,表示被解析的值的进制。
- (1)、自动去除头部空格,若遇到不能转为数字的字符(包括小数点、空格),会停止转换,将返回已转换好的部分。
parseInt(' 123.89 6 ') // 123
- (2)、如果
parseInt()
的参数不是字符集,则先转为字符串再转换为整数。如果字符串的第一个字符不能转化为数字(后面跟着数字的正负号除外),返回NaN。
也就意味着,true
、false
、null
、undefined
将先转为字符串,再进行数值转换。
parseInt(123)
// 等同于
parseInt('123')
parseInt('abc') // NaN
parseInt(null) // NaN
parseInt('.3') // NaN
parseInt('') // NaN
parseInt('+') // NaN
parseInt('+1') // 1
所以parseInt()的返回值只有两种结果:数字、NaN
6.2、parseFloat()
parseFloat()
:将字符串转换为浮点数,自动去除头部空格;若字符串符合科学计数法,则会自动转换。
parseFloat('314e-2') // 3.14
// 其余的运算和parseInt()大同小异
6.3、isNaN()
isNaN()
方法用来判断一个number类型的值是否为NaN。
如果传入其他类型的值,自动会被转为数值(通过Number()方法转换),再做判断
isNaN(123) // false
isNaN('123') // false
isNaN(NaN) // true
isNaN('sada') // true
isNaN(true) // false
特殊的:
isNaN(['xzy']) // true
// 等同于
isNaN(Number(['xzy'])) // true
但是,对于空数组和只有一个数值成员的数组,isNaN返回false。
isNaN([]) // false
isNaN([123]) // false
isNaN(['123']) // false
因此,使用isNaN之前,最好判断一下数据类型。
function myIsNaN(value) {
return typeof value === 'number' && isNaN(value);
}
判断NaN更可靠的方法是,利用NaN为唯一不等于自身的值的这个特点,进行判断。
function myIsNaN(value) {
return value !== value;
}
6.4、isFinite()
isFinite
方法返回一个布尔值,表示某个值是否为正常的数值。
若传入其他类型的值将会被Number()
转换为number
类型在做判断
isFinite(Infinity) // false
isFinite(-Infinity) // false
isFinite(NaN) // false
isFinite(undefined) // false
isFinite(null) // true
isFinite(-1) // true
四、字符串
1、length属性
length
属性返回字符串的长度
var a = 'dsckjjnc'
console.log(a.length) // 8
2、字符集
JavaScript引擎内部所有字符都是用Unicode表示。
var a = '/u00A9';
console.log(a) // @版权符号
3、base64转码
base64
是一种编码方法 ,可以将任意值转换为0-9
、A-Z
、a-z
、+
、/
这64个字符组成的可打印字符。
使用它的目的不是加密,而是为了不出现特殊字符,简化程序处理。
使用场景:
文本中包含一些不可打印的字符,比如ASCII吗0到31的符号都无法打印出来;这时可以使用base64编码,把他们转换为可以打印的编码。
需要以文本格式传递二进制数据时,可以使用base64编码。
JavaScript原生提供两个base64方法:
-
btoa()
:任意值转为base64编码。 -
atob()
:base64编码转为原来的值。
注意:这两个方法不适合非ASCII码的字符,会报错
btoa('你好') // 报错
若需要将 非ASCII码字符串转为base64编码,需要先转码,再进行base64编码。
// 编码
function b64Encode(str) {
return btoa(encodeURIComponent(str)) // 转码,再base64编码
}
// 解码
function b64Decode(str) {
return DecodeURIComponent(atob(str)) // 先base64解码,再转码
}
五、对象
1、生成方法
对象是一组“键值对”(key-value)的组合,是一种无序的符合数据组合。
var obj = {
key: value
}
2、键名、键值
键名(属性):所有键名都是字符串(ES6引入Symbol值也可以作为键名),所以键名加不加引号都是可以的。
如果键名是数值,会被自动转为字符串。
var obj = {
1000: 'value'
}
obj['1000'] // value
obj[1000] // value
如果键名不符合标识符的的条件,则必须使用引号。
var obj = {
'1saj': '数字开头的key',
'h m': 'key中有空格',
'h+m': '可以中包含运算符'
}
键值(属性值):键值可以是任何数据类型。
如果一个属性的值为函数,通常把这个属性称为方法,它可以像函数那样调用。
3、属性的操作
3.1、属性的读取和赋值
使用点运算符
-
使用方括号运算符
var obj = { name: 'nico' } // 读取属性 obj.name // nico obj['name'] // nico // 属性赋值 obj.name = 'allen' // allen obj['name'] = 'jack' // jack
3.2、属性的查看和删除
- 查看:查看一个对象本身的所有属性,可以使用
Object.keys()
方法。该方法返回一个数组;若对象为空对象,则返回一个空数组。
var obj = {
name: 'nico',
age: 18
}
Object.keys(obj) // [ 'name', 'age' ]
-
删除:
delete
命令用于删除对象本身的属性。删除成功后返回
true
;删除对象上一个不存在的属性也会返回true
。若属性不可删除,则返回
false
delete
只能删除对象本身的属性,无法删除继承的属性。
var obj = {
name: 'nico'
}
console.log(delete obj.name) // true
delete obj.height // true
var objExtend = Object.defineProperty({}, 'p', {
value: 123,
configureable: false // 设置该属性不可改变
})
delete objExtend.p // true
console.log(objExtend.p) // 123,属性并没有被删除,依然存在。
3.3、属性是否存在
in
运算符用于检查对象是否包含某个属性(检查的是键名,不是键值)。但它不能识别哪些属性是自身的,哪些是继承来的。可以使用对象的方法hasOwnProperty
判断属性是否为对象自身的属性。
var obj = Object.defineProperty({}, 'name', { value: 'nico'})
obj.age = 18
console.log(obj.hasOwnProperty('name')) // true
console.log(obj.hasOwnProp1erty('age')) // true
console.log('name' in obj) // true
console.log('age' in obj) // true
3.4、属性的遍历
for...in
循环用来遍历一个对象的全部属性
var obj = {
name: 'nico',
age: 19
}
for(let key in obj) {
console.log('键名:' + key)
console.log('键值:' + obj[key])
}
// 键名:name
// 键值:nico
// 键名:age
// 键值:19
注意:
for...in
不仅遍历自身属性,还遍历继承来的属性。-
它遍历对象上所有可遍历的属性(enumberable),会跳过不可遍历的属性。
如果只想遍历对象自身的属性,需结合对象的
hasOwnProperty()
方法来判断实现。var obj = { name: 'nico', age: 19 } for(let key in obj) { if (obj.hasOwnProperty(key)) { console,log(key) } } // name // nico
4、with语句
with
语句的作用是操作同一对象的多个属性时,提供一些书写的方便
with (对象) {
语句;
}
var obj = {
name: '',
age: 0
}
// 操作对象属性
with(obj) {
name: 'nico',
age:20
}
// 等同于
obj.name = 'nico'
obj.age = 23
注意:如果with
区块内部有变量的赋值操作,必须是当前对象已存在的属性,否则会创建一个当前作用域的全局变量。
这是因为with
区块没有改变作用域,区块内部任然是当前作用域,所以with
语句绑定的对象不是很明确。建议不使用with
语句。
let obj = {
name: 'nico',
weight: '60KG'
}
with (obj) {
name = 'allen'
age = 18
}
console.log(obj.age) // undefined
conosle.log(age) // '60KG'
var x = 123
with (obj) {
let x = ''
console.log(x) // with无法判断x是区块内的变量x还是当前环境的变量x
}
六、函数
1、概述
JavaScript 语言将函数看作一种值,与其它值(数值、字符串、布尔值等等)地位相同。凡是可以使用值的地方,就能使用函数。比如,可以把函数赋值给变量和对象的属性,也可以当作参数传入其他函数,或者作为函数的结果返回。函数只是一个可以执行的值,此外并无特殊之处。
1.1、函数的声明
(1)、
function
命令(2)、函数表达式
-
(3)、
Function
构造函数:此种方式不直观,几乎不使用。function fun1 () {} const fun2 = function() {}; // 需要加分号用来结束语句 const fun3 = new Function('x', 'y', 'return x + y') // 等同于 function fun3(x, y) { return x + y } // 特殊的:该函数名`x`只在函数体内部有效,在函数体外部无效。 const f = function x() {} // 这种写法的用处有两个,一是可以在函数体内部调用自身;二是方便除错(除错工具显示函数调用栈时,将显示函数名,而不再显示这里是一个匿名函数)。
1.2、函数的重复声明
如果同一个函数被多次声明,后面的声明会覆盖前面的声明。
在JavaScript
中由于函数名的提升,第一次的函数声明在任何时候都是无效的!
1.3圆括号运算符、return语句和递归
圆括号运算符:调用函数时,要使用圆括号运算符;圆括号中可以加入函数的参数。
return语句
:JavaScript
引擎遇到return
语句就会返回return
语句后面的表达式,不会执行后续的代码;如果return
语句后面没有表达式,,该函数不会返回任何值,或者说返回undefined
。-
递归:函数可以调用自身。
function fib(num) { if (num === 0) return 0; if (num === 1) return 1; return fib(num - 2) + fib(num - 1); } fib(6) // 8
1.4、函数名的提升
JavaScript引擎
将函数名视同变量名,所以采用function
声明函数时,整个函数会像变量声明一样,被提升到代码头部。
fun() // 不会报错
function fun () {}
使用var
定义函数表达式
fun() // TypeError: undefined is not a function
var fun = () => {}
// 上面代码等同于
var fun; // 先进行变量提升,在进行赋值操作
fun();
fun = () => {}
变量提升函数提升与函数重复声明:
var fun = () => {
console.log(1)
}
function fun() {
console.log(2)
}
fun() // 1
// 解析:
// 1、先进行变量提升和函数提升
// var fun;
// function fun() {
// console.log(2)
// }
// 2、最后进行变量赋值操作。
2、函数的属性和方法
2.1、name属性和length属性
- 函数的
name
属性返回函数的名字
function fun1() {}
fun1.name // fun1
const fun2 = () => {}
fun2.name // fun2
// 特殊的
const fun3 = function x() {}
fun3.name // x
-
函数的
length
属性函数的
length
属性返回函数预期的参数个数。不管调用函数时传入多少个参数,length
属性总为定义函数时的参数个数。function fun(x, y) {} fun.length // 2
2.2、toString()方法
函数的toString()方法返回函数源码字符串
const fun = () => {}
console.log(fun.toString()) // () => {}
// 对于JavaScript原生函数,将返回 function Number() { [native code] }
console.log(Number.toString()) // function Number() { [native code] }