介绍

javascript的数据类型共有六种:

  • 数值(number):整数和小数
  • 字符串(string):文本
  • 布尔值(boolean):true和false
  • undefined:未赋值或不存在
  • null:空
  • 对象(object,array,function):集合

JS的数据类型 - 图1

typeof

  1. console.log(typeof 123);//number
  2. console.log(typeof '123');//string
  3. console.log(typeof false);//boolean
  4. console.log(typeof {});//object
  5. console.log(typeof []);//object
  6. console.log(typeof [1,2,3]); //object
  7. console.log(typeof function(){});//function
  8. console.log(typeof null);//object
  9. console.log(typeof undefined);//undefined

未定义和未赋值

  1. var arr;
  2. console.log(arr===undefined);//true
  3. console.log(typeof arr===undefined);//true
  4. console.log(typeof v === "undefined");//true

null undefined

特性

  • boolean值都为false
  • 相等运算符下,两者相等undefined==null //true
  • Nunber转换不一样Number(null)//0 Number(undefined)//NaN

区别

  1. var i;
  2. console.log(i);
  3. function test(a,b){
  4. console.log(b);
  5. }
  6. test();
  7. function test1(){
  8. }
  9. console.log(test1());
  10. var o = {};
  11. console.log(o.p);
  12. //全部是undefined

原始类型 引用类型

javascript和java一样,也有值传递和引用传递,值类型不可改变,对象变量指向堆里的地址
image.pngimage.png
image.png
image.png

惯例

对象现在不想赋值,可以赋给 null

非对象现在不想赋值,可以赋给undefined或者直接不赋值

boolean

规则

运算符

  • 前置逻辑运算符: ! (Not)
  • 相等运算符:=,!,==,!=
  • 比较运算符:>,>=,<,<=

自动转为布尔值的时候,除了下面六个值被转为false,其他值都视为true

  • undefined
  • null
  • false
  • 0
  • NaN
  • “”或’’(空字符串)

空数组和空对象的bool值为true

  1. console.log(Boolean({}));
  2. console.log(Boolean([]));
  3. //true

数组和对象

空数组([])和空对象({})对应的布尔值,都是true

  1. var arr = []
  2. var obj = {}
  3. if(arr){
  4. console.log(1) //1
  5. }
  6. if(obj){
  7. console.log(2) //2
  8. }
  9. if(arr.length===0){
  10. console.log("数组空")
  11. }
  12. if(Object.keys(obj).length===0){
  13. console.log("对象空")
  14. }

number

介绍

JavaScript 语言的底层根本没有整数,所有数字都是小数(64位浮点数) 1===1.0 //true
浮点数不是精确的值 0.1+0.2===0.3 //false 0.3/0.1 //2.9999999999999996
精确范围:-253到253
数值范围:21024到2-1075

Math.pow(2, 1024) // Infinity Math.pow(2, -1075) //5e-324 Math.pow(2, -1076) //0
Number.MAX_VALUE // 1.7976931348623157e+308
Number.MIN_VALUE // 5e-324

进制

  • 十进制:没有前导0的数值。
  • 八进制:有前缀0o或0O的数值,或者有前导0、且只用到0-7的八个阿拉伯数字的数值。
  • 十六进制:有前缀0x或0X的数值。
  • 二进制:有前缀0b或0B的数值。

特殊数值

+0 和 -0

几乎所有场合,正零和负零都会被当作正常的0

  1. -0 === +0 // true
  2. 0 === -0 // true
  3. 0 === +0 // true
  4. +0 // 0
  5. -0 // 0

NaN:Not a Number

  1. 字符串解析成数字出错 5 - 'x' //NaN
  2. 数学函数的运算结果 Math.sqrt(-1) //NaN
  3. 0除以0 0 / 0 //NaN
  4. NaN是数值 typeof NaN // 'number'
  5. NaN不等于任何值 NaN === NaN //false
  6. NaN在布尔运算时被当作false Boolean(NaN) //false
  7. NaN与任何数(包括它自己)的运算,得到的都是NaN NaN + 32 //NaN

Infinity

Infinity表示“无穷”,用来表示两种场景。一种是一个正的数值太大,或一个负的数值太小,无法表示;另一种是非0数值除以0,得到Infinity

  1. Math.pow(2, 1024) // Infinity
  2. 0 / 0 // NaN
  3. 1 / 0 // Infinity
  4. Infinity === -Infinity // false
  5. 1 / -0 // -Infinity
  6. -1 / -0 // Infinity

全局方法

parseInt():用于将字符串转为整数
转为整数的时候,是一个个字符依次转换,如果遇到不能转为数字的字符,就不再进行下去,返回已经转好的部分
如果字符串的第一个字符不能转化为数字(后面跟着数字的正负号除外),返回NaN。

  1. parseInt(' 81') // 81
  2. parseInt('1.23') // 1
  3. parseInt('8a') // 8
  4. parseInt('12**') // 12
  5. parseInt('15px') // 15
  6. parseInt('abc') // NaN
  7. parseInt('.3') // NaN
  8. parseInt('') // NaN
  9. parseInt('1000', 2) // 8
  10. parseInt('1000', 6) // 216
  11. parseInt('1000', 8) // 512
  12. parseInt('1000', 10) // 1000
  13. parseInt([1,2,3]) //1
  14. parseInt({}) //NaN

parseFloat():用于将一个字符串转为浮点数

  1. parseFloat(true) // NaN
  2. Number(true) // 1
  3. parseFloat(null) // NaN
  4. Number(null) // 0
  5. parseFloat('') // NaN
  6. Number('') // 0
  7. parseFloat('123.45#') // 123.45
  8. Number('123.45#') // NaN

isNaN():用来判断一个值是否为NaN

  1. isNaN(NaN) // true
  2. isNaN(123) // false
  3. isNaN('Hello') // true
  4. // 相当于
  5. isNaN(Number('Hello')) // true
  6. isNaN({}) // true
  7. // 等同于
  8. isNaN(Number({})) // true
  9. isNaN({}) // true
  10. //判断NaN
  11. function myIsNaN(value) {
  12. return value !== value;
  13. }

isFinite():表示某个值是否为正常的数值

  1. isFinite(Infinity) // false
  2. isFinite(-Infinity) // false
  3. isFinite(NaN) // false
  4. isFinite(undefined) // false
  5. isFinite(null) // true
  6. isFinite(-1) // true

字符串

概述

  • 单引号字符串的内部,可以使用双引号。双引号字符串的内部,可以使用单引号
    'key = "value"' "It's a long journey"
  • 单引号字符串的内部,使用单引号,就必须在内部的单引号前面加上反斜杠,用来转义
    'Did she say \'Hello\'?' "Did she say \"Hello\"?"
  • 字符串代码分行 ```javascript var longString = ‘Long \ long \ long \ string’;

var longString = Long long long string;

  1. - 字符串可以被视为字符数组,因此可以使用数组的方括号运算符,但仅此而已
  2. ```javascript
  3. var s = 'hello';
  4. s[0] // "h"
  5. s[1] // "e"
  6. s[4] // "o"
  7. delete s[0];
  8. s // "hello"
  9. var s = 'hello';
  10. s.length // 5
  11. s.length = 3;
  12. s.length // 5
  • 字符串遍历
    1. var aaa = '1321321'
    2. Array.prototype.forEach.call(aaa,element => {
    3. console.log(element)
    4. })
    5. Array.from(aaa).forEach(item=>{
    6. console.log(item)
    7. })

转义

  • \0 :null(\u0000)
  • \b :后退键(\u0008)
  • \f :换页符(\u000C)
  • \n :换行符(\u000A)
  • \r :回车键(\u000D)
  • \t :制表符(\u0009)
  • \v :垂直制表符(\u000B)
  • ‘ :单引号(\u0027)
  • “ :双引号(\u0022)
  • \ :反斜杠(\u005C)

对象

  • 对象的所有键名都是字符串,所以加不加引号都可以
  • 对象的每一个键名又称为“属性”(property),它的“键值”可以是任何数据类型
  • 不同的变量名指向同一个对象,那么它们都是这个对象的引用
  1. var o1 = {};
  2. var o2 = o1;
  3. o1.a = 1;
  4. o2.a // 1
  5. o2.b = 2;
  6. o1.b // 2

属性的操作

属性的读取

  • 读取对象的属性,有两种方法,一种是使用点运算符,还有一种是使用方括号运算符 ```javascript var obj = { p: ‘Hello World’ };

obj.p // “Hello World” obj[‘p’] // “Hello World”

  1. - 如果使用方括号运算符,键名必须放在引号里面,否则会被当作变量处理
  2. - 方括号运算符内部还可以使用表达式 `obj['hello' + ' world']` `obj[3 + 3]`
  3. - 数字键可以不加引号,因为会自动转成字符串
  4. ```javascript
  5. var obj = {
  6. 0.7: 'Hello World'
  7. };
  8. obj['0.7'] // "Hello World"
  9. obj[0.7] // "Hello World"
  • 数值键名不能使用点运算符 ```javascript var obj = { 123: ‘hello world’ };

obj.123 // 报错 obj[123] // “hello world”

  1. <a name="a08f772d"></a>
  2. #### 属性的查看
  3. ```javascript
  4. var obj = {
  5. key1: 1,
  6. key2: 2
  7. };
  8. Object.keys(obj);
  9. // ['key1', 'key2']

属性的删除

  1. var obj = { p: 1 };
  2. Object.keys(obj) // ["p"]
  3. delete obj.p // true
  4. obj.p // undefined
  5. Object.keys(obj) // []

属性是否存在

  1. var obj = { p: 1 };
  2. 'p' in obj // true
  3. 'toString' in obj // true
  4. var obj = {};
  5. if ('toString' in obj) {
  6. console.log(obj.hasOwnProperty('toString')) // false
  7. }

属性的遍历

  1. var person = { name: '老张' };
  2. for (var key in person) {
  3. if (person.hasOwnProperty(key)) {
  4. console.log(key);
  5. }
  6. }

函数

概述

函数的声明

function

  1. function f(s){
  2. console.log(s);
  3. }

函数表达式

  1. var f = function(s){
  2. console.log(s);
  3. };

需要分号

递归

斐波那契数列

  1. function fib(num) {
  2. if (num === 0) return 0;
  3. if (num === 1) return 1;
  4. return fib(num - 2) + fib(num - 1);
  5. }
  6. fib(6) // 8

第一等公民

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

  1. function add(x, y) {
  2. return x + y;
  3. }
  4. // 将函数赋值给一个变量
  5. var operator = add;
  6. // 将函数作为参数和返回值
  7. function a(op){
  8. return op;
  9. }
  10. a(add)(1, 1)
  11. // 2

函数名提升

JavaScript 引擎将函数名视同变量名,所以采用function命令声明函数时,整个函数会像变量声明一样,被提升到代码头部

f(); function f() {}
f(); var f = function (){}; //报错

函数的属性和方法

name

  1. function f1() {}
  2. f1.name // "f1"
  3. var f2 = function () {};
  4. f2.name // "f2"
  5. var myFunc = function () {};
  6. function test(f) {
  7. console.log(f.name);
  8. }
  9. test(myFunc) // myFunc 获取参数函数的名字

length

函数预期传入的参数个数,即函数定义之中的参数个数
function f(a, b) {} f.length // 2

toString

返回一个字符串,内容是函数的源码

  1. function f() {
  2. a();
  3. b();
  4. c();
  5. }
  6. f.toString()
  7. // function f() {
  8. // a();
  9. // b();
  10. // c();
  11. // }
  12. //实现多行字符串
  13. var multiline = function (fn) {
  14. var arr = fn.toString().split('\n');
  15. return arr.slice(1, arr.length - 1).join('\n');
  16. };
  17. function f() {/*
  18. 这是一个
  19. 多行注释
  20. */}
  21. multiline(f);
  22. // " 这是一个
  23. // 多行注释"

函数作用域

Javascript 只有两种作用域:一种是全局作用域,变量在整个程序中一直存在,所有地方都可以读取;另一种是函数作用域,变量只在函数内部存在

  1. var v = 1;
  2. function f(){
  3. var v = 2;
  4. console.log(v);
  5. }
  6. f() // 2
  7. v // 1
  8. if (true) {
  9. var x = 5;
  10. }
  11. console.log(x); // 5 块级作用域

函数内部变量提升

  1. function a(){
  2. console.log(x);
  3. var x = 0;
  4. }
  5. a(); //undefined

函数自己的作用域

作用域与变量一样,就是其声明时所在的作用域,与其运行时所在的作用域无关

  1. var a = 1;
  2. var f = function(){
  3. console.log(a);
  4. }
  5. function ff(){
  6. var a=2;
  7. f();
  8. }
  9. ff(); //1
  10. var x = function () {
  11. console.log(a);
  12. };
  13. function y(f) {
  14. var a = 2;
  15. f();
  16. }
  17. y(x)
  18. // ReferenceError: a is not defined

参数

函数参数不是必需的,Javascript 允许省略参数

  1. function f(a, b) {
  2. return a;
  3. }
  4. f(1, 2, 3) // 1
  5. f(1) // 1
  6. f() // undefined
  7. f.length // 2
  8. function f(a, b) {
  9. return a;
  10. }
  11. f( , 1) // SyntaxError: Unexpected token ,(…)
  12. f(undefined, 1) // undefined

值传递和引用传递

  1. var p = 2;
  2. function f(p) {
  3. p = 3;
  4. }
  5. f(p);
  6. p // 2
  7. var obj = { p: 1 };
  8. function f(o) {
  9. o.p = 2;
  10. }
  11. f(obj);
  12. obj.p // 2

同名参数

如果有同名的参数,则取最后出现的那个值

  1. function f(a, a) {
  2. console.log(a);
  3. }
  4. f(1, 2) // 2

arguments

  • 由于 JavaScript 允许函数有不定数目的参数,所以需要一种机制,可以在函数体内部读取所有参数
  • 严格模式下,arguments对象是一个只读对象,修改它是无效的,但不会报错
  1. var f = function(a,b,c){
  2. 'use strict';
  3. arguments[0] = 3;
  4. return [(a+b+c),arguments.length];
  5. }
  6. console.log(f(1,2,3)); //6,3
  • 虽然arguments很像数组,但它是一个对象。数组专有的方法(比如slice和forEach),不能在arguments对象上直接使用 ,变成数组:var args = Array.prototype.slice.call(arguments);
  • arguments对象带有一个callee属性,返回它所对应的原函数
  1. var f = function () {
  2. console.log(arguments.callee === f);
  3. }
  4. f() // true

闭包

通过函数访问函数内部变量

  1. function a(x){
  2. var arr = 1;
  3. function b(){
  4. console.log(arr);
  5. }
  6. return {
  7. fb : b,
  8. brr : arr
  9. }
  10. }
  11. var brr = a();
  12. brr.fb(); //1
  13. console.log(brr.brr); //1

闭包作用域是函数内部,内存不释放

  1. function a(x){
  2. return function(){
  3. return x++;
  4. };
  5. }
  6. var arr = a(5);
  7. console.log(arr()); //5
  8. console.log(arr()); //6
  9. console.log(arr()); //7

闭包-构造函数

  1. function a(){
  2. var age;
  3. function getAge(){
  4. return age;
  5. }
  6. function setAge(a){
  7. age = a;
  8. }
  9. return {
  10. getage:getAge,
  11. setage:setAge
  12. }
  13. }
  14. var b = a();
  15. b.setage(5);
  16. console.log(b.getage()); //5

立即调用函数

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

eval的本质是在当前作用域之中,注入代码。由于安全风险和不利于 JavaScript 引擎优化执行速度,所以一般不推荐使用。通常情况下,eval最常见的场合是解析 JSON 数据的字符串,不过正确的做法应该是使用原生的JSON.parse方法

数组

介绍

  • 任何类型的数据,都可以放入数组
  1. var arr = [
  2. 1,
  3. 2,
  4. 'abc',
  5. {p:1},
  6. function(){
  7. console.log(this.p);
  8. }
  9. ]
  • 数组属于一种特殊的对象,键名是按次序排列的一组整数
  1. typeof [1, 2, 3] //"object"
  2. var arr = ['a', 'b', 'c'];
  3. Object.keys(arr)
  4. //["0", "1", "2"]
  5. arr['0'] // 'a'
  6. arr[0] // 'a'
  7. a[1.00] = 6;
  8. a[1] // 6
  9. arr.0 // SyntaxError
  • length

length属性是可写的。如果人为设置一个小于当前成员个数的值,该数组的成员会自动减少到length设置的值

  1. var arr = [ 'a', 'b', 'c' ];
  2. arr.length // 3
  3. arr.length = 2;
  4. arr // ["a", "b"]
  5. arr.length = 0;
  6. arr // []

如果人为设置length大于当前元素个数,则数组的成员数量会增加到这个值,新增的位置都是空位
var a = ['a']; a.length = 3; a[1] // undefined

由于数组本质上是一种对象,所以可以为数组添加属性,但是这不影响length属性的值

var a = []; a['p'] = 'abc'; a.length // 0 a[2.1] = 'abc'; a.length // 0

in

  1. var arr = [ 'a', 'b', 'c' ];
  2. 2 in arr // true
  3. '2' in arr // true
  4. 4 in arr // false
  5. var arr = [];
  6. arr[100] = 'a';
  7. 100 in arr // true
  8. 1 in arr // false

数组遍历

不要使用for in,它会把非数字键也遍历出来

for

  1. for(var i = 0; i < a.length; i++) {
  2. console.log(a[i]);
  3. }

while

  1. var i = 0;
  2. while (i < a.length) {
  3. console.log(a[i]);
  4. i++;
  5. }

foreach

  1. var colors = ['red', 'green', 'blue'];
  2. colors.forEach(function (color) {
  3. console.log(color);
  4. });
  5. // red
  6. // green
  7. // blue

数组空位

  • 当数组的某个位置是空元素,即两个逗号之间没有任何值,我们称该数组存在空位

var a = [1, , 1]; a.length // 3

  • 如果最后一个元素后面有逗号,并不会产生空位

var a = [1, 2, 3,]; a.length // 3 a // [1, 2, 3]

  • 数组的空位是可以读取的,返回undefined

var a = [, , ,]; a[1] // undefined

  • 遍历空位都会被跳过,是undefined遍历的时候就不会被跳过
  1. var a = [, , ,];
  2. a.forEach(function (x, i) {
  3. console.log(i + '. ' + x);
  4. })
  5. // 不产生任何输出
  6. var a = [undefined, undefined, undefined];
  7. a.forEach(function (x, i) {
  8. console.log(i + '. ' + x);
  9. });
  10. // 0. undefined
  11. // 1. undefined
  12. // 2. undefined

类似数组的对象

典型的“类似数组的对象”是函数的arguments对象,以及大多数 DOM 元素集,还有字符串

  1. // arguments对象
  2. function args() { return arguments }
  3. var arrayLike = args('a', 'b');
  4. arrayLike[0] // 'a'
  5. arrayLike.length // 2
  6. arrayLike instanceof Array // false
  7. // DOM元素集
  8. var elts = document.getElementsByTagName('h3');
  9. elts.length // 3
  10. elts instanceof Array // false
  11. // 字符串
  12. 'abc'[1] // 'b'
  13. 'abc'.length // 3
  14. 'abc' instanceof Array // false
  1. //通过call(),可以把forEach()嫁接到string上面调用
  2. Array.prototype.forEach.call('abc', function (chr) {
  3. console.log(chr);
  4. });
  5. // a
  6. // b
  7. // c
  8. //数组的slice方法可以将“类似数组的对象”变成真正的数组
  9. var arr = Array.prototype.slice.call('abc');
  10. arr.forEach(function (chr) {
  11. console.log(chr);
  12. });
  13. // a
  14. // b
  15. // c

嫁接方法比直接使用数组原生的forEach要慢,所以最好还是先将“类似数组的对象”转为真正的数组,然后再直接调用数组的forEach方法