JS数据类型

95 阅读9分钟

JavaScript 语言的每一个值,都属于某一种数据类型。JavaScript 的数据类型,共有六种。

ES6新增加了第七种 Symbol 类型

基本(简单)数据类型

数值(number)

字符串(string)

布尔值(boolean)

布尔值代表“真”和“假”两个状态。“真”用关键字true表示,“假”用关键字false表示。布尔值只有这两个值。

  • undefined
  • null
  • false
  • 0
  • NaN
  • ""''(空字符串) 除了这6个为false 其余都为true

undefined

undefined == null
// true
Number(undefined) // NaN
5 + undefined // NaN

null

nullundefined都可以表示“没有”,含义非常相似。将一个变量赋值为undefinednull,语法效果几乎没区别。

Number(null) // 0
5 + null // 5

复杂(引用)数据类型

对象(object)

对象是最复杂的数据类型,又可以分成三个子类型

狭义的对象和数组是两种不同的数据组合方式,除非特别声明,本教程的“对象”都特指狭义的对象。函数其实是处理数据的方法,JavaScript 把它当成一种数据类型,可以赋值给变量,这为编程带来了很大的灵活性,也为 JavaScript 的“函数式编程”奠定了基础。

- 狭义的对象(object)

1、概述

1.1 生成对象
let obj = {
     a:   1,
     b:   2
  //键名  键值
}
1.2 键名

对象的所有键名都是字符串(ES6 又引入了 Symbol 值也可以作为键名),所以加不加引号都可以。

如果键名是数值,会被自动转为字符串。

对象的每一个键名又称为“属性”(property),它的“键值”可以是任何数据类型。如果一个属性的值为函数,通常把这个属性称为“方法”,它可以像函数那样调用。

1.3 对象引用

如果不同的变量名指向同一个对象,那么它们都是这个对象的引用,也就是说指向同一个内存地址。修改其中一个变量,会影响到其他所有变量。

let o1 = {};
ket o2 = o1;

o1.a = 1;
o2.a // 1

o2.b = 2;
o1.b // 2

o1o2指向同一个对象,因此为其中任何一个变量添加属性,另一个变量都可以读写该属性。

2、属性操作

2.1 读取

读取对象的属性,有两种方法,一种是使用点运算符,还有一种是使用方括号运算符。

let obj = {
     a:   1,
     b:   2
}
obj.a   //1
obj['a']  //1 键名必须放在引号里面,否则会被当作变量处理
2.2 赋值
let obj = {};
obj.a = 1
obj['b']=2

2.3 查看
let obj = {
     a:   1,
     b:   2
}
Object.keys(obj) // ['a','b']
2.4 删除

delete命令用于删除对象的属性,删除成功后返回true.

let obj = { p: 1 };
Object.keys(obj) // ["p"]

delete obj.p // true
obj.p // undefined
Object.keys(obj) // []
2.5 in运算符

in运算符用于检查对象是否包含某个属性(注意,检查的是键名,不是键值),如果包含就返回true,否则返回false。它的左边是一个字符串,表示属性名,右边是一个对象。

var obj = { p: 1 };
'p' in obj // true
'toString' in obj // true

in运算符的一个问题是,它不能识别哪些属性是对象自身的,哪些属性是继承的。就像上面代码中,对象obj本身并没有toString属性,但是in运算符会返回true,因为这个属性是继承的。

这时,可以使用对象的hasOwnProperty方法判断一下,是否为对象自身的属性。

var obj = {};
if ('toString' in obj) {
  console.log(obj.hasOwnProperty('toString')) // false****
}
2.6 遍历

for...in循环用来遍历一个对象的全部属性。

var obj = {a: 1, b: 2, c: 3};

for (var i in obj) {
  console.log('键名:', i);
  console.log('键值:', obj[i]);
}
// 键名: a
// 键值: 1
// 键名: b
// 键值: 2
// 键名: c
// 键值: 3

for...in循环有两个使用注意点。

  • 它遍历的是对象所有可遍历(enumerable)的属性,会跳过不可遍历的属性。
  • 它不仅遍历对象自身的属性,还遍历继承的属性。

函数(function)

1、函数的声明

JavaScript 有三种声明函数的方法。

(1)function 命令

function fn1(s) {
  console.log(s);
}

上面的代码命名了一个fn1函数,以后使用fn1()这种形式,就可以调用相应的代码。这叫做函数的声明(Function Declaration)。

(2)函数表达式

除了用function命令声明函数,还可以采用变量赋值的写法。

let kk = function(s) {
  console.log(s);
};

这种写法将一个匿名函数赋值给变量

(3)Function 构造函数

var add = new Function(
  'x',
  'y',
  'return x + y'
);

// 等同于
function add(x, y) {
  return x + y;
}

上面代码中,Function构造函数接受三个参数,除了最后一个参数是add函数的“函数体”,其他参数都是add函数的参数。

你可以传递任意数量的参数给Function构造函数,只有最后一个参数会被当做函数体,如果只有一个参数,该参数就是函数体。 总的来说,这种声明函数的方式非常不直观,几乎无人使用。

如果同一个函数被多次声明,后面的声明就会覆盖前面的声明

return

函数体内部的return语句,表示返回。JavaScript 引擎遇到return语句,就直接返回return后面的那个表达式的值,后面即使还有语句,也不会得到执行。也就是说,return语句所带的那个表达式,就是函数的返回值。return语句不是必需的,如果没有的话,该函数就不返回任何值,或者说返回undefined

函数可以调用自身,这就是递归(recursion)。下面就是通过递归,计算斐波那契数列的代码。

function fib(num) {
  if (num === 0) return 0;
  if (num === 1) return 1;
  return fib(num - 2) + fib(num - 1);
}

fib(6) // 8

上面代码中,fib函数内部又调用了fib,计算得到斐波那契数列的第6个元素是8。

JavaScript 语言将函数看作一种值,与其它值(数值、字符串、布尔值等等)地位相同。凡是可以使用值的地方,就能使用函数。比如,可以把函数赋值给变量和对象的属性,也可以当作参数传入其他函数,或者作为函数的结果返回。函数只是一个可以执行的值,此外并无特殊之处。

由于函数与其他数据类型地位平等,所以在 JavaScript 语言中又称函数为第一等公民

函数名的提升 预解析

2、函数的属性方法

name属性

函数的name属性返回函数的名字。

function f1() {}
f1.name // "f1"

如果是通过变量赋值定义的函数,那么name属性返回变量名。

var f2 = function () {};
f2.name // "f2"

但是,上面这种情况,只有在变量的值是一个匿名函数时才是如此。如果变量的值是一个具名函数,那么name属性返回function关键字之后的那个函数名。

var f3 = function myName() {};
f3.name // 'myName'
length属性

函数的length属性返回函数预期传入的参数个数,即函数定义之中的参数个数。

function f(a, b) {}
f.length // 2
toString()

函数的toString()方法返回一个字符串,内容是函数的源码。

function f() {
  a();
  b();
  c();
}

f.toString()
// function f() {
//  a();
//  b();
//  c();
// }

3、函数作用域 √

4、参数

由于 JavaScript 允许函数有不定数目的参数,所以需要一种机制,可以在函数体内部读取所有参数。这就是arguments对象的由来。

arguments对象包含了函数运行时的所有参数,arguments[0]就是第一个参数,arguments[1]就是第二个参数,以此类推。这个对象只有在函数体内部,才可以使用。

var f = function (one) {
  console.log(arguments[0]);
  console.log(arguments[1]);
  console.log(arguments[2]);
}

f(1, 2, 3)
// 1
// 2
// 3

5、闭包 自执行

闭包

后面详细总结

自执行
(function(){ /* code */ }());
// 或者
(function(){ /* code */ })();

!function () { /* code */ }();
~function () { /* code */ }();
-function () { /* code */ }();
+function () { /* code */ }();

通常情况下,只对匿名函数使用这种“立即执行的函数表达式”。它的目的有两个:一是不必为函数命名,避免了污染全局变量;二是 IIFE 内部形成了一个单独的作用域,可以封装一些外部无法读取的私有变量

6、eval命令

eval命令接受一个字符串作为参数,并将这个字符串当作语句执行。

eval('var a = 1;');
a // 1

eval没有自己的作用域,都在当前作用域内执行,因此可能会修改当前作用域的变量的值,造成安全问题。

var a = 1;
eval('a = 2');

a // 2

数组(array) √

基础数据类型 与 引用(复杂)数据类型区别

1、在内存中的存储方式

任何数据都是在内存中存储的,内存中有一部分内存存储管理数据

栈内存 堆内存

基础类型 在栈内存中开辟空间,将数据直接存入这个空间中

引用类型 将数据存储在堆内存中 ,将堆内存的内存地址存储在栈内存中

2、赋值的方式

基础类型

  let a = 1
  let b =a
  a = 2
  console.log(b) // 1
  在栈内存中开辟ab 两个空间 将a中的数据复制了一份新的给b空间中
  给a重新赋值 但并没有再次给b赋值

引用类型

let arr = [1,2,3,4,5,6]
let brr=arr
arr[0] = 'c'
console.log(brr) // ["c",2,3,4,5,6]
把arr的内存地址 复制了一份给brr 所以他们俩指向同一个内存空间
修改后 brr也会改

注意!!!
let arr = [1,2,3,4,5,6]
let brr=arr
arr=[1,2,3] //重新给arr赋值 就是重新给arr一个内存地址
所以arr和brr不指向一个内存空间了
console.log(brr)//[1,2,3,4,5,6]

3、比较

基础类型

let a = 1 let b=1 log(a==b) log(a===b) 都是true

== 比较内存中存储的数据是否相等 ===先比较类型是否相等,然后在比较值 引用类型

let arr = [1,2]
let brr = [1,2]
log(arr==brr) false
log(arr===brr) false

检测数据类型

JavaScript 有三种方法,可以确定一个值到底是什么类型。

  • typeof运算符
  • instanceof运算符
  • Object.prototype.toString方法

typeof 运算符

数值、字符串、布尔值分别返回numberstringboolean

typeof 123 // "number"
typeof '123' // "string"
typeof false // "boolean"

函数返回function

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

undefined返回undefined

typeof window // "object"
typeof {} // "object"
typeof [] // "object"

null返回object

instanceof 运算符

Object.prototype.toString 方法