一、基本对象的创建
① 使用Object构造函数,创建之后添加属性
let obj = new Object;
obj.a = 1;
obj.b = 2;
obj.fn = function(){
console.log(this.a,this.b);
}
② 对象字面量,创建同时定义属性
let obj = {
a: 1,
b: 2,
fn:function(){
console.log(this.a,this.b);
}
}
二、对象的内部特性
每个对象都有一些内部特性来描述自己属性的特征,分别是数据属性和访问器属性,这些内部特性我们不能直接访问到,但我们可以使用Object.getOwnPropertyDescriptor(对象,对象的指定属性)获取。
let obj = {
a: 1,
}
console.log(Object.getOwnPropertyDescriptor(obj,'a'));
//输出结果
configurable: true
enumerable: true
value: 1
writable: true
在没有定义访问器属性时,我们可以看到对象属性的四个数据属性:
- configurable:是否可以通过delete删除和能否重新定义,默认为true
- enumerable:是否可通过for in循环和Object.keys获取属性名数组,默认为true
- value:属性名对应的值
- writable:属性名对应的值能否被修改,默认为true 接下来我们给对象的属性定义访问器属性:
let obj = {
a: 1,
}
Object.defineProperty(obj,'a',{
get:function(){},
set:function(){}
})
console.log(Object.getOwnPropertyDescriptor(obj,'a'));
//输出结果
configurable: true
enumerable: true
get: ƒ ()
set: ƒ ()
在通过Object.defineProperty定义访问器属性之后,writable和value不见了,取而代之的是get和set。
- configurable:同数据属性
- enumerable:同数据属性
- get:在获取属性时调用
- set: 在设置属性时调用
三、对象的创建模式
使用Object和对象字面量可以快速的创建一个自己想要的对象,但是要批量创建具有相同属性的对象时就不是很方便了,因为要编写大量的重复代码,这时候肯定会有批量创建我们想要的对象的方式,以下是满足这种场景下的对象创建方式。
一、工厂模式
function createCar(varieties,color){
let obj = new Object;
obj.varieties = varieties;
obj.color = color;
return obj;
}
let car1 = createCar('大车','蓝色');
let car2 = createCar('小车','红色');
console.log(car1);
//{
// color: "蓝色",
// varieties: "大车"
//}
console.log(car2);
//{
// color: "红色",
// varieties: "小车"
//}
声明一个函数createCar,在createCar内部使用Object创建一个对象obj,然后在obj上添加传入的参数作为属性,最后返回obj对象,赋值给新的变量名。虽然工厂模式可以解决批量创建类似属性对象的问题,但是它不能很好的体现对象的类型。
二、构造函数模式
除了Object、Array之类的原生构造函数,我们也可以自定义构造函数,下面我们把工厂模式的例子改写成构造函数模式
function Car(varieties,color){
this.varieties = varieties;
this.color = color;
}
let car1 = new Car('大车','蓝色');
let car2 = new Car('小车','红色');
console.log(car1);
//{
// color: "蓝色",
// varieties: "大车"
//}
console.log(car2);
//{
// color: "红色",
// varieties: "小车"
//}
我们可以看到和工厂模式的效果是一样的,对比工厂模式的写法,我们看到的区别:
- 没有创建对象的步骤
- 赋值是赋给this
- 没有return对象
- 调用方法的区别 除了写法上的区别,还有一个重要的区别,那就是构造函数模式能体现出实例的类型,下面我们使用instanceof判断类型:
console.log(car1 instanceof Car);
//true
console.log(car2 instanceof Car);
//true
可以看到,car1和car2都是Car的实例。
构造函数模式可以创建对象,也可以体现对象的类型,但它也不是完全没有问题,我们看下面这个例子:
function Car(varieties,color){
this.varieties = varieties;
this.color = color;
this.whistle = function(){
console.log('嘟嘟~');
}
}
let car1 = new Car('大车','蓝色');
let car2 = new Car('小车','红色');
console.log(car1.whistle == car2.whistle);
//false
可以看到,car1和car2上都有一个名为whistle的函数实例,但是各自的whistle函数实例却不是同一个函数实例,这是因为构造函数模式创建实例时,会把定义的方法在每个实例上都创建一遍,每个实例上的方法都是独立的。如果创建出的实例很多,那么占用的内存也会很多,这种情况也不是没有办法解决,我们看下面这个例子:
function whistle(){
console.log('嘟嘟~');
}
function Car(varieties,color){
this.varieties = varieties;
this.color = color;
this.whistle = whistle;
}
let car1 = new Car('大车','蓝色');
let car2 = new Car('小车','红色');
console.log(car1.whistle == car2.whistle);
//true
上面例子我们把whistle函数实例定义在构造函数外部,可以看到car1和car2各自的whistle函数实例是同一个函数实例,这样是解决了这个问题,但是又有另一个问题,那就是实例需要的方法很多时,那么就要在全局作用域中也要定义很多方法,这样非常不方便代码的集中,这个问题也有办法解决,我们看下一个对象创建模式。
三、构造函数原型模式
只要创建一个函数,这个函数就会有一个名为prototype的属性,这个属性是一个对象,当函数被作为构造函数调用时(函数和构造函数的唯一区别是调用方式不同),prototype上的属性和方法就是所有实例的公用方法和公用属性。
function Car(varieties,color){
this.varieties = varieties;
this.color = color;
}
Car.prototype.whistle = function(){
console.log('嘟嘟~');
}
let car1 = new Car('大车','蓝色');
let car2 = new Car('小车','红色');
car1.whistle();
//嘟嘟~
car2.whistle();
//嘟嘟~
console.log(car1.whistle == car2.whistle);
//true
可以看到,把whistle方法添加在Car.prototype上,通过Car创建的实例就都拥有了whistle方法,且各个实例的whistle方法都是同一个方法。