详解 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 相关文章推荐
动态加载外部javascript文件的函数代码分享
Jul 28 Javascript
js实现checkbox全选和反选示例
May 01 Javascript
快速学习JavaScript的6个思维技巧
Oct 13 Javascript
Jquery实现上下移动和排序代码
Oct 17 Javascript
bootstrap配合Masonry插件实现瀑布式布局
Jan 18 Javascript
Vue.js实现文章评论和回复评论功能
May 30 Javascript
解决JS外部文件中文注释出现乱码问题
Jul 09 Javascript
vue todo-list组件发布到npm上的方法
Apr 04 Javascript
详解如何构建一个Angular6的第三方npm包
Sep 07 Javascript
使用vue 国际化i18n 实现多实现语言切换功能
Oct 11 Javascript
JS内置对象和Math对象知识点详解
Apr 03 Javascript
vue过滤器实现日期格式化的案例分析
Jul 02 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数据库连接
2006/10/09 PHP
小偷PHP+Html+缓存
2006/12/20 PHP
使用php实现从身份证中提取生日
2016/05/09 PHP
使用PHP json_decode可能遇到的坑与解决方法
2017/08/03 PHP
laradock环境docker-compose操作详解
2019/07/29 PHP
php+mysql实现的无限分类方法类定义与使用示例
2020/05/27 PHP
JavaScript 异步方法队列链实现代码分析
2010/06/05 Javascript
EasyUI中的tree用法介绍
2011/11/01 Javascript
如何调试异步加载页面里包含的js文件
2014/10/30 Javascript
JavaScript实现的一个倒计时的类
2015/03/12 Javascript
JQuery球队选择实例
2015/05/18 Javascript
根据user-agent判断蜘蛛代码黑帽跳转代码(js版与php版本)
2015/09/14 Javascript
功能强大的Bootstrap组件(结合js)
2016/08/03 Javascript
JS解决移动web开发手机输入框弹出的问题
2017/03/31 Javascript
基于three.js编写的一个项目类示例代码
2018/01/05 Javascript
vue学习笔记五:在vue项目里面使用引入公共方法详解
2019/04/04 Javascript
jQuery实现条件搜索查询、实时取值及升降序排序的方法分析
2019/05/04 jQuery
Vue.js实现大转盘抽奖总结及实现思路
2019/10/09 Javascript
antd-日历组件,前后禁止选择,只能选中间一部分的实例
2020/10/29 Javascript
[43:58]DOTA2上海特级锦标赛C组败者赛 Newbee VS Archon第二局
2016/02/27 DOTA
python使用正则搜索字符串或文件中的浮点数代码实例
2014/07/11 Python
Python中装饰器高级用法详解
2017/12/25 Python
利用 python 对目录下的文件进行过滤删除
2017/12/27 Python
python多线程抽象编程模型详解
2019/03/20 Python
Python匿名函数/排序函数/过滤函数/映射函数/递归/二分法
2019/06/05 Python
python实现超市商品销售管理系统
2019/10/25 Python
python复合条件下的字典排序
2020/12/18 Python
奥地利体育网上商店:Gigasport
2019/10/09 全球购物
"引用"与指针的区别是什么
2016/09/07 面试题
八荣八耻演讲稿
2014/09/15 职场文书
干部考察材料范文
2014/12/24 职场文书
拾金不昧表扬信
2015/01/16 职场文书
大学生团日活动总结
2015/05/06 职场文书
2019年冬至:天冷暖人心的问候祝福语大全
2019/12/20 职场文书
Python中Schedule模块使用详解 周期任务神器
2022/04/19 Python
Tomcat弱口令复现及利用
2022/05/06 Servers