# 数据类型-对象类型
# Object
# 对象的生成
对象(object)是 JavaScript 语言的核心概念,也是最重要的数据类型。
var obj = { foo: "Hello", bar: "World" };
该对象内部包含两个键值对(又称为两个“成员”),第一个键值对是 foo: 'Hello',第二个键值对是 bar: 'World',两个键值对之间用逗号分隔。
“键名”都是字符串,如果键名是数值,会被自动转为字符串。
“键值”可以是任何数据类型。如果一个属性的值为函数,通常把这个属性称为“方法”,它可以像函数那样调用。
# 对象的引用
如果不同的变量名指向同一个对象,那么它们都是这个对象的引用,也就是说指向同一个内存地址。修改其中一个变量,会影响到其他所有变量。
var o1 = {}; var o2 = o1; o1.a = 1; o2.a; // 1 o2.b = 2; o1.b; // 2
# 对象的属性
读取对象的属性,有两种方法,一种是使用点运算符,还有一种是使用方括号运算符。
var p = "bar"; var obj = { p: "Hello World" }; console.log(obj.p); // "Hello World" 点号后面p为对象的属性 console.log(obj["p"]); // "Hello World" []里加引号p为对象的属性
点运算符和方括号运算符,不仅可以用来读取值,还可以用来赋值。
var obj = {}; obj.foo = "Hello"; obj["bar"] = "World";
看一个对象本身的所有属性,可以使用 Object.keys 方法
var obj = { key1: 1, key2: 2 }; console.log(Object.keys(obj)); // ['key1', 'key2']
JavaScript 允许属性的“后绑定”,也就是说,你可以在任意时刻新增属性,没必要在定义对象的时候,就定义好属性。
var obj = {}; obj.foo = 123; obj.foo; // 123
delete 命令 用于删除对象的属性,删除成功后返回 true。
var obj = {}; delete obj.p // true // 注意,删除一个不存在的属性,delete不报错,而且返回true。
属性是否存在:
in 运算符用于检查对象是否包含某个属性,如果包含就返回 true,否则返回 false。
in 运算符 (属性 in 对象); var obj = { p: 1 }; console.log('p' in obj);// true obj对象中有p console.log('toString ' in obj); //false
for...in 循环 用来遍历一个对象的全部属性。
var obj = { a: 1, b: 2, c: 3 }; for (var k in obj) { console.log("键名:", i); console.log("键值:", obj[k]); }
with 语句 操作同一个对象的多个属性时,提供一些书写的方便。
with (对象) { 语句; }
建议不要使用 with 语句,可以考虑用一个临时变量代替 with
# 对象的方法
- obj.valueOf() 返回值为该对象的原始值。
- obj.toString() 方法返回一个表示该对象的字符串。
# Array
对象允许存储键值集合,这很好。
但很多时候我们发现还需要 有序集合,里面的元素都是按顺序排列的。
这时一个特殊的数据结构数组(Array)就派上用场了,它能存储有序的集合。
# 声明
let arr = new Array();
// 绝大多数情况下使用的都是第二种语法
let arr = [];
# 元素
数组(array)是按次序排列的一组值。每个值的位置都有编号(从 0 开始),整个数组用方括号表示。 任何类型的数据,都可以放入数组。
var arr = [
{ a: 1 },
[1, 2, 3],
function () {
return true;
}
];
arr[0]; // Object {a: 1}
arr[1]; // [1, 2, 3]
arr[2]; // function (){return true;}
如果数组的元素还是数组,就形成了多维数组。(一般使用不超过三维,超过三维用其他结构体)
var a = [
[1, 2],
[3, 4]
];
a[0][1]; // 2
a[1][1]; // 4
# 长度
数组的
length
属性,返回数组的成员数量。 只要是数组,就一定有length
属性。该属性是一个动态的值,等于键名中的最大整数加上1
。数组的数字键不需要连续,
length
属性的值总是比最大的那个整数键大1
。 也表明数组是一种动态的数据结构,可以随时增减数组的成员。清空数组的一个有效方法,就是将
length
属性设为 0。 当然也可以直接 var arr =[];
var arr = ["a", "b"];
arr.length; // 2
arr[2] = "c";
arr.length; // 3
arr[9] = "d";
arr.length; // 10
# 队列
队列(queue)是最常见的使用数组的方法之一。在计算机科学中,这表示支持两个操作的一个有序元素的集合
- pop 取出并返回数组的最后一个元素
- push 在数组末端添加元素
- shift 取出数组的第一个元素并返回它
- unshift 在数组的首端添加元素
# 数组的本质
本质上,数组属于一种特殊的对象。typeof
运算符会返回数组的类型是object
。 可以看到数组的键名就是整数 0、1、2...。
typeof [1, 2, 3]; // "object"
var arr = ["a", "b", "c"];
Object.keys(arr);
// ["0", "1", "2"]
但是数组成员只能用方括号arr[0]
表示(方括号是运算符,可以接受数值)。
记住,在 JavaScript 中只有 8 种基本的数据类型。数组是一个对象,因此其行为也像一个对象。
数组真正特殊的是它们的内部实现。JavaScript 引擎尝试把这些元素一个接一个地存储在连续的内存区域,就像本章的插图显示的一样,而且还有一些其它的优化,以使数组运行得非常快。
# 探究数组的性能
push/pop 方法运行的比较快,而 shift/unshift 比较慢。
为什么作用于数组的末端会比首端快呢?让我们看看在执行期间都发生了什么:
shift 操作必须做三件事:
- 移除索引为 0 的元素。
- 把所有的元素向左移动,把索引 1 改成 0,2 改成 1 以此类推,对其重新编号。
- 更新 length 属性。
数组里的元素越多,移动它们就要花越多的时间,也就意味着越多的内存操作
# Function
# 函数的声明
function 命令
function print(s) {
console.log(s);
}
函数表达式
var print = function x() {
console.log(typeof x);
};
console.log(x); // ReferenceError: x is not defined
print(); // function
注意:若在 function 后面加上函数名,该函数名只在该函数体内部有效
注意
- JavaScript 语言将函数看作一种值,与其它值(数值、字符串、布尔值等等)地位相同。 由于函数与其他数据类型地位平等,所以在 JavaScript 语言中又称函数为第一等公民。
- JavaScript 引擎将函数名视同变量名,所以采用
function
命令声明函数时,整个函数会像变量声明一样,被提升到代码头部。
# 函数的属性和方法
- 函数的
name
属性返回函数的名字。 - 函数的
length
属性返回函数的形参个数 - 函数的
toString
方法返回一个字符串,内容是函数的源码。
# 函数的作用域
- 在 ES5 的规范中,JavaScript 只有两种作用域:一种是全局作用域,变量在整个程序中一直存在,所有地方都可以读取;另一种是函数作用域,变量只在函数内部存在。
var
命令声明的变量,不管在什么位置,变量声明都会被提升到函数体的头部。- 函数的作用域与变量一样,就是其声明时所在的作用域,与其运行时所在的作用域无关。