JS基础--ES5创建对象的7种模式
以下内容总结自《JavaScript高级程序设计(第3版)》
一. 工厂模式
ES5中无法创建类,所以开发人员用函数封装以特定接口创建对象的细节。
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", 20, "Software Engineer");
var person2 = createPerson("Greg", 23, "Doctor");
- 优点: 可以解决创建多个相似对象写重复代码的问题。
- 缺点: 这里的对象都是Object类型,没有解决对象识别的问题(即怎样知道一个对象的类型)。
二. 构造函数模式
ECMAScript中可以创建自定义的构造函数。
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", 20, "Software Engineer");
var person2 = new Person("Greg", 23, "Doctor");
- 优点: 指定了实例的类型,弥补了工厂模式的缺点。
- 缺点: 每个方法都要在每个实例中重新创建一遍。
是否有办法弥补这个缺点呢?有。
将sayName函数定义转移到构造函数外部,如下:
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", 20, "Software Engineer");
var person2 = new Person("Greg", 23, "Doctor");alert(person1.sayName === person2.sayName); // false
- 缺点: 上面这种做法解决问题的同时又带来了新的问题,就是如果一个对象里面要定义很多方法的话,我们就要定义很多的全局函数,这样我们自定义的引用类型就毫无封装性可言了。
构造函数模式和工厂模式的不同:
- 没有显示地创建对象;
- 直接将属性和方法赋值给了this对象;
- 没有return语句。
使用new创建对象实际会经历以下步骤:
- 创建一个新对象;
- 将构造函数的作用域赋值给新对象(此时this就指向这个新对象);
- 执行构造函数中的代码(为这个新对象添加属性);
- 返回新对象。
使用构造函数创建对象的经典用法:
// 当做构造函数使用
var person = new Person("Nicholas", 20, "Software Engineer");
person.sayName();// 当做普通函数使用,此时的this指向window
Person("Nicholas", 20, "Software Engineer");
window.sayName();// 在另一个对象的作用域调用
var o = new Object();
Person.call(o, "Nicholas", 20, "Software Engineer");
o.sayName();
三. 原型模式
每一个函数都有一个prototype(原型)属性,这个属性是一个指针,指向一个对象,而这个对象的用途是包含可以由特定类型的所有实例共享的属性和方法。
也就是说,prototype是通过调用构造函数而创建的那个对象实例的原型对象。
function Person() {}
Person.prototype.name = "Nicholas";
Person.prototype.age= 20;
Person.prototype.job= "Software Engineer";
Person.prototype.sayName = function() {alert(this.name);
};var person1 = new Person();
person1.sayName(); // Nicholasvar person2 = new Person();
person1.sayName(); // Nicholasalert(person1.sayName === person2.sayName); // true
- 优点: 使用原型对象可以让所有对象实例共享它所包含的属性和方法,解决了构造函数模式的缺点。
- 缺点:
(1). 省略了构造函数传参的环节,这会导致所有的实例都默认取得相同的属性。在例子 中表现为无法为不同的实例定义不同的name,age等属性。
(2). 当原型对象中存在引用类型的属性时,所有的实例会共享同一个该引用类型的值。
例:
function Person() {}
Person.prototype = {constructor: Person,name: "Nicholas",age: 20,job: "Software Engineer",friends: ["Shelby", "Court"],sayName: function() {alert(this.name);}
}var person1 = new Person();
var person2 = new Person();person1.friends.push("Van");alert(person1.friends); // Shelby,Court,Van
alert(person2.friends); // Shelby,Court,Van
alert(person1.friends === person2.friends); // true
由上面的代码可知,person1和person2两个实例共享同一个friends属性。而正常来讲,我们是希望每一个实例都有自己的属性,不受其他实例的影响。
由于上述问题,人们很少单独使用原型模式,而是将原型模式和构造函数模式组合起来一起使用。
四. 组合使用原型模式和构造函数模式
原型模式用于定义方法和共享的属性,构造函数模式用于定义实例属性。
这样每个实例都会有自己的一份实力属性的副本,同时又共享对方法的引用。
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", 20, "Software Engineer");
var person2 = new Person("Greg", 23, "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
- 优点: 结合了构造函数模式和原型模式的优点。
- 缺点: (未知,待填)
五. 动态原型模式
该模式将分离的构造函数和原型全部封装在了构造函数里,并通过检查某个应存在的方法是否有效,来决定是否初始化原型。
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 person1 = new Person("Nicholas", 20, "Software Engineer");
person1.sayName();
上面的sayName方法会在初次调用构造函数的时候添加到原型当中去,之后再调用时,由于能够在原型中找到sayName方法,所以不会再重新执行。
六. 寄生构造函数模式
该模式和工厂模式只有两点不同:
- 使用new操作符。
- 把使用的包装函数叫做构造函数。
示例:
function Person(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 = new Person("Nicholas", 20, "Software Engineer");
person1.sayName();
这个模式可以在特殊情况下为对象创建构造函数。比如我们想为数组添加额外方法,但是不能直接修改Array构造函数,此时可以使用这个模式。
function SpecialArray() {// 创建数组var values = new Array();// 添加值values.push.apply(values, arguments);// 添加方法values.toPipedString = function () {return this.join("|");}// 返回数组return values;
}var color = new SpecialArray("red", "blue", "green");
color.toPipedString(); // "red|blue|green"
- 优点: 解决为对象创建构造函数的问题。
- 缺点: 返回的对象与在构造函数外面创建的对象没有区别,不能使用instanceof确定对象类型。
七. 稳妥构造函数模式
首先明确什么是稳妥对象(durable object):所谓稳妥对象,指的是没有公共属性,而且其方法也不能引用this的对象。
稳妥对象最适合在一些安全的环境中(这些环境会禁止使用this或new),或者在防止数据被其他应用程序(如Maphup程序)改动时使用。
示例:
function Person(name, age, job) {// 创建要返回的对象var o = new Object();// 可以在这里定义私有变量和函数// 方法o.sayName = function () {alert(name);};// 返回对象return o;
}var person1 = Person("Nicholas", 20, "Software Engineer");
person1.sayName(); // "Nicholas"
person1.name; // undefined
以这种模式创建的对象中,name属性只能通过sayName方法访问。
即使有其他代码给这个对象添加方法或者数据成员,但也不可能有别的办法访问传入到构造函数中的原始数据。
稳妥构造函数模式与寄生构造函数模式的区别:
- 新创建的实例方法不引用this;
- 不使用new操作符调用构造函数。
JS基础--ES5创建对象的7种模式相关推荐
- JavaScript中的对象,如何创建对象,创建对象的7种模式
ECMA-262把对象定义为:"无需属性的集合,其属性可以包含基本值.对象或者函数."严格来讲,这就相当于说明对象是一组没有特定顺序的值.对象的每个属性或方法都有一个名字,而每个名 ...
- 细节:js 创建对象的几种模式举例
工厂模式(不推荐) var sayName = function(){return this.name; };function createPerson(name,age){var obj = {}; ...
- JavaScript简餐——创建对象的三种模式
文章目录 前言 一.工厂模式 二.构造函数模式 三.原型模式 总结 前言 写本<JavaScript简餐>系列文章的目的是记录在阅读学习<JavaScript高级程序设计(第4版)& ...
- Java的基础方法Java的对象_java基础之 创建对象的几种方式
有4种显式地创建对象的方式: 1.用new语句创建对象,这是最常用的创建对象的方式. 2.运用反射手段,调用java.lang.Class或者java.lang.reflect.Constructor ...
- 批量创建对象的四种模式
1.工厂模式 封装创建对象的函数,提高代码复用性, 缺点: 创建出来的对象都是Object类型,无法区分种类. 每创建一个对象,sayName方法都连着创建一个,浪费空间 ...
- Java基础之创建对象的五种方式
第一种:直接new 效率最高 Person person1 = new Person("person1",22); 第二种:Class类的newInstance 可以new 无参的 ...
- js创建对象的几种常用方式小结(推荐)
(转http://www.jb51.net/article/25093.htm) 第一种模式:工厂方式 复制代码 代码如下: var lev=function(){ return "脚本之家 ...
- 课程介绍 复习 创建对象的三种方式 自定义构造函数创建对象 工厂模式创建对象
课程介绍 <!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8 ...
- 【javascript基础——系列10】js中隐藏元素的几种方法以及代码
系列文章 [javascript基础--系列1]前端页面ajax连接后台服务器传输数据 [javascript基础--系列2]前端页面axios连接后台服务器传输数据 [javascript基础--系 ...
最新文章
- 网站基本维护躲不过这三点!
- SpringMVC简介-传统的Model1和Model2/MVC思想及其优势/Spring MVC的优势
- 软件开发架构介绍||OSI七层协议之物理层、数据链路层、网络层、传输层(mac地址、ip协议、断开协议、tcp协议之三次握手四次挥手)
- LeetCode 215 Kth Largest Element in an Array
- 罗技键盘linux,logiops,在 Linux下设置罗技鼠标的按键和手势
- 论文浅尝 - ICML2020 | 通过关系图上的贝叶斯元学习进行少样本关系提取
- Linux操作系统 (二)下载以及安装
- python将png转为jpg,Python OpenCV读取png图像转成jpg图像存储的方法
- 为什么增益裕度可以是负数
- [转载]八种常见的防盗链方法总结及分析
- 新版Edge浏览器如何设置崩溃自动恢复网页
- 美团外卖离线数仓建设实践
- php完美导出word,使用phpword插件实现word文档导出
- NVT平台model sensor配置
- 获取非行间样式和行间样式 Math对象
- Jenkins使用过程遇到的问题记录
- 2023最新SSM计算机毕业设计选题大全(附源码+LW)之java大学生学科竞赛管理系统t16zl
- mysqlclient安装失败解决方案
- 3D 打印机 G 代码命令:完整列表和教程
- 医院计算机系统日常维护记录表,医院计算机信息管理系统维护措施
热门文章
- C++自学20:指针/指针的指针/const
- Bootstrap-datetimepicker控件使用
- 文件操作,读文件、写文件、获取文件长度、删除文件、判断文件格式等。
- 命令行插入时显示不存在_成年人的世界里,不存在“容易”两个字没人心疼时自己要学会坚强...
- 计算机网络具有双重性特点,计算机网络(本)_201806_模拟卷2_答案
- Ubuntu 16.04 LTS误删系统内核或驱动导致无法上网解决方案
- 初解vue脚手架vue-cli,及demo示例(一)
- 探索安卓中有意义的动画!
- 嵌套点击事件只执行第一次
- listview与adapter用法