简言
再次回顾理解JavaScript中的对象概念。
js对象
对象概念
对象是一个包含相关数据和方法的集合(通常由一些变量和函数组成,我们称之为对象里面的属性和方法)。对象包含三个要素:
- 唯一标识: 这个对象的标记,通过它可以找到和理解这个对象;js是靠内存地址表示对象的唯一标识。
- 状态: 对象的各种描述性属性。
- 行为:对象的行为。
创建js对象
js对象创建有多种方式:
- {} 、new
- Object.create()
我们先来看使用上面三种创建的对象示例:
// 1. {}
const a = {}
console.log(a);
// 2. new
const b = new Object()
console.log(b);
const c = new function () {
}
console.log(c);
// 3. Object.create()
const d = Object.create(null)
console.log(d);
结果都是一个空对象,只是他们的原型对象里的构造函数不同
那是因为a和b是基于Object创建的,c基于Function,d基于null。
它们都可以添加自己的属性(property),属性是用键值对(key/value)来描述的,key可以是字符串、number和Symbol类型,value则可以是任意类型。
const a = {
name: 'zsk6',
'a': 123,
6: {
name: 'lala',
age: 18
},
fn: () => {
console.log('我是fn');
}
}
使用js对象
使用对象一般就是调用里面的属性,有以下两种方式:
- 点(.)操作符
- 中括号[]操作符
示例:
另外,你可以添加或修改属性,自身的普通属性通过上面的方式可以修改和添加(不存在就是添加,存在就修改)。
原型链
每个对象都有一个原型对象,属于不可以的内置对象,用[[Prototype]]表示,js的对象创建不可能凭空出现,他需要基于其他对象来创建,类似面向对象的继承,但它不是复制其他对象的属性,而是使用原型对象来和其他对象建立联系,你可以使用浏览器提供的__proto__和官方提供的Object.getPrototypeOf()来访问对象的[[Prototype]]。
const a = {
name: 'zsk6',
'a': 123,
6: {
name: 'lala',
age: 18
},
fn: () => {
console.log('我是fn');
}
}
const aa = Object.create(a)
aa.name = 'aa'
console.log(aa.__proto__, Object.getPrototypeOf(aa));
原型链 ----当你创建的对象继承层级足够多的时候,访问本身不存在的属性时,js会依靠[[Prototype]]来尝试读取原型上是否有改属性,若没用继续依靠原型的[[Prototype]]尝试读取是否有改属性,直至找到顶层null,若找不到则认为是未定义(undefined),这种链式查找方式称为原型链。
如果是对象有构造函数,那么构造函数会有一个prototype属性可以访问[[Prototype]]。
const A = function (name, age) {
this.name = name
this.age = age
}
const AA = new A('zsk', 18, 22)
// AA实例对象 A构造函数
console.log('AA实例对象 A构造函数', AA, A);
// AA.__proto__ 指向原型对象 A.prototype也只原型对象 ,AA 和 A 是不同的观察角度,就像两个人打游戏,我抢A包点,你守A包点,最终都会去A包点,
console.log(AA.__proto__, A.prototype, A.prototype === AA.__proto__);
// AA.__proto__.__proto__ 指向A的原型对象(Object) ,
console.log(AA.__proto__.__proto__, A.prototype.__proto__, Object.prototype, Object.__proto__.__proto__);
// 顶层对象null
console.log(AA.__proto__.__proto__.__proto__, A.prototype.__proto__.__proto__, Object.prototype.__proto__, Object.__proto__.__proto__.__proto__);
// 我们给A原型添加属性,
A.prototype.hh = '嘿嘿'
console.log('我们给A原型添加属性,', AA.hh);
// 我们给Object原型添加属性aa
Object.prototype.aa = '哈哈'
console.log('我们给Object原型添加属性aa', AA.aa);
效果:
查找路径:
AA---->AA.proto(构造函数A)---->Object---->null
找得到就调用,找不到undefined。
内置对象Object
万物皆对象。js内置了很多的特殊对象。例如Object、Array、Math、Function、Date、Number、String等等。
这里只了解Object。
Object 是 JavaScript 的一种 数据类型 。它用于存储各种键值集合和更复杂的实体。Objects 可以通过 Object() 构造函数或者使用 对象字面量 的方式创建。
Object的属性
上文我们知道对象是用属性来定义行为和状态的。
官方使用描述符对象来描述属性,对象里目前存在的属性描述符有两种主要形式:数据描述符和存取描述符。数据描述符是一个具有值的属性,该值可以是可写的,也可以是不可写的。存取描述符是由 getter 函数和 setter 函数所描述的属性。一个描述符只能是这两者其中之一;不能同时是两者。
描述符键值描述:
键值 | 描述 |
---|---|
configurable | 当且仅当该属性的 configurable 键值为 true 时,该属性的描述符才能够被改变,同时该属性也能从对应的对象上被删除。 默认为 false。configurable 特性表示对象的属性是否可以被删除,以及除 value 和 writable 特性外的其他特性是否可以被修改。 |
enumerable | 当且仅当该属性的 enumerable 键值为 true 时,该属性才会出现在对象的枚举属性中。 默认为 false。定义了对象的属性是否可以在 for…in 循环和 Object.keys() 中被枚举。 |
value | 该属性对应的值。可以是任何有效的 JavaScript 值(数值,对象,函数等)。 默认为 undefined |
writable | 当且仅当该属性的 writable 键值为 true 时,属性的值,也就是上面的 value,才能被赋值运算符 (en-US)改变。 默认为 false。 |
get | 属性的 getter 函数,如果没有 getter,则为 undefined。当访问该属性时,会调用此函数。执行时不传入任何参数,但是会传入 this 对象(由于继承关系,这里的this并不一定是定义该属性的对象)。该函数的返回值会被用作属性的值。 默认为 undefined。 |
set | 属性的 setter 函数,如果没有 setter,则为 undefined。当属性值被修改时,会调用此函数。该方法接受一个参数(也就是被赋予的新值),会传入赋值时的 this 对象。 默认为 undefined。 |
描述符可拥有的键值:
configurable | enumerable | value | writable | get | set | |
---|---|---|---|---|---|---|
数据描述符 | 可 | 可 | 可 | 可 | 不 | 不 |
存取描述符 | 可 | 可 | 不 | 不 | 可 | 可 |
- 拥有布尔值的键 configurable、enumerable 和 writable 的默认值都是 false。
- 属性值和函数的键 value、get 和 set 字段的默认值为 undefined。
如果一个描述符不具有 value、writable、get 和 set 中的任意一个键,那么它将被认为是一个数据描述符。如果一个描述符同时拥有 value 或 writable 和 get 或 set 键,则会产生一个异常。
示例:
const obj = {};
// 定义描述符 , Object.create()原型指向null,意思是不受其他影响,纯净的{}
const descriptor = Object.create(null); // 没有继承的属性
// 默认没有 enumerable,没有 configurable,没有 writable
descriptor.value = 'static';
descriptor.configurable = true // 描述符对象可变
Object.defineProperty(obj, 'key', descriptor);
// 显式
Object.defineProperty(obj, "key", {
enumerable: true, // 可遍历
configurable: true, //
writable: false,
value: "static"
});
obj.key = '123'
console.log(obj.key);
// 报错,Cannot redefine property: key 不能重新定义key值
Object.defineProperty(obj, "key", {
enumerable: true,
configurable: false,
writable: false,
value: "static2", // 重新设描述符值
});
// 没屁用,writable为false,不可写
obj.key = 'zsk'
console.log(obj.key);
如果对象中不存在指定的属性,Object.defineProperty() 会创建这个属性。当描述符中省略某些字段时,这些字段将使用它们的默认值。
get和set使用
function Archiver() {
var temperature = null;
var archive = [];
Object.defineProperty(this, 'temperature', {
get: function() {
console.log('get!');
return temperature;
},
set: function(value) {
temperature = value;
archive.push({ val: temperature });
}
});
this.getArchive = function() { return archive; };
}
var arc = new Archiver();
arc.temperature; // 'get!'
arc.temperature = 11;
arc.temperature = 13;
arc.getArchive(); // [{ val: 11 }, { val: 13 }]
当访问者的属性是被继承的,get和set存储访问的值需要使用this,才不会被实例对象共享
function myclass() {
}
Object.defineProperty(myclass.prototype, "x", {
get() {
return this.stored_x;
},
set(x) {
this.stored_x = x;
}
});
var a = new myclass();
var b = new myclass();
a.x = 1;
console.log(b.x); // undefined
结语
结束了。