JAVASCRIPT面向对象(创建对象)第二章

  • 创建对象
    • 工厂模式
    • 构造函数模式
      • 构造函数与普通函数的区别
      • 构造函数存在的弊端

创建对象

虽然用Object构造函数或者字面量创建对象,但是会产生大量重复性代码,而且代码不宜维护。

工厂模式

因为ECMAScript无法创建类,用工厂模式是可以实现创建对象的细节,也可以看成封装对象

function createAnimal(name,age){
   var o = new Object();
   o.name = name;
   o.age = age;
   o.bark = function(){
      console.info(this.name);
   }
   return o;
}

var dog1 = createAnimal("Small Yellow",5);
var dog2 = createAnimal("Big Yellow",25);

注意:虽然工厂模式可以创建对象,但是无法解决对象识别问题。

构造函数模式

像Object和Array这样原生的构造函数,在运行时(浏览器内核或者Node环境中)出现在执行环境中。此外,可以创建自定义的构造函数,用构造函数的方式重新实现上面的例子

function Animal(name,age){
   this.name = name;
   this.age = age;
   this.bark = function(){
      console.info(this.name);
   }
}

var dog1 = Animal("Small Yellow",5);
var dog2 = Animal("Big Yellow",25);

这个例子中Animal取代了工厂模式的createAnimal函数,但是这种原生支持的函数会与工厂模式有三种不同之处,如下:

  • 使用this来调用属性和方法
  • 没有return语句
  • 没有创建对象(new Object)

借鉴与OO(面向对象)语言,函数名首字母约定为大写,当然小写不会报错,但不建议这样做。创建对象也是和OO语言保持一致,通过new关键字,那么在使用new来调用构造函数,会有下面四个step:

  • 创建一个新对象
  • 将构造函数的作用域给新对象,this会指向这个新对象
  • 执行构造函数中的代码
  • 返回新对象

那么此时,由同个函数生成的对象,他们的类型保持一致。


console.info((dog1.constructor == dog2.constructor) && (dog1.constructor == Animal) && (dog2.constructor == Animal));//true

如果去检测对象类型的话,还是instanceof靠谱些

console.info(dog1 instanceof Object);//true
console.info(dog2 instanceof Object);//true
console.info(dog1 instanceof Animal);//true
console.info(dog2 instanceof Animal);//true

注意:dog1和dog2同时也是Object的子类,这个原理是原型继承,第三章节会详细给出答案

构造函数与普通函数的区别

其实构造函数与普通函数没有任何区别,只是调用方式的不同。也就是说任何的函数都可以通过new来调用,但这样做可能会失去业务场景的意义,所以该如何调用,完全取决于您是否想去规划对象的创建和如何组织整个工程的代码。那么具体区别会在下面的例子中一一展现出

//函数通过new来进行调用
var dog = new Animal("Small Yellow",5);
dog.bark();//"Small Yellow"

//当作普通函数调用,就是直接调用
Animal("Small Yellow",5);//添加到window
window.bark(); //"Small Yellow"

//在其它对象的作用域调用
var o = new Object();
Animal.call(o,"Small Yellow",5);//o通过call会持有Animal的运行环境,同时也可以调用apply方法,只是参数不一致
o.sayName(); //"Small Yellow"

构造函数存在的弊端

讲到这里,不得不提前巩固个知识,所有的函数都是Function的实例。当创建对象时,通过new来调构造函数,每调用一次,Animal内部代码都会执行一次,所以这里的this.bark就不是同一个函数了。因此,不同的实例上同名的函数引用是不同的。

console.info(dog1.bark == dog2.bark);//false

然而遇到这样的问题,我们可以通过把函数写在外部,就解决了这么尴尬的问题,但是不要高兴太早。它任然存在弊端,虽然共享的bark是同一个引用,但是这样在全局作用域上定义方法,这样会造成很多的全局函数,以后很难维护,更别谈封装了。如何解决这种问题,那么下面会讲到原型模式

function Animal(name,age){
   this.name = name;
   this.age = age;
   this.bark = bark;
}

function bark (){//函数声明升级,编译器会先执行声明,所以这里不会报错
   console.info(this.name);
}

var dog1 = new Animal("Small Yellow",5);
var dog2 = new Animal("Big Yellow",25);

你可能感兴趣的:(javascript,es5,前端开发)