数据类型
JavaScript 中有 8 种数据类型,分别是:
简单基本类型
string字符串类型number数字类型boolean布尔值类型undefined未定义类型null空类型bigint范围更大的整型值symbol独一无二的值
引用数据类型
Object对象类型
其中 7 种简单基本类型的变量直接存储值,而应用数据类型存储地址,对象的值存储于堆中。
可以使用 tyeof 来判断变量的数据类型,其中 typeof null 输出为 Object 是因为在 JavaScript 中二进制前三位为 0 则会被判断为 Object 类型,而 null 的二进制全是 0 ,所以这是一个 JavaScript 的 bug 。
简单基本类型是值类型,不具有属性,但是我们依然可以使用 'abc'.length ,这是因为进行了封箱和拆箱的处理,在执行这句代码时会自动将 'abc' 进行封箱操作,自动生成一个 new String('abc') 对象,并调用打印其 length 属性,执行完毕则进行拆箱,回到字面量 'abc' 。而 null 和 undefined 没有对应的内置对象,所以无法进行封箱操作,也无法调用任何方法或属性。
对象的定义
对象可以通过两种方式定义: 声明形式和构造形式
// 声明形式
var obj1 = {
name: 'knight1',
age: 18
};
// 构造形式
var obj2 = new Object();
obj2.name = 'knight2';
obj2.age = 19;
对象是引用类型,变量存储对象的引用,而对象的属性如果也是对象,则存储的也是对象的引用
属性描述符
上面这个例子向我们展示了通过 Object.getOwnPropertyDescriptor() 来获取属性的属性描述符,并通过 Object.defineProperty() 来设置属性描述符
属性描述符分为两类,
- 数据描述符:
{
configurable: true,
enumerable: true,
value: "knight2",
writable: true
}
- 访问描述符:
{
configurable: true,
enumerable: true,
get(){console.log('get')},
set(){console.log('set')}
configurable
设置属性是否是可配置,若为 false 无法通过 Object.defineProperty() 修改属性描述符,默认为 true
注意: 若我们将 configurable 设置为 false ,而 writable 为 true 时,我们还可以调用一次 Object.defineProperty() 用于将 wirtable 修改为 false
enumberable
是否可被枚举,若为 false 则属性不会出现在对象的属性枚举种,如 for of 中,默认为 true
value
属性的值
writable
决定是否可以修改属性的值,若为 false 则属性只可被读取而无法被写入
get/set
重写属性的 get 和 set 操作,若只定义其中一个,则另一个什么都不执行,如只定义 get ,则无法修改属性值
原型
如图所示,其中 person 和 Person.protype 是对象,而 Person 为函数,我们通过 Person 构造函数实例化 person 对象,然后会将 Person() 的 prototype 对象作为 person 的原型,在浏览器中国,我们可以通过 person.__proto__ 属性来访问这个原型对象,而 Personprototype.constructor 指向 Person() 函数。
原型链
刚才已经说了, person 和 Person.protype 是对象,所以 Person.protype 也有 __proto__ 属性,然后如果我们依次向上寻找原型的话,大部分时候都会找到 Object.prototype 对象,而这个对象的原型指向 null 。
当我们访问 person 中的属性时,若该属性在 person 中没有,则会向原型中寻找,然后依次向上寻找,如果找到原型的尽头依然找不到的话,就会返回 undefined 。
而如果我们找到了则会返回该属性,并停止寻找,这也会造成属性的屏蔽,就是原型链下端的属性会屏蔽上端的属性。
如果属性不在对象中,而在对象的原型中则会出现下面三种情况
- 如果原型中存在该属性,且违背标记为只读,则会在对象中添加一个同名属性,他就是屏蔽属性
- 如果原型中存在该属性,但被标记为只读,则无法被修改,也不会在对象中添加一个同名属性
- 如果原型中存在该属性,但是是一个
setter,则会调用该setter