JS数据类型(包含对象的基本用法)

390 阅读2分钟

数据类型和运算符

js一共有7种数据类型,简称‘四基两空一对象’。

7种数据类型分别为:

  • 四基:String、Number、Symbol、Bool

  • 两空:null、undefined

  • 一对象:Object

其中string(字符串)、number数字、布尔值(bool)属于简单数据类型,它们已经分无可分了。

Object是复杂数据类型,还可以细分为狭义对象、数组、函数,我们一般说的对象不是指Object而是狭义对象。

特别要注意的是:函数、数组、日期都不是数据类型,他们都属于Object(考试要考)

typeof 运算符

js有三种方法可以确定数据类型到底是什么:

1、 typeof运算符

2、instanceof运算符

3、Object.prototype.toString方法

typeof 方法应用示例:

typeof 123 //"number"
typeof '123' //"string"
typeof false //"boolean"
typeof function(){} // "function"
typeof undefined // "undefined"
typeof null // "object"
typeof {a:1,b:2} // "object"
typeof [1,2,3] // "object"

typeof 数组或者null的时候返回的都是"object",如果想要区分数组和对象,则使用instanceof,数组是一种特殊的对象。

typeof null返回的是object是因为一开始设计js时,设计者把null作为一种特殊的对象。后来null独立出来了,为了兼容以前的代码,就没有对此做出修改。

Number

实际上JS的数字类型是64位浮点数,所以不管你打出来的是不是整数,它实际上都是浮点数的一种。

对于Number,能讲的可能是它的一些特殊值,比如说最大值Number.maxValue,最小值Number.minValue,正无穷+Infinity、负无穷-Infinity、和NaN(非数字)

可以使用Number.isNaN()方法来测试是不是NaN,如果是则返回true。

NaN与任何东西都不相等,包括它自己。 console.log(NaN === NaN);//false

目前数字具有精度不准的问题,例如:

for(var i= 0.1;i<2;i=i+0.1){
    if (i ===1){
        console.log(i);
        break;
    }
}
console.log(i)//2.0000000000000004

按照正常的逻辑,这段代码应当打出1才对,说明浮点数在相加的时候,会出现精度不准的情况。

parseInt

parseInt可以将浮点数转化成整数,直接截断小数点后面的数字。

parseInt(3.14) //3

parseInt()函数括号里的参数实际上是字符串,并且返回一个整数。

如果参数不是字符串,则会变成字符串进行转换。如果遇到不能转成数字的字符串,那么会停止,并返回已经转好的部分。

如果字符串第一个就不能转成数字,那么返回NaN

typeof parseInt('3.14') //3  number
parseInt('3a') //3
parseInt('a') //NaN

parseFloat

parseFloat 和parseInt类似,同样接受一个字符串,返回一个小数。当传入的字符串第一个就不能转化时,返回NaN。当参数是''时,同理。

parseFloat('')//NaN
parseFloat([])//NaN

isNaN

isNaN方法可以判断一个值是否为NaN,true代表是NaN。

isNaN(NaN) //true
isNaN(123) //false
isNaN([]) //false
isNaN('hello') //true
isNaN(['hello']) //true
isNaN([123]) // false
isNaN({}) // true

isNaN()括号中的参数会被先转化成数字,再返回布尔值。

比如,传入字符串,会被转化成数值,整个过程是类似于isNaN(Number('hello')),基于这个特性,只有一个数字的数组和空数组会返回false,因为Number([])会返回1,Number([123])返回123。

String

字符串的写法:

'hello'

"hello"

`hello`

现在我们都提倡用反引号来书写。这也是es6的最新写法,好处多多,反正干就对了。

在字符串中,如果想书写引号,有可能会被js当成是字符串,那么可以使用转义符\操作。

常见的转义符:

' 表示单引号

" 表示双引号

\n 表示换行

\r 表示回车

\t 表示制表符

\b 表示空格

字符串跟数字都不是对象,属于字面量。按理来说除了非对象都没有属性,但是字符串有对象的属性

例如:

str.length可以返回字符串的长度

字符串也可以用下标索引进行读取,就像数组一样。

例如:

'string'[0] // 返回s 's'[1] // 返回undefined

每个下标都是从0开始。如果索引超出字符串长度,则会返回undefined。跟数组不一样的是,字符无法像数组一样可以被更改。

boolean

布尔值只有两个,true和false。

以下操作会得到布尔值

  • 否定运算!value
  • 相等运算 1==2 2!=3 3===4
  • 比较运算 1>2 3<4 3<=4

需要注意的是逻辑运算符&& || 不能直接得到布尔值。

例如:

0 && 1 //得到0而不是false

六个falsy值

0、null、undefined、NaN、'' 、false

这六个falsy值返回的就是false

null和undefined

只有js语言才有两个空,这俩货没有本质上的区别。

有一些细节:

如果一个变量声明了但是未定义,那么变量为undefined

如果一个函数没有return,那么返回的也是undefined(我记得我写过一篇博客,log返回的也是undefined。)

前端程序员如果需要设置空,会定一个非对象为undefined,定一个对象为null(仅仅是习惯问题)。

symbol

千年难得一用,我现在不会~

再来说说变量声明的一些事

let es6推荐使用,不会有一些莫名其妙的bug

1、这种声明方式遵循作用域块,实际上就是大括号{} 2、不能重复声明 3、可以赋值也可以不赋值 4、必须先声明后使用,否则报错 5、用let声明全局变量,不会变成window的属性 6、for和let搭配会产生一个隐藏作用域

var 不推荐使用,太垃圾了。。。

关于let,我还写了一篇博客,欢迎阅读let薛定谔的变量提升(读方应杭知乎的一点延伸)

const

声明了一个常量,一旦声明了,就不能修改了。其他与let没区别。

类型转换

转化成字符串
let a =1
a.toString()//`a` 数字转化成字符串
String(a)//`a`
a + '' //a

字符串转数字
let b ='1'
Number(b)
parseInt(b)
parseFloat(b)
b - 0

转换成布尔值
Boolean(x)
!!x 取反可以得到布尔值,再取反就是原值的布尔值

Object对象

说到对象,需要先说明的一点 {foo:1,}这串代码在chrome中不是一个对象,是一个lambol。如果是firefox,可以不加逗号。

对象的定义

对象是一组无序的数据集合,以键值对的形式储存。

let foo= 'bar'
var obj = {
  foo: 'Hello',
  bar: 'World'
};
obj[foo] ==='world' //true
obj.foo === obj['foo'] //true

细节:键名key是字符串,引号可以省略 通过obj.foo 可以获得值:'hello',也可以通过obj['foo']获得值:'hello'

特别需要注意这个foo是字符串。如果写成这样obj[foo],那么foo就会被当成一个变量来解析。

通过Object.keys(obj)方法可以得到obj对象的所有key,以数组的形式返回。

通过Obekect.values(obj)方法可以得到obj对象的所有value,以数组的形式返回。

如果我们希望键key是一个变量,变量内存的是键名,可以使用以下方法[]中括号包裹住变量名

let a ='name'
var obj={[a]:'我是name'}

实际上这是以前语法的衍生。以前我们会这样使用变量来给对象添加属性

let obj={}
let name='age'
obj[name]=18
console.log(obj)结果为{age:18}

通过中括号[]这种方法来添加属性,会先求里面的值,再转化为字符串,例如: obj[1+2+3]='我是值' //得出obj为{6:我是值}

注意,数值键名不能使用点运算符(因为会被当成小数点),只能使用方括号运算符。

var obj = {
  123: 'hello world'
};
obj.123 // 报错
obj[123] // "hello world"

原型

这时候我们还可以思考一下变量在内存中的排列关系,例如内存图里面变量是怎么存储的。可以参见我的一篇博客。JS世界与内存

一句话总结:每个对象都有一个隐藏属性__proto__,里面保存了原型地址。原型实际上就是共有属性。Object函数实际上也是一个对象,它也有__proto__隐藏属性,指向了null

in关键字

如果我们想知道属性名是否在对象中,可以使用in关键字。

'name' in obj 注意:因为key是字符串,所以请加引号,不加引号需要事先赋值才可以用,不加引号这种方式很容易出错!!

可以通过delete obj.name来删除对象的某一个属性。

或者使用obj.name =undefined来将属性值设置为undefined。

额外说一点

使用obj.xxx来查看属性值是不是undefined,如果没有xxx这个属性名也会返回undefined

        let name = 'uname'
        var obj = {
            uname: 1,
            age: undefined
        }
        console.log(name in obj); //true
        console.log('age' in obj); //true
        console.log(obj.age); //undefined age的属性值是undefined
        console.log(obj.color); //undefined 
        没有color这个属性也出来undefined
        

可以使用以下方法获取值为undefined的属性xxx是否在对象中

'xxx' in obj && obj.xxx

使用Object.keys(obj)可以查看obj里面所有的属性名

使用console.dir(obj)查看自身属性+共有属性

查看共有属性可以直接打印obj.__proto__

判断一个属性是不是对象的私有属性 obj.hasOwnProperty('toString')

hasOwnPropertyin不能查看的区别在于,in无法判断是共有属性还是私有属性,而hasOwnProperty可以知道是不是私有属性

查看属性

可以使用obj.name或者使用obj['name']来查看对象的属性值。

注意:这里的属性名name是一个字符串。

请不要和obj[name]混淆了。

对象遍历

for..in 循环可以用于对象的遍历。

var obj = {a: 1, b: 2, c: 3};
for(let k in obj){
	console.log(k)
	console.log(obj[k])
} 
// k: a b c
//obj[k] 1 2 3

这里有两个注意点:

  • for in 会遍历对象所有可遍历的属性,会跳过不可遍历的属性
  • 如果有一些继承来的属性可以遍历,那么也会遍历

举例来说,对象都继承了toString属性,但是遍历时不会遍历到,因为它是不可遍历的。

但是有一些继承的属性默认可以遍历,那么假设我们只要遍历对象自身的属性,那么可以通过hasOwnProperty判断一下

for (let k in obj){
	if (obj.hasOwnproperty(k)){
    	console.log(obj[k])
    }
}

增或者改

JS中增加或者修改对象都是差不多的,如果有就修改,没有就增加。
例如:

var obj={name:'姓名'}
obj.name ='邱彦兮' //修改
obj['age'] =8  //增加

我们也可以批量对对象的属性名进行赋值。

可以使用Object.assign(obj,{age:18,size:18})进行批量赋值

看下面的例子:

    var obj = {};
    Object.assign(obj, {
        a: () => {
            console.log(123)
        },
        b: '123'
    })
    console.log(obj);

打印结果为

我们可以修改对象的私有属性,那么如果修改对象的共有属性呢?

JS实际上是允许修改的,例如我要修改一个对象obj的共有属性

    let obj = {};
    obj.__proto__.toString = 'xxx';
    Object.prototype.valueOf = 'yyy'
    console.dir(obj)

打印出来看看

修改成功!!

一般来说不建议修改原型,会出现很多问题。

如果说想换掉整个原型,可以使用create方法,实际上是创建了一个新的原型,而js自生成的原型就挂在这个创建的新原型上。

        let obj = Object.create({
            uname: 'xxxx'
        })
        console.dir(obj)

用一张图来描述到底create做了啥吧