JavaScript(二)数据类型

一、null

null是只有一个值得数据类型,这个特殊值是null
null值表示一个空对象指针,如果保存对象的变量还没有真正的保存对象,就应该明确的让该变量保存null值
null与undefined都可以表示“没有”,含义非常相似。将一个变量赋值为undefined或null,在if语句中,它们都会被自动转为false,相等运算符(==)甚至直接报告两者相等。

if (!undefined) {
  console.log('undefined is false');
}
// undefined is false

if (!null) {
  console.log('null is false');
}
// null is false

undefined == null
// true

Number(undefined) // NaN
5 + undefined // NaN

二、undefind

1、定义一个变量,但是没有初始化,会得到undefined
2、变量未定义,会得到undefined
3、函数中return不带任何返回值时,函数停止执行会返回undefined
4、函数参数arguments,没有传递值的命名参数将自动被赋予undefined
5、对象没有赋值的属性var o = new Object(); o.p // undefined

三、布尔值

该类型有2个字面量值true和false,经常用在流程控制语句和选择判断语句,
常见false值,除了false值都基本都是true
转换规则是除了下面六个值被转为false,其他值都视为true
1.数字0、
2.NaN、
3.“ ”,空字符串
4.false
5.undefined
6.null
下列运算符会返回布尔值:

  • 前置逻辑运算符: ! (Not)
  • 相等运算符:===,!==,==,!=
  • 比较运算符:>,>=,<,<=
    注意:空数组([])和空对象({})对应的布尔值,都是true
if ([]) {
  console.log('true');
}
// true

if ({}) {
  console.log('true');
}
// true

四、数值

  • 1、浮点数:所谓浮点数,就是该数值中必须包含一个小数点,且小数点后面至少有一位数字,由于保存浮点数的内存空间是保存整数的2倍,因此,如果小数点后面没有任何数字,或者本身就是一个整数(1.0),那么该值会被转化成整数,但是浮点数计算会产生四舍五入误差的问题。

  • 2、数值精度:精度最多只能到53个二进制位,这意味着,绝对值小于2的53次方的整数,即-253到253,都可以精确表示。

  • 3、数值范围:则 JavaScript 能够表示的数值范围为21024到2-1023(开区间),超出这个范围的数无法表示。这时就会返回Infinity Math.pow(2, 1024) // Infinity

  • 4、数值的表示法:小数点前的数字多于21位 或者 小数点后的零多于5个,JavaScript 会自动将数值转为科学计数法表示,其他情况都采用字面形式直接表示

  • 5、特殊数值:
    正零和负零:几乎所有场合,正零和负零都会被当作正常的0
    (1 / +0) === (1 / -0) // false 除以正零得到+Infinity,除以负零得到-Infinity,这两者是不相等的
    NaN:NaN是 JavaScript 的特殊值,表示“非数字”(Not a Number),主要出现在将字符串解析成数字出错的场合
    NaN不是独立的数据类型,而是一个特殊数值,它的数据类型依然属于Number typeof NaN // 'number'
    NaN不等于任何值,包括它本身。NaN === NaN // false
    NaN与任何数(包括它自己)的运算,得到的都是NaN
    Infinity:一个正的数值太大,或一个负的数值太小,或者另一种是非0数值除以0,得到Infinity
    Infinity有正负之分,Infinity表示正的无穷,-Infinity表示负的无穷
    由于数值正向溢出(overflow)、负向溢出(underflow)和被0除,JavaScript 都不报错,所以单纯的数学运算几乎没有可能抛出错误
    Infinity大于一切数值(除了NaN),-Infinity小于一切数值(除了NaN)
    Infinity与NaN比较,总是返回false

  • 6、与数值相关的全局方法
    parseInt():将字符串转为整数。

// parseInt方法用于将字符串转为整数。
parseInt('123') // 123
// 如果字符串头部有空格,空格会被自动去除。
parseInt('   81') // 81
// 如果parseInt的参数不是字符串,则会先转为字符串再转换。
parseInt(1.23) // 1 // 等同于 parseInt('1.23') // 1
// 字符串转为整数的时候,是一个个字符依次转换,如果遇到不能转为数字的字符,就不再进行下去,返回已经转好的部分
parseInt('15e2') // 15
// 如果字符串的第一个字符不能转化为数字(后面跟着数字的正负号除外),返回NaN
parseInt('abc') // NaN
parseInt('.3') // NaN
// parseInt的返回值只有两种可能,要么是一个十进制整数,要么是NaN
// parseInt方法还可以接受第二个参数(2到36之间),表示被解析的值的进制,默认情况下,parseInt的第二个参数为10,即默认是十进制转十进制
parseInt('1000') // 1000
// 等同于
parseInt('1000', 10) // 1000
parseInt('1000', 2) // 8

parseFloat:将一个字符串转为浮点数

// 如果字符串符合科学计数法,则会进行相应的转换。
parseFloat('314e-2') // 3.14
// 如果字符串包含不能转为浮点数的字符,则不再进行往后转换,返回已经转好的部分。
parseFloat('3.14more non-digit characters') // 3.14
// parseFloat方法会自动过滤字符串前导的空格。
parseFloat('\t\v\r12.34\n ') // 12.34
// 如果参数不是字符串,或者字符串的第一个字符不能转化为浮点数,则返回NaN。
parseFloat([]) // NaN
parseFloat('FF2') // NaN
parseFloat('') // NaN

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

// isNaN只对数值有效,如果传入其他值,会被先转成数值。比如,传入字符串的时候,字符串会被先转成NaN,所以最后返回true,这一点要特别引起注意。也就是说,isNaN为true的值,有可能不是NaN,而是一个字符串。
isNaN('Hello') // true
// 相当于
isNaN(Number('Hello')) // true
// 使用isNaN之前,最好判断一下数据类型。
function myIsNaN(value) {
  return typeof value === 'number' && isNaN(value);
}
// 判断NaN更可靠的方法是,利用NaN为唯一不等于自身的值的这个特点,进行判断。
function myIsNaN(value) {
  return value !== value;
}

isFinite():isFinite方法返回一个布尔值,表示某个值是否为正常的数值。

// 除了Infinity、-Infinity、NaN和undefined这几个值会返回false,isFinite对于其他的数值都会返回true
isFinite(Infinity) // false
isFinite(-Infinity) // false
isFinite(NaN) // false
isFinite(undefined) // false
isFinite(null) // true
isFinite(-1) // true

五、字符串

字符串就是零个或多个排在一起的字符,放在单引号或双引号之中

'key = "value"'
"It's a long journey"
// 如果要在单引号字符串的内部,使用单引号,就必须在内部的单引号前面加上反斜杠,用来转义。双引号字符串内部使用双引号,也是如此
'Did she say \'Hello\'?'
// 字符串默认只能写在一行内,分成多行将会报错。
'a
b
c'
// SyntaxError: Unexpected token ILLEGAL
// 连接运算符(+)可以连接多个单行字符串,将长字符串拆成多行书写,输出的时候也是单行
var longString = 'Long '
  + 'long '
  + 'long '
  + 'string';

字符串可以被视为字符数组,因此可以使用数组的方括号运算符,用来返回某个位置的字符(位置编号从0开始)

var s = 'hello';
s[0] // "h"
s[1] // "e"
s[4] // "o"

// 直接对字符串使用方括号运算符
'hello'[1] // "e"
// 如果方括号中的数字超过字符串的长度,或者方括号中根本不是数字,则返回undefined。
'abc'[3] // undefined
'abc'[-1] // undefined
'abc'['x'] // undefined
// 字符串内部的单个字符无法改变和增删,这些操作会默默地失败
var s = 'hello';
delete s[0];
s // "hello"
s[1] = 'a';
s // "hello"

length属性返回字符串的长度,该属性也是无法改变的。

var s = 'hello';
s.length // 5

s.length = 3;
s.length // 5

六、对象

对象就是一组“键值对”(key-value)的集合,是一种无序的复合数据集合。
键名与键值之间用冒号分隔。
两个键值对之间用逗号分隔。
键名:对象的所有键名都是字符串(ES6 又引入了 Symbol 值也可以作为键名),所以加不加引号都可以。

// 如果键名不符合标识名的条件(比如第一个字符为数字,或者含有空格或运算符),且也不是数字,则必须加上引号,否则会报错
// 报错
var obj = {
  1p: 'Hello World'
};

// 不报错
var obj = {
  '1p': 'Hello World',
  'h w': 'Hello World',
  'p+q': 'Hello World'
};

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

var obj = {
  p: function (x) {
    return 2 * x;
  }
};

obj.p(1) // 2

如果属性的值还是一个对象,就形成了链式引用。

var o1 = {};
var o2 = { bar: 'hello' };

o1.foo = o2;
o1.foo.bar // "hello"

属性可以动态创建,不必在对象声明时就指定。

var obj = {};
obj.foo = 123;
obj.foo // 123

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

var o1 = {};
var o2 = o1;

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

o2.b = 2;
o1.b // 2
// 这种引用只局限于对象,如果两个变量指向同一个原始类型的值。那么,变量这时都是值的拷贝。
var x = 1;
var y = x;

x = 2;
y // 1

对象属性操作

读取:有两种方法,一种是使用点运算符,还有一种是使用方括号运算符

var foo = 'bar';

var obj = {
  foo: 1,
  bar: 2
};

obj.foo  // 1
obj[foo]  // 2
// 引用对象obj的foo属性时,如果使用点运算符,foo就是字符串;如果使用方括号运算符,但是不使用引号,那么foo就是一个变量,指向字符串bar
// 数字键可以不加引号,因为会自动转成字符串
var obj = {
  0.7: 'Hello World'
};

obj['0.7'] // "Hello World"
obj[0.7] // "Hello World"

赋值:点运算符和方括号运算符,不仅可以用来读取值,还可以用来赋值

var obj = {};

obj.foo = 'Hello';
obj['bar'] = 'World';

查看 :查看一个对象本身的所有属性,可以使用Object.keys方法

var obj = {
  key1: 1,
  key2: 2
};

Object.keys(obj);
// ['key1', 'key2']

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

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

delete obj.p // true
obj.p // undefined
Object.keys(obj) // []

属性是否存在:in 运算符

// in运算符用于检查对象是否包含某个属性(注意,检查的是键名,不是键值),如果包含就返回true,否则返回false
var obj = { p: 1 };
'p' in obj // true
'toString' in obj // true

// in运算符的一个问题是,它不能识别哪些属性是对象自身的,哪些属性是继承的。
// 这时,可以使用对象的hasOwnProperty方法判断一下,是否为对象自身的属性
var obj = {};
if ('toString' in obj) {
  console.log(obj.hasOwnProperty('toString')) // false
}

属性的遍历:for...in 循环

// 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
// 它遍历的是对象所有可遍历(enumerable)的属性,会跳过不可遍历的属性。
// 它不仅遍历对象自身的属性,还遍历继承的属性。
// 如果只想遍历自身属性,应该结合使用hasOwnProperty方法
var person = { name: '老张' };

for (var key in person) {
  if (person.hasOwnProperty(key)) {
    console.log(key);
  }
}
// name

七、函数

7.1 函数的声明

1)function 命令

function 函数名(参数1,参数2) {
  // 函数体
}
// 调用 
函数名()

2)函数表达式

// 将一个匿名函数赋值给变量
var print = function(s) {
  console.log(s);
};
// 采用函数表达式声明函数时,function命令后面不带有函数名。如果加上函数名,该函数名只在函数体内部有效,在函数体外部无效
var print = function x(){
  console.log(typeof x);
};

x
// ReferenceError: x is not defined

print()
// function

3)Function 构造函数

//  Function构造函数接受三个参数,除了最后一个参数是add函数的“函数体”,其他参数都是add函数的参数。
var add = new Function(
  'x',
  'y',
  'return x + y'
);

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

7.2 函数的重复声明

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


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

function f() {
  console.log(2);
}
f() // 2

7.3 return 语句

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

7.4 函数名的提升

7.5 函数的属性和方法

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

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

如果变量的值是一个具名函数,那么name属性返回function关键字之后的那个函数名

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

name属性的一个用处,就是获取参数函数的名字。

var myFunc = function () {};

function test(f) {
  console.log(f.name);
}

test(myFunc) // myFunc

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

// 它的length属性就是定义时的参数个数。不管调用时输入了多少个参数,length属性始终等于2
function f(a, b) {}
f.length // 2

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

// 对于那些原生的函数,toString()方法返回function (){[native code]}。
function f() {
  a();
  b();
  c();
}

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

7.6 函数作用域

作用域(scope)指的是变量存在的范围。在 ES5 的规范中,JavaScript 只有两种作用域:一种是全局作用域,变量在整个程序中一直存在,所有地方都可以读取;另一种是函数作用域,变量只在函数内部存在。
对于顶层函数来说,函数外部声明的变量就是全局变量(global variable),它可以在函数内部读取。

var v = 1;

function f() {
  console.log(v);
}

f()
// 1

在函数内部定义的变量,外部无法读取,称为“局部变量”

function f(){
  var v = 1;
}

v // ReferenceError: v is not defined

函数内部定义的变量,会在该作用域内覆盖同名全局变量。

var v = 1;

function f(){
  var v = 2;
  console.log(v);
}

f() // 2
v // 1

注意,对于var命令来说,局部变量只能在函数内部声明,在其他区块中声明,一律都是全局变量

if (true) {
  var x = 5;
}
console.log(x);  // 5
函数内部的变量提升

与全局作用域一样,函数作用域内部也会产生“变量提升”现象。var命令声明的变量,不管在什么位置,变量声明都会被提升到函数体的头部。

function foo(x) {
  if (x > 100) {
    var tmp = x - 100;
  }
}

// 等同于
function foo(x) {
  var tmp;
  if (x > 100) {
    tmp = x - 100;
  };
}
函数本身的作用域

函数本身也是一个值,也有自己的作用域。它的作用域与变量一样,就是其声明时所在的作用域,与其运行时所在的作用域无关。

var a = 1;
var x = function () {
  console.log(a);
};

function f() {
  var a = 2;
  x();
}

f() // 1

数执行时所在的作用域,是定义时的作用域,而不是调用时所在的作用域
很容易犯错的一点是,如果函数A调用函数B,却没考虑到函数B不会引用函数A的内部变量。

var x = function () {
  console.log(a);
};

function y(f) {
  var a = 2;
  f();
}

y(x)
// ReferenceError: a is not defined

函数体内部声明的函数,作用域绑定函数体内部。

function foo() {
  var x = 1;
  function bar() {
    console.log(x);
  }
  return bar;
}

var x = 2;
var f = foo();
f() // 1

7.7 arguments 对象

函数运行的时候,有时需要提供外部数据,不同的外部数据会得到不同的结果,这种外部数据就叫参数。

function square(x) {
  return x * x;
}

square(2) // 4
square(3) // 9

函数参数如果是原始类型的值(数值、字符串、布尔值),传递方式是传值传递.这意味着,在函数体内修改参数值,不会影响到函数外部。

var p = 2;

function f(p) {
  p = 3;
}
f(p);

p // 2

如果函数参数是复合类型的值(数组、对象、其他函数),传递方式是传址传递(pass by reference)。也就是说,传入函数的原始值的地址,因此在函数内部修改参数,将会影响到原始值。

var obj = { p: 1 };

function f(o) {
  o.p = 2;
}
f(obj);

obj.p // 2

如果函数内部修改的,不是参数对象的某个属性,而是替换掉整个参数,这时不会影响到原始值。

var obj = [1, 2, 3];

function f(o) {
  o = [2, 3, 4];
}
f(obj);

obj // [1, 2, 3]

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

function f(a, a) {
  console.log(a);
}

f(1, 2) // 2

在函数体内部读取所有参数,就是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

正常模式下,arguments对象可以在运行时修改

var f = function(a, b) {
  arguments[0] = 3;
  arguments[1] = 2;
  return a + b;
}

f(1, 1) // 5

严格模式下,arguments对象与函数参数不具有联动关系。也就是说,修改arguments对象不会影响到实际的函数参数

var f = function(a, b) {
  'use strict'; // 开启严格模式
  arguments[0] = 3;
  arguments[1] = 2;
  return a + b;
}

f(1, 1) // 2

arguments对象的length属性,可以判断函数调用时到底带几个参数

function f() {
  return arguments.length;
}

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

需要注意的是,虽然arguments很像数组,但它是一个对象.
如果要让arguments对象使用数组方法,真正的解决方法是将arguments转为真正的数组。下面是两种常用的转换方法:slice方法和逐一填入新数组。

var args = Array.prototype.slice.call(arguments);

// 或者
var args = [];
for (var i = 0; i < arguments.length; i++) {
  args.push(arguments[i]);
}

arguments对象带有一个callee属性,返回它所对应的原函数。

var f = function () {
  console.log(arguments.callee === f);
}

f() // true

可以通过arguments.callee,达到调用函数自身的目的。这个属性在严格模式里面是禁用的,因此不建议使用。

八、数组

数组(array)是按次序排列的一组值。每个值的位置都有编号(从0开始),整个数组用方括号表示。var arr = ['a', 'b', 'c'];
定义时赋值,数组也可以先定义后赋值
任何类型的数据,都可以放入数组
如果数组的元素还是数组,就形成了多维数组。
本质上,数组属于一种特殊的对象。typeof运算符会返回数组的类型是object typeof [1, 2, 3] // "object"
Object.keys方法返回数组的所有键名。可以看到数组的键名就是整数0、1、2
数组的length属性,返回数组的成员数量。

更详细见后续数组详细章节。

本文参考:JavaScript 教程

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容

  • 强制转换 强制转换主要指使用Number、String和Boolean三个构造函数,手动将各种类型的值,转换成数字...
    灯火阑珊Zone阅读 390评论 0 3
  • 每天一句:每个月要读一本书,离开学校了,你还要经常保持阅读的习惯,大学时候,考前的通宵达旦,说明你具备快速学习的能...
    EndEvent阅读 421评论 0 0
  • FreeCodeCamp - Basic JavaScript 写在前面: 我曾经在进谷前刷过这一套题,不过当时只...
    付林恒阅读 16,428评论 5 28
  • 接口/抽象类意义规范、扩展、回调为其子类提供一个公共的类型 封装子类中得重复内容 定义抽象方法,子类虽然有不同的实...
    MigrationUK阅读 2,153评论 1 28
  • 哪月已圆,哪月缺。 谁家欢聚,谁家觉。 逢无滋味,分离伤! 散居各方,心念想! 多梦相逢,中秋节到,身影遥无期。 ...
    宁_5687阅读 210评论 1 3