循序渐进了解对象的几种创建方式和内部特性

我心飞翔 分类:javascript

一、基本对象的创建

① 使用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: "小车"
//}
 

我们可以看到和工厂模式的效果是一样的,对比工厂模式的写法,我们看到的区别:

  1. 没有创建对象的步骤
  2. 赋值是赋给this
  3. 没有return对象
  4. 调用方法的区别

除了写法上的区别,还有一个重要的区别,那就是构造函数模式能体现出实例的类型,下面我们使用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方法都是同一个方法。

回复

我来回复
  • 暂无回复内容