《你不知道的JavaScript》第三章笔记
语法
JavaScript
中对象的定义方式有 2 种,声明或构造的 2 种方式。
- 对象的文字形式
1
let a = { name: "micromatrix" };
- 对象的构造形式
1
2let obj = new Object();
obj.name = "micromatrix";
类型
JavaScript
中的简单基本类型分别为:
- string
- number
- boolean
- undefined
- null
- object
简单基本类型不是对象,但是平常使用中能用"1234".length
这种语法是由于JavaScript
中自动把基本类型转化成了内置类型。
内置类型分别为:
- String
- Number
- Boolean
- Array
- Function
- Object
- Date
上面说的只是一部分,其实随着 ES6 等后续的 JS 规范更新,像BigInt
和Symbol
都被加入了。这些内置类型实际上就是一些内置函数,它们可以被当作构造函数使用。
1 | var strPrimitive = "I am a string"; |
值得注意的是由于内置类型和基础类型感觉就像首字母大写了一样,但是其中 null 和 undefined 没有对应的构造形式,它们只有文字形式。相反,Date 只有构造,没有文字形式。
内容
对象的内容是有一些存储在特定命名位置的值组成,也就是属性。但是注意的是在 JS 引擎内部中,这些值的存储位置多种多样的,一般不会存在对象容器内部,存储在对象内部的这个属性名称,他们就像指针一样,指向这些值真正的存储位置。
访问对象属性的值有 2 种方式,使用.
或者[]
操作符
1 | var myObject = { |
.a
被称为”属性访问”,而[a]
被称为”键访问”,实际上他们访问的使用同一个位置。
在对象中,属性名永远都是字符串。即使使用数字作为属性名,它首先会被转换为一个字符串。
可计算属性名
ES6 增加了可计算属性名,可以在文字形式中使用 [] 包裹一个表达式来当作属性名
1 | var prefix = "foo"; |
属性和方法
访问的对象属性是一个函数,有的人的叫法是方法,因为在类中通常被称为方法。
作者认为对象内部引用的函数被称为”方法”这不太合适,因为 JS 中函数永远不会属于一个对象。这主要是由于有些函数具有this
引用,有时候这些 this 确实是指向调用位置的对象引用。但是 this 是在运行的时候根据调用位置动态绑定的,所以函数和对象的关系最多是间接关系。
数组
数组是对象,也支持[]
。不过由于数组有一套更加结构化的存储机制。[]
访问的是下标值,并且下标值是整数。
你也可以给数组添加属性,但是这并不会改变数组的 length。
可以把数组当作一个普通的键 / 值对象来使用,并且不添加任何数值索引,但是这并不是一个好主意。数组和普通的对象都根据其对应的行为和用途进行了优化,所以最好只用对象来存储键 / 值对,只用数组来存储数值下标 / 值对。
复制对象
- 深拷贝
- 浅拷贝
这 2 种拷贝的最大区别在于,浅拷贝对基础值复制一遍,但是对内置类型的值仅仅是复制了对象属性上的引用。而深拷贝要复制引用对象上的值。
注意的是如果你在深拷贝仅仅考虑复制所有的值的话,可能会遇到循环引用的对象复制,这样的话会使得程序进入死循环。
而巧妙的复制方法:
1 | var newObj = JSON.parse(JSON.stringfy(someObj)); |
浅拷贝: object.assign(...)
第一个参数是目标对象,之后还可以跟多个源对象。
属性描述符
从 ES5 开始,所有的属性具有了属性描述符。
1 | var myObject = { |
可以使用Object.defineProperty
来创建普通属性并且设置属性描述符的值
1 | var myObject = {}; |
不变性
如果想让属性或者对象是不可改变的,有多种方法可以实现。注意的是,所有的方法创建的都是浅不变性。也就是只会影响目标对象和它的直接属性,如果目标对象引用了其他对象(数组、函数等),其他对象的内容不受影响。
对象常量
使用属性描述符种的writable:false
和configurable:false
创建一个不可以修改,定义和删除的属性。禁止拓展
如果静止一个对象新增属性,并且保留已有属性的话。可以使用Object.preventExtensions(...)
。1
2
3
4
5
6var myObject = {
a: 2,
};
Object.preventExtensions(myObject);
myObject.b = 3;
muObject.b; // undefined 。非严格模式下静默失败,严格模式下提示Type Error密封
Object.seal(..)
会创建一个“密封”的对象,这个方法实际上会在一个现有对象上调用Object.preventExtensions(..)
并把所有现有属性标记configurable:false
。冻结
Object.freeze(..)
会创建一个冻结对象,这个方法实际上会在一个现有对象上调用 Object.seal(..) 并把所有“数据访问”属性标记为 writable:false,这样就无法修改它们的值。它是最高级别的不可变性运用,但是这个对象引用的其他对象则不受印象Getter 和 Setter
[[Get]]和[[Put]]可以控制属性值的设置和获取。
对象内置的[[Get]]操作首先在对象中查找是否有名称相同的属性,如果找到就会返回这个属性的值。存在性
1
2
3var obj={a:2};
console.log('a' in obj) ;//true
console.log(obj.hasOwnProperty('a')//true这里主要的是
in
操作符会检查属性是否存在对象及其[[Prototype]]原型链中,而hasOwnProperty
则只会检查属性是否在对象中,不会检查[[Prototype]]原型链。Object.propertyIsEnumberable(...)
会检查给定的属性是否直接存在于对象中;Object.keys(...)
会返回一个数组,包含所有可枚举属性;Object.getOwnPropertyNames(...)
会返回一个数组,包含所有属性,无论它们是否可枚举。