你的浏览器不支持canvas

Love You Ten Thousand Years

JavaScript的数据结构和数据类型

Date: Author: M/J

本文章采用 知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议 进行许可。转载请注明来自小可嗒嗒的博客

关于数据类型和数据结构的概念,可以参考Wiki百科

JavaScript中,对象是语言的中心,连functions也被视为对象。

JavaScript有两种基本的数据类型: primitive type and reference type。两者都是通过对象进行访问的。原始类型保存为简单的数据值,引用类型则保存为对象(objects),其本质是指向内存位置的引用

其它的编程语言用栈(stack)来储存原始类型,用堆(heap)来储存引用对象。JavaScript则完全不同:它使用一个variable object来追踪变量的生存周期,原始类型的值被直接保存在变量对象中,而引用类型的值作为一个指针保存在变量对象中,该指针指向实际对象在内存中的存储位置1

Data types

The lastest ECMAScript standard defines seven(七种) data types.

Six data types that are primitives

原始数据类型指的是按照原样保存一些简单的数据,比如true25。所以的原始类型都可以使用字面量(literal)来代表它们的值,而字面量表示不被存放在变量中的值。

A primitive(基元) is data that is not an object and has no methods.

  • Boolean. true and false.
  • null. A special keyword denoting a null value. And null is not the same as Null, NULL ,or any other variant for JavaScript is case-sensitive.
  • undefined. A top-level property whose value is undefined.
  • Number. 42 or 3.14159.
  • String.
  • Symbol (new in ECMAScript 2015). A data type whose instances are unique and immutable.

鉴别原始类型

鉴别原始类型的最佳方法是使用typeof操作符。但null类型和function类型可能需要注意:

console.log(typeof null);//'object'
console.log(typeof function a(){/*...*/} === "function" ); //true

你可以使用复合条件来鉴别null类型或者直接与null比较。

var a = null;
(!a && typeof a === "Object"); //true

// or
console.log((a === null)); //true

这被委员会TC39认定为一个错误。

原始方法(原始变量,原始值)

原始类型(stringnumberbool有方法,nullundefined没有)也拥有方法,但它们并不是对象,JavaScript只是使它们看上去像对象一样而已。

var name = 'Nicholas';
var lowercaseName = name.toLowerCase();

变量是没有类型的,只有值才有

javaScript中,变量是没有类型的,只有值才有。所以变量可以持有任何类型的值。

var  a = 42;
typeof a; //"number"

a = true;
typeof a; //"boolean"

基本数据类型的封装

为了方便我们使用一些通用的方法,JavaScript对基本的三个数据类型进行了封装。

  • String()
  • Number()
  • Boolean()
  • Symbol()ES6

这些内建函数也被称为原生函数

当然,我们常见的内建函数还包括:

  • Array()
  • Object()
  • Function()
  • RegExp()
  • Date()
  • Error()

由于它们的设计上的一致性,我们会统一来描述。

原生函数可以被当作构造函数来使用,所以创建的是封装对象,是对象。

var a = new String("abc");
console.log(typeof a); //"object"
console.log(a instanceof String); //true

所有typeof返回值为object的对象都包含一个内部属性[[Class]],这个属性无法直接访问,一般通过Object.prototype.toString()来查看。

Object.prototype.toString.call([1,2,3]); //"[object Array]"
Object.prototype.toString.call(/regex-literal/i); //"[object RegExp]"
Object.prototype.toString.call("abc"); //"[object String]"

Object.prototype.toString.call(null); //"[object Null]"
Object.prototype.toString.call(undefined); //"[object Undefined]"

虽然Null()Undefined()这样的原生构造函数不存在,但内部[[Class]]属性值仍然是NullUndefined

由于基本类型值没有.length.toString()这样的属性和方法,所以JavaScript会自动为基本类型值进行包装,详见本页面的原始数据封装


Reference type

引用类型JavaScipt中的对象(objects),引用值是引用类型的实例(instances),和对象是同义词。

对象是属性(properties)的无序列表,属性包含键/值。如果一个属性的值是函数(function),就被成为方法(method)。

我们可以通过使用new操作符和构造函数来创建对象。

var object1 = new Object();
var object2 = object1;

更多有关创建对象的操作在这里:Object

上述代码实例化了一个通用对象,并把它的引用保存在object1中。因为引用类型不在变量中直接保存对象,所以object1变量实际上并不包含对象的实例,而是一个指向内存中实际对象所在位置的指针(或者说引用),所以将object1的值赋值给object2变量,使得两个变量各获得一份指针的拷贝,指向内存中同一对象。这是对象与原始值之间的一个基本差别。

Dereferencing Objects

虽然JavaScriptgarbage-colletced language,但是最好在不使用对象时将引用解除。

var object1 = new Object();
//do something
object1 = null;

内建类型

  • Array - 数组类型
  • Dage - 日期和时间类型
  • Error - 运行期错误类型
  • Function - 函数类型
  • Object - 通用对象类型
  • RegExp - 正则表达式类型

引用类型也有字面量形式

//Object literal
var book = {"name": "The Kate Runner",
			"year": 2014};

//Array literal
var colors = ["red","bule","green"];

//Function literal
function reflect(value){
	return value;
}

//Regular Expression literal
var number = /\d+/g;

我们基本上都是使用字面量来定义函数

鉴别引用类型

函数最容易鉴别的引用类型,因为对函数使用typeof操作符时,返回值是function

对于其他的引用类型,使用typeof操作符都返回object。我们选择使用instanceof操作符。

var items = [];
var object = {};
function reflect(value){
	return value;
}
console.log(items instanceof Array); //true
console.log(object instanceof Object); //true
cosole.log(reflect instanceof Function); //true

instanceof操作符同样可以鉴别继承类型。然而,所有的引用类型都继承自Object,所以console.log(reflect instanceof Object)返回true

ECMAScript 5引入了Array.isArray()来鉴别一个值是否为Array的实例,当然,这是最佳的方法。

var items = [];
console.log(Array.isArray(items)); //true

Primitive Wrapper Types – 原始封装类型

原始封装类型有三种(StringNumberBoolean),使得原始类型用起来像对象一样方便。原始封装类型时引用类型,当读取StringNumberBoolean时,原始封装类型会自动创建

var name = "Nicholas";
var firstChar = name.charAt(0); 
console.log(firstChar);	//"N"

//在背后实际是这样的
var name = "Nicholas";
var temp = new String(name);
var fistChar = temp.charAt(0);
temp = null;
console.log(fistChar);	//"N"

为了能让字符串在charAt(0)工作,创建了一个临时对象,这个对象仅仅用于该语句,并在随后被销毁(这个过程也称之为autoboxing)。

看以下例子:

var name = "Nicholas";
name.last = "Zakas";
console.log(name.last);	//undefined

//在操作的背后实际上是这样的
var name = "Nicholas";
var temp = new String(name);
temp.last = "Zakas";
temp = null;

var temp = new String(name);
console.log(temp.last);	//undefined
temp = null;

所以说,添加的新的属性是挂载在临时对象上的,试图访问该属性时,另一个不同的临时对象被创建,而新属性并不存在。


详解

Number – 数字

根据语言规范,JavaScript采用IEEE 754 标准定义的双精度64位格式来表示数字,所以JavaScript不区分整数值和浮点数值。

小心NaN(Not a Number)。

parseInt ("hello", 10); // 函数返回NaN
NaN + 5//返回NaN

使用内置函数isNaN()可以判断一个变量是否为NaNisNaN(NaN);//true

JavaScript还有两个特殊值:Infinity(正无穷) 和 -Infinity(负无穷)

可以使用内置函数isFinite()来判断一个变量是否是一个有穷数,如果类型是Infinity-InfinityNaN则返回false

String – 字符串

JavaScript中的字符串是一串Unicode字符序列。JavaScript字符串是不可更改的。这意味着字符串一旦被创建,就不能被修改。但是,可以基于对原始字符串的操作来创建新的字符串。

布尔类型

False values – 假值

  • false
  • 0
  • "" 空字符串
  • NaN
  • null
  • undefined

其他类型

JavaScriptnullundefined是不同的。前者表示一个空值non-value,必须使用null关键字才能访问,后者是undefined(未定义)类型的对象,表示一个未初始化的值,也就是还没有被分配的值。

  • null: 指空值,或者说曾赋过值,但目前没有值
  • undefined: 指没有值,或者说从未赋值

null是一个特殊关键字,不是标识符,不能将其当作变量来使用和赋值。相反,undefined却是一个标识符,可以被当作变量来使用和赋值。

JavaScriptundeclaredundefined也是不同的。undeclared是指还没有在作用域内声明过的变量。如:

var a;
a; //undefined
b; //ReferenceError: b is not defined.

解释器解释成is not defined并不是undefined的意思,而是指undeclared,令人抓狂。

JavaScript允许声明变量但不对其赋值,一个未被赋值的变量就是undefined类型。还有一点需要说明的是,undefined实际上是一个不允许修改的常量。

Data type conversion

JavaScript is a dynamically typed language. That means you don’t have to specify the data type of a variable when you declare it.

In expressions involving numeric and string values with the + operator, JavaScript converts numeric values to strings.

x = "The answer is" + 42; //"The answer is 42"

In statements involving other operators, JavaScript does not convert numeric values to strings.

"37" - 7 //30
"37" + 7 //377

Functions parseInt() and parseFloat() is used for converting strings to numbers.

Literals

Literals are fixed values, not variables, that you literally provide in your script.

Array literals

An array literal is a list of zero or more expressions, each of which represents an array element, enclosed in square brackets ([]).

Note: An array literal is a type of object initializer.

Extra commas in array literals

	var fish = ["Lion", , "Angel"]; //fish[1] is undefined

is the same as:

	var fish = ["Lion", undefined,"Angel"];

If you include a trailling(尾随) comma at the end of elements, the comma is ignored.

	var myList = ['home', , 'school',];

The length of the array is three. And trailing commas can create errors in older browser versions.

Boolean literals

The Boolean type has two literal values: true and false.

Integers

  • Decimal integer literal consists of a sequence of digits without a leading 0 (zero).
  • Leading 0 (zero) on an integer literal, or leading 0o (or 0O) indicates it is in octal. Octal integers can include only the digits 0-7.
  • Leading 0x (or 0X) indicates hexadecimal. Hexadecimal integers can include digits (0-9) and the letters a-f and A-F.
  • Leading 0b (or 0B) indicates binary. Binary integers can include digits only 0 and 1.

Floating-point literals

A floating-point literal can have the following parts:

  • A decimal integer which can be signed (preceded by + or -),
  • A decimal point (.),
  • A fraction (another decimal number),
  • An exponent.

Object literals

An object literal is a list of zero or more pairs of property names and associated values of an object, enclosed in curly braces {}.

You should not use an object literal at the beginning of a statement. This will lead to an error or not behave as you expect.

	var sales = "Toyota";
	function carTypes(name) { ... }
	
	var car = {myCar: "Saturn", getCar: carType("Honda"), special: sales};

The first element of the car object defines a property myCar, and assigns to ti a new string value "Saturn". It’s called pairs of (property/value). the second element, the getCar property, is immediately assigned the result of invoking the function carTypes("Honda"); the third element, the special property, uses an existing variable sales.

Additionally, you can use a numeric or string literal for the name of a property or nest an object inside another.

	var car = { manyCars: {a: "Saab", "b": "Jeep"}, 7: "Mazda" };
	console.log(car.manyCars.b); //Jeep

Object property names can be any string, including the empty string.

If the property name would not be a valid JavaScript identifier or number, it must be enclosed in quotes. Property names that are not valid identifiers also cannot be accessed as a dot . property, but can be accessed and set with the array-like notation [].

	var unusualPropertyNames = {
	"!": "Bang!"
	}
	console.log(unusualPropertyNames.!); // SyntaxError: Unexpected token !
	console.log(unusualPropertyNames["!"]); // Bang!

RegExp literals

A regex literal is a pattern enclosed between slashes.

	var re = /ab+c/;

String literals

A string literal is zero or more characters enclosed in double " or single ' quotation marks.

You can call any of the methods of the String object on a string literal value : JavaScript automatically converts the string literal to a temporary String object, calls the method, then discards the temporary String object.

console.log("John's cat".length);

Using special characters in strings

  • \n
  • \t
  • \b

Escaping characters

\ is used as a Escaping characters(转义字符).

编辑备注:

  • 2017-01-18第一次编辑
  • 2017-03-21第二次编辑

对于本文内容有问题或建议的小伙伴,欢迎在文章底部留言交流讨论。