• JS面向对象的几种设计模式


    工厂模式

    从代码中可以发现,工厂模式就是在函数中new一个对象,然后将参数赋值给对象,最终返回这个对象。
    可以看出调用createPerson解决了创建多个相似对象的问题,但是你可以试试 person1.constructor或是person2.constructor是无法找createPerson函数的。

    function createPerson(name, age, job){ 
     var o = new Object(); 
     o.name = name; 
     o.age = age; 
     o.job = job; 
     o.sayName = function(){ 
     alert(this.name); 
     }; 
     return o; 
    } 
    var person1 = createPerson("Nicholas", 29, "Software Engineer"); 
    var person2 = createPerson("Greg", 27, "Doctor"); 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    构造函数模式

    function Person(name, age, job){ 
     this.name = name; 
     this.age = age; 
     this.job = job; 
     this.sayName = function(){ 
     alert(this.name); 
     }; 
    } 
    var person1 = new Person("Nicholas", 29, "Software Engineer"); 
    var person2 = new Person("Greg", 27, "Doctor"); 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    由代码可以看出:

    • 没有显式地创建对象;
    • 直接将属性和方法赋给了 this 对象;
    • 没有 return 语句

    按照惯例,构造函数始终都应该以一个大写字母开头,而非构造函数则应该以一个小写字母开头。要创建 Person 的新实例,必须使用 new 操作符。以这种方式调用构造函数实际上会经历以下 4个步骤:

    (1) 创建一个新对象;
    (2) 将构造函数的作用域赋给新对象(因此 this 就指向了这个新对象);
    (3) 执行构造函数中的代码(为这个新对象添加属性);
    (4) 返回新对象。

    创建自定义的构造函数意味着将来可以将它的实例标识为一种特定的类型;而这正是构造函数模式
    胜过工厂模式的地方。

    alert(person1.constructor == Person); //true 
    alert(person2.constructor == Person); //true
    alert(person1 instanceof Object); //true 
    alert(person1 instanceof Person); //true 
    alert(person2 instanceof Object); //true 
    alert(person2 instanceof Person); //true
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    纯构造函数模式的不足之处

    构造函数模式虽然好用,但也并非没有缺点。使用构造函数的主要问题,就是每个方法都要在每个
    实例上重新创建一遍。这种方式创建函数,会导致不同的作用域链和标识符解析,但创建 Function 新实例的机制仍然是相同的

    alert(person1.sayName == person2.sayName); //false
    
    • 1

    原型模式

    首先我们需要理解原型对象:
    无论什么时候,只要创建了一个新函数,就会根据一组特定的规则为该函数创建一个 prototype属性,这个属性指向函数的原型对象。在默认情况下,所有原型对象都会自动获得一个 constructor(构造函数)属性,这个属性包含一个指向 prototype 属性所在函数的指针。就拿前面的例子来说,Person.prototype. constructor 指向 Person。而通过这个构造函数,我们还可继续为原型对象添加其他属性和方法。
    创建了自定义的构造函数之后,其原型对象默认只会取得 constructor 属性;至于其他方法,则都是从 Object 继承而来的。当调用构造函数创建一个新实例后,该实例的内部将包含一个指针(内部属性),指向构造函数的原型对象ECMA-262 第 5 版中管这个指针叫[[Prototype]]。虽然在脚本中没有标准的方式访问[[Prototype]],但 Firefox、Safari 和 Chrome 在每个对象上都支持一个属性__proto__;而在其他实现中,这个属性对脚本则是完全不可见的。不过,要明确的真正重要的一点就是,这个连接存在于实例与构造函数的原型对象之间,而不是存在于实例与构造函数之间。以前面使用 Person 构造函数和 Person.prototype 创建实例的代码为例,下图展示了各个对象之间的关系。
    在这里插入图片描述

    我们创建的每个函数都有一个 prototype(原型)属性,这个属性是一个指针,指向一个对象,
    而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。使用原型对象的好处是可以
    让所有对象实例共享它所包含的属性和方法。这里的 person1 和 person2 都是构造函数Person的实例。

    function Person(){ 
    } 
    Person.prototype.name = "Nicholas"; 
    Person.prototype.age = 29; 
    Person.prototype.job = "Software Engineer"; 
    Person.prototype.sayName = function(){ 
     alert(this.name); 
    }; 
    var person1 = new Person(); 
    person1.sayName(); //"Nicholas" 
    var person2 = new Person();
    person2.sayName(); //"Nicholas" 
    alert(person1.sayName == person2.sayName); //true
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    Person与person1、person2 的关系
    注意:
    构造函数Person.prototype上的属性最好不要设置成对象等应用类型(除function外),如下代码所示,所以说纯原型模式也不太靠谱。

    Person.prototype.temp={a:1,b:2};
    person1 .temp.a = 2;
    console.log(person2 .temp.a ); //2
    
    • 1
    • 2
    • 3

    一些关于原型的方法:

    1. hasOwnProperty()方法可以检测一个属性是存在于实例中,还是存在于原型中。
    2. 有两种方式使用 in 操作符:单独使用和在 for-in 循环中使用。在单独使用时,in 操作符会在通过对象能够访问给定属性时返回 true,无论该属性存在于实例中还是原型中。
    3. Object.keys()方法接收一个对象作为参数,返回一个包含所有可枚举属性的字符串数组。
    4. Object.getOwnPropertyNames()得到所有实例属性,无论它是否可枚举

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

    创建自定义类型的最常见方式,就是组合使用构造函数模式与原型模式。构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性。

    function Person(name, age, job){ 
     this.name = name; 
     this.age = age; 
     this.job = job; 
     this.friends = ["Shelby", "Court"]; 
    } 
    Person.prototype = { 
     constructor : Person, 
     sayName : function(){ 
     alert(this.name); 
     } 
    } 
    var person1 = new Person("Nicholas", 29, "Software Engineer"); 
    var person2 = new Person("Greg", 27, "Doctor"); 
    person1.friends.push("Van"); 
    alert(person1.friends); //"Shelby,Count,Van" 
    alert(person2.friends); //"Shelby,Count" 
    alert(person1.friends === person2.friends); //false 
    alert(person1.sayName === person2.sayName); //true 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    在这个例子中,实例属性都是在构造函数中定义的,而由所有实例共享的属性 constructor 和方法 sayName()则是在原型中定义的。而修改了 person1.friends(向其中添加一个新字符串),并不会影响到 person2.friends,因为它们分别引用了不同的数组。

    还有几种设计模式:动态原型模式,寄生构造函数模式,稳妥构造函数模式。感觉没怎么用到,就不详细说了,感兴趣可以去看《JavaScript高级程序设计(第3版》这本书,里面都有。

  • 相关阅读:
    数字IC设计全流程
    postman教程-13-关联接口调用
    具有多孔光纤的偏振分束器
    最优化建模、算法与理论(二)—— 典型优化问题
    二、uboot_源码分析
    ES6——ES6中对象、字符串、数组的扩展方法
    京东API接口(商品详情页采集+关键词搜索商品列表):开启电商业务的新篇章
    ssm基于Java和MySql的产业信息管理系统的设计与实现毕业设计源码260839
    第4讲 小程序首页实现
    小学生python游戏编程arcade----excel调用
  • 原文地址:https://blog.csdn.net/qq_22896159/article/details/126278291