JavaScript创建对象的几种模式

喜欢ヅ旅行 2022-06-18 00:27 320阅读 0赞

1. 工厂模式

其基本思想是:创建一个函数,在函数中创建一个对象,给对象增加属性和方法,最后返回该对象。下面是一个简单的工厂模式:

  1. function createPerson(name, age){ var obj = new Object(); //创建一个新对象 obj.name = name; //给对象增加属性和方法 obj.age = age; obj.show = function(){ alert("我的名字叫"+this.name+";性别:"+this.age); }; return obj; //返回该对象 } var person1 = createPerson("sean","male"); person1.show();
  2. 1
  3. 2
  4. 3
  5. 4
  6. 5
  7. 6
  8. 7
  9. 8
  10. 9
  11. 10
  12. 11
  13. 12
  14. 13
  15. 1
  16. 2
  17. 3
  18. 4
  19. 5
  20. 6
  21. 7
  22. 8
  23. 9
  24. 10
  25. 11
  26. 12
  27. 13

这样,函数createPerson()就能够接收对应的参数来创建对象,可以多次调用createPerson()函数,所以工厂模式能解决创建多个相似对象的问题。当然,也存在一些缺点:例如每个对象都有自己的方法,这样是很浪费资源的。

2. 构造函数模式

通过自定义构造函数来自定义对象类型的属性和方法。构造函数模式如下:

  1. function Person(name, age){ this.name = name; this.age = age; this.show = function(){ alert("我的名字叫"+this.name+";性别:"+this.age); }; } var person1 = new Person("sean","male"); person1.show();
  2. 1
  3. 2
  4. 3
  5. 4
  6. 5
  7. 6
  8. 7
  9. 8
  10. 9
  11. 10
  12. 11
  13. 1
  14. 2
  15. 3
  16. 4
  17. 5
  18. 6
  19. 7
  20. 8
  21. 9
  22. 10
  23. 11

我们可以发现,上面的Person()函数内没有创建一个新的对象,也没有返回对象,而是在函数外部通过new Person()来创建对象的实例。所以通过new Person();调用构造函数实际上需要经过一下步骤:

  • 创建一个新的对象;
  • 将this指向该对象;
  • 为这个新对象添加属性和方法;
  • 最后返回该对象
  • 上述代码运行之后,person1对象有一个constructor(构造函数)属性,该属性指向Person,person1是Person的一个实例。这样我们能知道person1的特定类型。
    另外,上述讲的构造函数和普通函数又有什么区别,其实通过观察我们可以发现构造函数和普通函数在形式上是没有差别的,真正的差别在于调用形式:任何函数,只要通过new操作符来调用,那么就可以当作构造函数,如果不通过new操作符调用,那就是普通函数通过为了区别构造函数和普通,在写法上也有区别,构造函数一般首字母都会大写)。

    //构造函数
    var person1 = new Person(“sean”,”male”);
    person1.show(); //sean
    //普通函数
    Person(“jack”,”male”);
    show(); //jack, 其实是window.show();

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 1
  8. 2
  9. 3
  10. 4
  11. 5
  12. 6

构造函数方法虽然简化了工厂模式,但是还是每一个实例上就有一个独立的方法。因为上面的show()方法其实是:

  1. this.show = new Function("alert('我的名字叫'+this.name+';性别:'+this.age);");
  2. 1
  3. 1

因此,在构造函数模式中,不同实例上的同名函数是不相等的。

3. 原型模式

为了节约内存,实现属性和方法的共享,引出了原型对象。原型对象的好处是可以让所有的对象实例共享它所包含的属性和方法。原型模式的写法如下:

  1. function Person(){
  2. }
  3. Person.prototype.name="sean";
  4. Person.prototype.sex="male";
  5. Person.prototype.friends=["jack","mary"];
  6. Person.prototype.show = function(){ alert("我是"+this.name);};
  7. var person1 = new Person();
  8. person1.show(); //sean
  9. var person2 = new Person();
  10. person2.name="Tom";
  11. person2.show(); //Tom
  12. alert(person2.show===person1.show); //true
  13. 1
  14. 2
  15. 3
  16. 4
  17. 5
  18. 6
  19. 7
  20. 8
  21. 9
  22. 10
  23. 11
  24. 12
  25. 13
  26. 14
  27. 15
  28. 16
  29. 17
  30. 1
  31. 2
  32. 3
  33. 4
  34. 5
  35. 6
  36. 7
  37. 8
  38. 9
  39. 10
  40. 11
  41. 12
  42. 13
  43. 14
  44. 15
  45. 16
  46. 17

由此可以看出,person1和person2的共享一个show方法,而不是像上面两种方法一样每个实例都有一个show方法。默认情况下,每个原型对象都会自动获得一个constructor(构造函数)属性,该属性指向prototype属性所在函数的指针。上面的构造函数属性Person.prototype.constructor指向Person。上面代码也可简写成如下:

  1. function Person(){
  2. }
  3. Person.prototype = {
  4. constructor: Person,
  5. name:"sean",
  6. sex:"male",
  7. friends:["jack","mary"],
  8. show : function(){ alert("我是"+this.name);},
  9. };
  10. var person1 = new Person();
  11. person1.friends.push("Lily"); //jack, mary, Lily
  12. alert(person1.friends);
  13. var person2 = new Person();
  14. alert(person2.friends); //jack, mary, Lily
  15. 1
  16. 2
  17. 3
  18. 4
  19. 5
  20. 6
  21. 7
  22. 8
  23. 9
  24. 10
  25. 11
  26. 12
  27. 13
  28. 14
  29. 15
  30. 16
  31. 17
  32. 18
  33. 1
  34. 2
  35. 3
  36. 4
  37. 5
  38. 6
  39. 7
  40. 8
  41. 9
  42. 10
  43. 11
  44. 12
  45. 13
  46. 14
  47. 15
  48. 16
  49. 17
  50. 18

通过上面的实例,我们可以发现,在创建新的实例对象时,所有的实例对象都或者相同的初始化属性值,这个可以通过设置实例中的值来隐藏原型中的属性值。但是原型模式最大的一个缺点是,如果属性中包含引用类型值的属性,只要改变了其中一个实例中的属性值,那么其他实例中的属性值也将被改变。就比如上述代码中person1的friends属性改变后,person2中的friends属性值也会被改变。
这是我们就需要将属性分开,哪些是实例属性,哪些是共享的属性,这样就出现了第四种创建对象的模式。

4、组合使用构造函数和原型模式

这种创建对象的思想是:使用构造函数定义实例属性,而使用原型模式定义方法和共享的属性,这样每个实例都有自己的一份实例属性的副本,但同时又共享着对方法的使用,最大限度的节省了内存。上面的代码可以改写成:

  1. function Person(name, sex, friends){
  2. this.name=name;
  3. this.sex=sex;
  4. this.friends=friends;
  5. }
  6. Person.prototype = {
  7. constructor: Person,
  8. show : function(){ alert("我是"+this.name);},
  9. };
  10. var person1 = new Person("sean", "male", ["jack", "mary"]);
  11. person1.friends.push("Lily"); //jack, mary, Lily
  12. alert(person1.friends);
  13. var person2 = new Person("leo", "male", ["john", "Lucy"]);
  14. alert(person2.friends); //john, Lucy
  15. alert(person1.friends===person2.friends); //false
  16. alert(person2.show===person1.show); //true
  17. 1
  18. 2
  19. 3
  20. 4
  21. 5
  22. 6
  23. 7
  24. 8
  25. 9
  26. 10
  27. 11
  28. 12
  29. 13
  30. 14
  31. 15
  32. 16
  33. 17
  34. 18
  35. 19
  36. 20
  37. 1
  38. 2
  39. 3
  40. 4
  41. 5
  42. 6
  43. 7
  44. 8
  45. 9
  46. 10
  47. 11
  48. 12
  49. 13
  50. 14
  51. 15
  52. 16
  53. 17
  54. 18
  55. 19
  56. 20

上述代码中,属于实例的属性都被定义在构造函数中,而所有实例共享的属性以及方法定义在原型中。这样,即使修改了person1中的friends属性,person2中的friends属性并没有改变。这种设计模式是目前使用最广泛,认可度最高的一种自定义类型的方式。

5、动态原型模式

动态原型模式设计思想是:将所有的信息都放在构造函数中,在满足一定的条件时动态添加原型。这种方法保证了构造函数和原型的优点。

  1. function Person(name, sex, friends){
  2. this.name=name;
  3. this.sex=sex;
  4. this.friends=friends;
  5. if (typeof this.show !="function") {
  6. alert("创建原型方法");
  7. Person.prototype.show = function(){
  8. alert("我是"+this.name);
  9. };
  10. }
  11. }
  12. var person1 = new Person("sean", "male", ["jack", "mary"]);
  13. person1.show(); //弹出"创建原型方法"和"我是sean"
  14. var person2 = new Person("leo", "male", ["Tom"]);
  15. person2.show(); //只弹出"我是Tom"
  16. 1
  17. 2
  18. 3
  19. 4
  20. 5
  21. 6
  22. 7
  23. 8
  24. 9
  25. 10
  26. 11
  27. 12
  28. 13
  29. 14
  30. 15
  31. 16
  32. 17
  33. 18
  34. 1
  35. 2
  36. 3
  37. 4
  38. 5
  39. 6
  40. 7
  41. 8
  42. 9
  43. 10
  44. 11
  45. 12
  46. 13
  47. 14
  48. 15
  49. 16
  50. 17
  51. 18

由于第一次创建person1实例时,没有show函数,因此将show方法添加到原型中。只要show方法添加到原型中,立马能共享到其他的实例中,因此在创建person2时,将不再添加show方法到原型中。

发表评论

表情:
评论列表 (有 0 条评论,320人围观)

还没有评论,来说两句吧...

相关阅读