详解 javascript对象创建模式


Posted in Javascript onOctober 30, 2020

创建模式

在javascript中,主要有以下几种创建模式:

工厂模式
构造函数模式
原型模式
组合模式
动态原型模式
寄生构造函数模式
稳妥构造模式

工厂模式

工厂模式是软件工程领域一种广为人知的设计模式。javascript实现方式:

function createPerson(name, obj, job) {
    var o = new Object();
    o.name = name;
    o.obj = obj;
    o.job = job;
    o.sayName = function() {
      alert(this.name);
    }
    return o;
  }
  
  var person1 = createPerson("Nicholas", 29, "software Enginner");
  var person2 = createPerson("Greg", 27, "Doctor");

工厂模式虽然解决了创建多个相似对象的问题,但却没有解决对象识别问题

构造函数模式

function Person(name, age, job) {
    this.name = name;
    this.age = name;
    this.job = name;
    this.sayName = function () {
      alert(this.name);
    }
  }
  
  var person1 = new Person("Nicholas", 29, "Software Engineer");
  var person2 = new Person("Greg", 27, "Doctor");
  
  person1 instanceof Person; // true
  person1 instanceof Object; // true
  
  person2 instanceof Person; // true
  person2 instanceof Object; // true

new操作符实现原理请查看文章附录

不同于工厂模式,构造函数模式

没有显示创建对象

直接将属性和方法赋值给了this对象

没有return语句

解决了对象识别问题

但是构造函数模式同样存在问题,就是每个方法都要在每个实例上重新申明一遍。person1和person2都有一个名为 sayName() 的方法,但那两个方法不是同一个Function实例。(在javascript中,函数实质上也是对象,因此每定义一个函数,也就是实例化一个对象。)

通过吧函数定义转移到构造函数外部可以解决这个问题:

function Person(name, age, job) {
    this.name = name;
    this.age = age;
    this.job = job;
    this.sayName = sayName;
  }
  function sayName() {
    alert(this.name);
  }
  var person1 = new Person("Nicholas", 29, "Software Engineer");
  var person2 = new Person("Greg", 27, "Doctor");

但这种方式又带来了一个新的问题,我们在全局创建了一个全局函数。

需要注意一点,按照惯例,构造函数始终应该以一个大写字母开头,而非构造函数应该以一个小写字母开头。这主要用于区别构造函数和非构造函数,因为构造函数本身也是函数。

原型模式

我们创建的每个函数都有一个 prototype (原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途可以由特定类型的所有实例共享的属性和方法。

函数原型对象请查看附录

通过原型模式创建对象,我们不必在构造函数中定义对象实例的信息,同时实例化多个对象,每个对象不会再申明一个新的函数。

可以看到, person1.sayName 和 person2.sayName 都指向了同一个函数。

但是原型模式的缺点也是显而易见的。

首先原型模式省略了构造函数模式传递参数这一环节,结果导致所有实例的初始值在默认情况下都是相同的属性值。

更重要的是,因为将属性和方法都放置在原型对象上,实质上原型上的属性是 被所有实例所共享的 。对于包含基本值的属性还表现正常,改变属性值,只是在实例上添加一个同名属性。但对于引用类型值的属性来说,这可能是个灾难。

function Person() {}
  
  Person.prototype = {
    constructor: Person,
    name: "Nicholas",
    age: 29,
    job: "Software Engineer",
    friends: ["shelby", "Court"],
    sayName: function() {
      alert(this.name);
    }
  };
  
  var person1 = new Person();
  var person2 = new Person();
  
  person1.friends.push("Van");
  
  person1.friends; // ["shelby", "Court", "Van"]
  person2.friends; // ["shelby", "Court", "Van"]

组合模式

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

这样,每个实例都会有自己的一份实例属性副本,但同时又共享方法的引用。

function Person(name, age, job) {
    this.name = name;
    this.age = age;
    this.job = job;
    this.friends = ["Shelby", "Court"];
  }
  
  Person.prototype.sayName = function() {
    alert(this.name);
  }
  
  var person1 = new Person("Nicholas", 29, "Software Enginner");
  var person2 = new Person("Greg", 27, "Doctor");
  
  person1.friends.push("Van");
  
  person1.firends; // ["Shelby", "Court", "Van"];
  person2.firends; // ["Shelby", "Court"]
  
  person1.firends === person2.firends; // false
  person1.sayName === person2.sayName; // true

动态原型模式

function Person(name, age, job) {
    this.name = name;
    this.age = age;
    this.job = job;
    
    if (typeof this.sayName != "function") {
      Person.prototype.sayName = function() {
        alert(this.name);
      }
    }
  }

寄生构造函数模式

寄生模式的基本概念就是创建一个函数,该函数的作用仅仅是封装创建对象的代码,然后再返回新创建的对象。

function Person(name, age, job) {
    var o = new Object();
    o.name = name;
    o.age = age;
    o.job = job.
    
    o.sayName = function() {
      alert(this.name);
    }
  }
  
  var person1 = new Person("Nicholas", 29, "Software Engineer");
  person1.sayName(); // "Nicholas"

看起来,除了使用new操作符之外,这个模式和工厂模式其实是一模一样的。
这个模式可以在特殊的情况下用来作为对象创建构造函数。
假设我们需要一个具有额外方法的特殊数组类型。由于不能直接修改Array构造函数,因此可以使用这个模式。

function SpecialArray() {
    var values = [];
    
    values.push.push(values, arguments);
    values.toPipedString = function() {
      return this.join("|");
    }
    
    return values;
  }
  
  var colors = new SpecialArray("red", "blue", "green");
  colors.toPipedString(); // "red|blue|green"

该模式主要缺点:
返回的对象和构造函数或构造函数的原型属性间没有关系,不·能依赖instanceof来确定对象类型。
在其他模式能够使用的情况下,尽量不要使用这种模式。

稳妥构造函数模式

function Person(name, age, job) {
    var o = new Object();
    var name = name;
    var age = age;
    var job = jbo;
    
    o.sayName = function() {
      alert(name);
    }
  }
  
  var person1 = Person("Nicholas", 29, "Software Enginner");
  firend.sayName(); // "Nicholas"
  1. 附录
  2. new 操作符

new操作符实际上会经历4个步骤:

  1. 创建一个空的简单JavaScript对象(即**{}**);
  2. 链接该对象(设置该对象的constructor)到另一个对象 ;
  3. 将步骤1新创建的对象作为**this**的上下文 ;
  4. 如果该函数没有返回对象,则返回**this**。
function new(func) {
    var o = {};
    o.__proto__ = func.prototype;
    var result = func.apply(o, arguments);
    return typeof result === "object" ? object : o;
  }

函数原型对象

理解原型对象

无论什么时候,只要创建一个新函数,就会根据一组特定的规则为该函数创建一个prototype属性,这个属性指向函数的原型对象。在默认情况下,所有原型对象都会自动获得一个construtor(构造函数)属性,这个属性包含一个指向prototype属性所在函数的指针。

在创建了一个自定义的构造函数之后,其原型对象只会取得construtoe属性,至于其他属性,则都是从Object继承而来。当调用构造函数创建一个新实例时,该实例的内部将包含一个指针(内部属性),指向构造函数的原型对象,这个指针叫[[Prototype]]。在多数浏览器中,每个对象都支持一个属性__proto__来调用[[Prototype]]。

详解 javascript对象创建模式

虽然所有实现都无法直接访问[[Prototype]],但可以通过isPrototype方法来确定对象之间是否存在关系。

我们测试了person1和person2,都返回了true。因为他们内部都有一个指向Person.prototype的指针。

Object.getPrototype()可以返回对象的原型对象。

每当代码读取某个对象的属性时,都会执行一次搜索,目标是具有给定名字的属性。搜索首先会从对象本身开始,如果在实例中找到了对应的属性,则返回该属性的值。如果没找到,则继续搜索指针指向的原型对象。这也是为什么我们在person1和person2两个实例中,并没有定义sayName这个属性,但仍能够正常使用。
我们在调用person1.sayName()是,会执行两次搜索。首先,解析器会问:“实例person1有sayName属性吗?”,答:“没有”。然后他继续搜索,再问:“person1的原型有sayName属性吗?”,答:“有”。于是,它就读取保存在原型中的函数。

虽然我们能够通过实例访问原型的属性,但却不能重新原型的属性。
如果我们在实例上添加属性名,而这个属性名又与原型中的属性名相同,即我们希望在实例中重写属性。

function Person() {}
  Person.prototype.name = 'Nicholas';
  
  var person1 = new Person();
  var person2 = new Person();
  
  person1.name === person2.name; // true
  person1.name = 'Greg';
  
  person1.name === person2.name; // false
  person1.name; // 'Greg'
  person2.name; // 'Nicholas'
  
  person1.__proto__.name; // 'Nicholas'

事实上,当我们重写原型属性时,只是在实例上添加了一个新属性。当我们把实例上的属性删除后,又会暴露出原型属性。

delete person1.name;
  person1.name; // 'Nicholas'

使用hasOwnProperty()函数能判断属性是否在实例上。

person1.hasOwnProperty('name'); // false
  person1.name = 'Greg';
  person1.hasOwnProperty('name'); // true

以上就是详解 javascript对象创建模式的详细内容,更多关于Java 创建模式的资料请关注三水点靠木其它相关文章!

Javascript 相关文章推荐
JS Timing
Apr 21 Javascript
js 点击页面其他地方关闭弹出层(示例代码)
Dec 24 Javascript
快速解决js中window.location.href不工作的问题
Nov 02 Javascript
input输入密码变黑点密文的实现方法
Jan 09 Javascript
走进AngularJs之过滤器(filter)详解
Feb 17 Javascript
Node.js创建Web、TCP服务器
Dec 05 Javascript
react以create-react-app为基础创建项目
Mar 14 Javascript
解决Vue axios post请求,后台获取不到数据的问题方法
Aug 11 Javascript
JavaScript类的继承操作实例总结
Dec 20 Javascript
vue根据条件不同显示不同按钮的操作
Aug 04 Javascript
JS绘图Flot应用图形绘制异常解决方案
Oct 16 Javascript
JavaScript实现打字游戏
Feb 19 Javascript
ES6中的Javascript解构的实现
Oct 30 #Javascript
vue video和vue-video-player实现视频铺满教程
Oct 30 #Javascript
Echarts.js无法引入问题解决方案
Oct 30 #Javascript
解决Vue keep-alive 调用 $destory() 页面不再被缓存的情况
Oct 30 #Javascript
vue 通过 Prop 向子组件传递数据的实现方法
Oct 30 #Javascript
微信小程序:报错(in promise) MiniProgramError
Oct 30 #Javascript
vue keep-alive实现多组件嵌套中个别组件存活不销毁的操作
Oct 30 #Javascript
You might like
PHP模块 Memcached功能多于Memcache
2011/06/14 PHP
php中常用字符串处理代码片段整理
2011/11/07 PHP
php中getservbyport与getservbyname函数用法实例
2014/11/18 PHP
php+ajax登录跳转登录实现思路
2016/07/31 PHP
Ubuntu 16.04下安装PHP 7过程详解
2017/03/28 PHP
jquery $.each()使用探讨
2013/09/23 Javascript
一些老手都不一定知道的JavaScript技巧
2014/05/06 Javascript
js实现非常简单的焦点图切换特效实例
2015/05/07 Javascript
JS+CSS实现大气的黑色首页导航菜单效果代码
2015/09/10 Javascript
基于jQuery实现点击列表加载更多效果
2016/05/31 Javascript
js不间断滚动的简单实现
2016/06/03 Javascript
Javascript blur与click冲突解决办法
2017/01/09 Javascript
JS Select下拉框(支持输入模糊查询)
2017/02/04 Javascript
js用类封装pop弹窗组件
2017/10/08 Javascript
AngularJS中控制器函数的定义与使用方法示例
2017/10/10 Javascript
解决vue初始化项目时,一直卡在Project description上的问题
2019/10/31 Javascript
VSCode launch.json配置详细教程
2020/06/18 Javascript
一篇文章入门Python生态系统(Python新手入门指导)
2015/12/11 Python
Python画柱状统计图操作示例【基于matplotlib库】
2018/07/04 Python
python实现画循环圆
2019/11/23 Python
keras读取训练好的模型参数并把参数赋值给其它模型详解
2020/06/15 Python
keras 简单 lstm实例(基于one-hot编码)
2020/07/02 Python
美国零售商店:Blue&Cream
2017/04/07 全球购物
Kusmi茶美国官网:优质散叶茶和茶包
2019/10/13 全球购物
全球最大运动品牌的男装、女装和童装官方库存商:A&A Sports
2021/01/17 全球购物
linux面试题参考答案(4)
2013/01/28 面试题
Tomcat的缺省是多少,怎么修改
2014/04/09 面试题
家电业务员岗位职责
2014/03/10 职场文书
企业口号大全
2014/06/12 职场文书
推普周活动总结
2014/08/28 职场文书
党员教师四风问题对照检查材料
2014/09/26 职场文书
解除劳动合同证明书模板
2014/11/20 职场文书
出纳工作检讨书范文
2014/12/27 职场文书
SQL之各种join小结详细讲解
2021/08/04 MySQL
python实现一个简单的贪吃蛇游戏附代码
2022/06/28 Python
win10拖拽文件时崩溃怎么解决?win10文件不能拖拽问题解决方法
2022/08/14 数码科技