JavaScript 语言的每一个值,都属于某一种数据类型。JavaScript 的数据类型,共有六种。
ES6新增加了第七种 Symbol 类型
基本(简单)数据类型
数值(number)
字符串(string)
布尔值(boolean)
布尔值代表“真”和“假”两个状态。“真”用关键字true表示,“假”用关键字false表示。布尔值只有这两个值。
undefinednullfalse0NaN""或''(空字符串) 除了这6个为false 其余都为true
undefined
undefined == null
// true
Number(undefined) // NaN
5 + undefined // NaN
null
null与undefined都可以表示“没有”,含义非常相似。将一个变量赋值为undefined或null,语法效果几乎没区别。
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
o1和o2指向同一个对象,因此为其中任何一个变量添加属性,另一个变量都可以读写该属性。
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
在栈内存中开辟a 和 b 两个空间 将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 运算符
数值、字符串、布尔值分别返回number、string、boolean。
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。