JS 创建对象(工厂模式、构造函数模式、原型模式、混合模式、动态原型模式,寄生构造函数模式,稳妥构造函数模式)

本文主要是介绍JS 创建对象(工厂模式、构造函数模式、原型模式、混合模式、动态原型模式,寄生构造函数模式,稳妥构造函数模式),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

构造函数,原型对象,实例化对象的关系:

  • 每个函数(包括构造函数)都有一个原型对象(prototype)
  • 原型对象都包含一个指向构造函数的指针(constructor)
  • 原型对象上的属性和方法,都可以被构造函数的实例化对象所继承(所有实例化对象共享一个原型对象)
  • 实例化对象又都包含一个指向构造函数的原型对象的指针(__proto__),(p1.__proto__ === Person.prototype)(p1.__proto__.constructor === Person.prototype.constructor)(p1.__proto__.constructor === Person)

 

prototype 是函数才有的属性,切记,切记

__proto__ 是每个对象(包括函数)都有的属性

 1.工厂模式

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("a1", 12, "程序");
var person2 = createPerson("b1", 18, "销售");
console.log(person1.name) // a1
console.log(person2.age) // 18

2.构造函数模式 

构造函数,是用来创建对象的函数,本质上也是函数
任何函数,只要通过 new 操作符来调用,那它就可以作为构造函数 ;
任何函数,如果不通过 new 操作符来调用,那它跟普通函数也没有什么两样。

// 创建函数
function Person(name, age, job){this.name = name;this.age = age;this.job = job;this.sayName = function(){alert(this.name);}
}// 当作构造函数使用
var person = new Person('Nicholas',29,'Software Engineer'); // this --> person
person.sayName(); // 'Nicholas'
这里的 Person 就称为构造函数,person 称为 Person 函数对象的一个实例(复制品)// 当作普通函数调用
Person('Greg',27,'Doctor'); // this --> window
window.sayName(); // 'Greg'

对比 

* 1.没有显式地创建对象;
* 2.直接将属性和方法赋给了 this 对象;
* 3.没有 return 语句;
* 4.构造函数都应该以 一个大写字母开头
function Person(){...}
而非构造函数则应该以一个小写字母开头
function person(){...}
* 5.使用 new 创建对象
* 6.能够识别对象(这正是构造函数模式胜于工厂模式的地方)

3.原型模式 

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

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

 

在此,我们将 sayName() 方法和所有属性直接添加到了 Person 的 prototype 属性中,构造函数变成了空函数。与构造函数模式不同的是,新对象的这些属性和方法是由所有实例共享的。换句话说,person1 和person2 访问的都是同一组属性和同一个 sayName() 函数。

虽然可以通过对象实例访问保存在原型中的值,但却不能通过对象实例重写原型中的值。如果我们在实例中添加了一个属性,而该属性与实例原型中的一个属性同名,那我们就在实例中创建该属性,该属性将会屏蔽原型中的那个属性

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();
var person2 = new Person();person1.name = "Grag";
alert(person1.name); // "grag"---来自实例
alert(person2.name); // "Nicholas"--来自原型

通过使用 delete 操作符可以完全删除实例属性

var person1 = new Person();
var person2 = new Person();person1.name = "Grag";
alert(person1.name); // "grag"---来自实例
alert(person2.name); // "Nicholas"--来自原型delete person1.name;
alert(person1.name); // "Nicholas"--来自原型

通过使用 hasOwnProperty() 方法,可以检测一个属性是存在实例中,还是存在于原型中

var person1 = new Person();
var person2 = new Person();
alert(person1.hasOwnProperty("name")); // false
alert(hasPrototypeProperty("person","name")); // trueperson1.name = "Grag";
alert(person1.name); // "grag"---来自实例
alert(person1.hasOwnProperty("name")); // true
alert(hasPrototypeProperty("person","name")); // false
alert(person2.name); // "Nicholas"--来自原型
alert(person2.hasOwnProperty("name")); // falsedelete person1.name;
alert(person1.name) // "Nicholas"--来自原型
alert(person1.hasOwnProperty("name")); // false

更简单的原型写法

用一个包含所有属性和方法的对象字面量来重写整个原型对象

function Person(){}
Person.prototype = {constructor: Person, // 由于字面量写法,导致 constructor (构造函数的指针)不再指向 Person 了,所以我们需要特意将它设置适当的值name: "Nicholas",age: 29,job: "Software Engineer",sayName: function(){alert(this.name);}
}
var person1 = new Person();
alert(person1.name) // "Nicholas"

原型对象的问题

原型中所有属性是被很多实例共享的,这种共享对于函数非常合适。对于那些包含基本值的属性倒也说得过去,毕竟通过在实例上添加一个同名属性,可以隐藏原型中的对应属性。然而,对于包含引用类型值的属性来说,就有问题了---全部共享一个属性(无论怎样修改,其他实例的值都是一样的)

4.混合模式(组合使用构造函数模式和原型模式)

构造函数模式用于定义实例属性,而原型模式用于定义方法和共享的属性

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("Grag", 27, "Doctor");person1.friends.push("Van");
alert(person1.friends); // Shelby,Court,Van
alert(person2.friends); // Shelby,Court
alert(person1.friends === person2.friends); // false
alert(person1.sayName === person2.sayName); // true

这种构造函数与原型混成的模式,是目前在 ECMAScript 中使用最广泛,认同度最高的一种创建自定义类型的方法。可以说,这是用来定义引用类型的一种默认模式。

5.动态原型模式

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);}}
}
var friend = new Person("Nicholas", 29, "Software Engineer");
friend.sayName();

这里只在 sayName() 方法不存在的情况下,才会将它添加到原型中。
使用动态原型模式时,不能使用对象字面量重写原型。

6.寄生构造函数模式

function SpecialArray() {var values = new Array()values.push.apply(values, arguments)values.toPipedString = function() {console.log(this)return this.join('|')}return values}var colors = new SpecialArray('red', 'blue', 'green')console.log(colors.toPipedString(), 666)var c = SpecialArray('red', 'blue', 'green')console.log(c.toPipedString(), 999)

 

这个例子其实是高程里面的例子,我分别通过构造调用和直接调用两种方式来创建对象,并且都调用了里面的方法,可以看到,结果是完全一致的。这让我很纠结,感觉这个new在这里没有用了,有他没他都一样啊。
仔细翻看了高程里面的介绍,是这么说的:

通常,在前述几种模式都不适用的情况下,可以使用寄生构造函数模式
除了使用new操作符并把使用的包装函数叫做构造函数之外,这个模式跟工厂模式其实是一模一样的。 

 emmm,也就是说,这个模式就是在其他模式都不适用的情况下可以考虑使用。但是什么情况下其他模式会都不适用呢?暂时没有碰到这种情况,即使是书中提到的例子也就是上面的例子,也并非是在其他模式都不适用的情况下才这么写的。
综上,寄生构造函数模式我感觉就跟废了差不多,了解一下就行了。当然,如果有对这个模式更深入的理解以及非他不可的情况出现,望不吝在评论区留下见解。


7.稳妥构造函数模式

function Person(name, age) {const obj = new Object()const height = 185const eat = function() {console.log('吃饭')}obj.sayName = function() {console.log(name, age, height)eat()}return obj}const p = Person('windy-boy', 18)p.sayName()

 

 

这就是稳妥构造函数模式。就是对外暴露一个可以访问函数内部变量和方法的接口。只有通过这个接口才可以访问函数内部的变量和方法,其他方式都不行。这样的模式就是稳妥构造函数模式。
对比一下寄生构造函数模式,有两点不同:

  •     没有this
  •     没有new

其实我一直认为没有通过new调用的函数(也就是所谓的构造调用)就是普通函数,这里把函数名首字母大写来作为构造函数的标识,以这样的方式来和构造函数搭上钩。这是强行搭讪啊。。。个人感觉直接叫稳妥模式还好点。当然,这只是我的一点吐槽,发明这种模式的大师在命名的时候肯定有自己的考量,至于他的考量是什么,我也没有他电话,联系不到他,就不得而知了。。。

相比寄生构造函数模式,稳妥构造函数模式感觉更有机会用到。比如为了防止一些变量被意外更改,就可以使用稳妥构造函数模式。
 

 

 

 

这篇关于JS 创建对象(工厂模式、构造函数模式、原型模式、混合模式、动态原型模式,寄生构造函数模式,稳妥构造函数模式)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/286216

相关文章

JS常用组件收集

收集了一些平时遇到的前端比较优秀的组件,方便以后开发的时候查找!!! 函数工具: Lodash 页面固定: stickUp、jQuery.Pin 轮播: unslider、swiper 开关: switch 复选框: icheck 气泡: grumble 隐藏元素: Headroom

第10章 中断和动态时钟显示

第10章 中断和动态时钟显示 从本章开始,按照书籍的划分,第10章开始就进入保护模式(Protected Mode)部分了,感觉从这里开始难度突然就增加了。 书中介绍了为什么有中断(Interrupt)的设计,中断的几种方式:外部硬件中断、内部中断和软中断。通过中断做了一个会走的时钟和屏幕上输入字符的程序。 我自己理解中断的一些作用: 为了更好的利用处理器的性能。协同快速和慢速设备一起工作

动态规划---打家劫舍

题目: 你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。 给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。 思路: 动态规划五部曲: 1.确定dp数组及含义 dp数组是一维数组,dp[i]代表

在JS中的设计模式的单例模式、策略模式、代理模式、原型模式浅讲

1. 单例模式(Singleton Pattern) 确保一个类只有一个实例,并提供一个全局访问点。 示例代码: class Singleton {constructor() {if (Singleton.instance) {return Singleton.instance;}Singleton.instance = this;this.data = [];}addData(value)

工厂ERP管理系统实现源码(JAVA)

工厂进销存管理系统是一个集采购管理、仓库管理、生产管理和销售管理于一体的综合解决方案。该系统旨在帮助企业优化流程、提高效率、降低成本,并实时掌握各环节的运营状况。 在采购管理方面,系统能够处理采购订单、供应商管理和采购入库等流程,确保采购过程的透明和高效。仓库管理方面,实现库存的精准管理,包括入库、出库、盘点等操作,确保库存数据的准确性和实时性。 生产管理模块则涵盖了生产计划制定、物料需求计划、

Node.js学习记录(二)

目录 一、express 1、初识express 2、安装express 3、创建并启动web服务器 4、监听 GET&POST 请求、响应内容给客户端 5、获取URL中携带的查询参数 6、获取URL中动态参数 7、静态资源托管 二、工具nodemon 三、express路由 1、express中路由 2、路由的匹配 3、路由模块化 4、路由模块添加前缀 四、中间件

代码随想录冲冲冲 Day39 动态规划Part7

198. 打家劫舍 dp数组的意义是在第i位的时候偷的最大钱数是多少 如果nums的size为0 总价值当然就是0 如果nums的size为1 总价值是nums[0] 遍历顺序就是从小到大遍历 之后是递推公式 对于dp[i]的最大价值来说有两种可能 1.偷第i个 那么最大价值就是dp[i-2]+nums[i] 2.不偷第i个 那么价值就是dp[i-1] 之后取这两个的最大值就是d

EasyPlayer.js网页H5 Web js播放器能力合集

最近遇到一个需求,要求做一款播放器,发现能力上跟EasyPlayer.js基本一致,满足要求: 需求 功性能 分类 需求描述 功能 预览 分屏模式 单分屏(单屏/全屏) 多分屏(2*2) 多分屏(3*3) 多分屏(4*4) 播放控制 播放(单个或全部) 暂停(暂停时展示最后一帧画面) 停止(单个或全部) 声音控制(开关/音量调节) 主辅码流切换 辅助功能 屏

模版方法模式template method

学习笔记,原文链接 https://refactoringguru.cn/design-patterns/template-method 超类中定义了一个算法的框架, 允许子类在不修改结构的情况下重写算法的特定步骤。 上层接口有默认实现的方法和子类需要自己实现的方法

【iOS】MVC模式

MVC模式 MVC模式MVC模式demo MVC模式 MVC模式全称为model(模型)view(视图)controller(控制器),他分为三个不同的层分别负责不同的职责。 View:该层用于存放视图,该层中我们可以对页面及控件进行布局。Model:模型一般都拥有很好的可复用性,在该层中,我们可以统一管理一些数据。Controlller:该层充当一个CPU的功能,即该应用程序