博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
javascript设计模式与开发实践(二)- 封装和原型模式
阅读量:6276 次
发布时间:2019-06-22

本文共 4138 字,大约阅读时间需要 13 分钟。

封装

封装数据

在许多语言的对象系统中,封装数据是由语法解析来实现的,这些语言也许提供了 private、 public、protected 等关键字来提供不同的访问权限。例如:java

但在js里面,并没有提供这些关键字的支持,我们只能通过作用域来模拟实现封装性。(ES6 let除外)

var myTest = (function() {    var _name ='jason';    return {        getName: function(){            return _name;        }    }})()console.log(myTest.getName()); // 输出:jasonconsole.log(_name); // 输出:_name is not defined复制代码

这里,我们定义了一个自执行函数(什么叫自执行函数?参考文章),函数内部定义了一个变量_name,函数return一个对象出去(也叫模块模式),向外部暴露了一个getName函数,这里的_name变量就被封装在了myTest函数内部。

上文说的是封装数据,在js里,不光数据可以封装,还可以封装实现、封装类型,封装变化等,封装使得对象之间的耦合变得松散,对象之间只通过暴露的 API 接口来通信。

原型模式和基于原型继承的JavaScript对象系统

在java中,对象必须由类创建而来,而在js中,对象却是通过原型继承的方式得来的,在原型编程的思想中,类并不是必需的,对象未必需要从类中创建而来, 一个对象是通过克隆另外一个对象所得到的。

原型模式不单是一种设计模式,也被称为一种编程泛型。

使用克隆的原型模式

从设计模式的角度讲,原型模式是用于创建对象的一种模式,如果我们想要创建一个对象, 一种方法是先指定它的类型,然后通过类来创建这个对象。原型模式选择了另外一种方式,我们 不再关心对象的具体类型,而是找到一个对象,然后通过克隆来创建一个一模一样的对象。 原型模式的实现关键,是语言本身是否提供了 clone 方法。ECMAScript 5 提供了 Object.create 方法,可以用来克隆对象。代码如下:

var Plane = function(){     this.blood = 100;    this.attackLevel = 1;    this.defenseLevel = 1;};var plane = new Plane();plane.blood = 500;plane.attackLevel = 10;plane.defenseLevel = 7;var clonePlane = Object.create( plane );console.log( clonePlane ); // 输出:Object {blood: 500, attackLevel: 10,defenseLevel: 7}在不支持 Object.create 方法的浏览器中,则可以使用以下代码:Object.create = Object.create || function( obj ){    var F = function(){};    F.prototype = obj;    return new F(); }复制代码

原型编程范型的一些规则

  • 所有的数据都是对象。
  • 要得到一个对象,不是通过实例化类,而是找到一个对象作为原型并克隆它。
  • 对象会记住它的原型。
  • 如果对象无法响应某个请求,它会把这个请求委托给它自己的原型。

JavaScript中的原型继承

这里我们来根据上面的范式来整理一下js中遵循的规则

所有的数据都是对象

在js中,除了undefined之外,所有数据都是对象,number、boolean、string 这几种基本类型数据也可以通过“包装类”的方式变成对象类型数据来处理。那么根据原型规则,这些对象一定有个根对象,这个对象就是Object.prototype,Object.prototype 对象是一个空的 对象。我们在 JavaScript 遇到的每个对象,实际上都是从 Object.prototype 对象克隆而来的, Object.prototype 对象就是它们的原型。

var obj1 = new Object();var obj2 = {};console.log( Object.getPrototypeOf( obj1 ) === Object.prototype ); // 输出:trueconsole.log( Object.getPrototypeOf( obj2 ) === Object.prototype ); // 输出:true复制代码
要得到一个对象,不是通过实例化类,而是找到一个对象作为原型并克隆它

js中克隆是引擎内部实现的,我们不用去关心他是如何实现的,我们只要知道使用var obj1 = new Object()或者var obj2 = {},引擎就会从Object.prototype克隆一个对象出来。

接下来我们看看如何使用new运算符得到一个对象

var Person = function(name) {    this.name = name;    this.getName = function() {        return this.name;    }}var p = new Person('jason');console.log(p.name);console.log(p.getName());console.log(Object.getPrototypeOf(p) === Person.prototype); // 输出true复制代码

在 JavaScript 中没有类的概念,这句话我们已经重复过很多次了。但刚才不是明明调用了newPerson()吗?

在这里 Person 并不是类,而是函数构造器,JavaScript 的函数既可以作为普通函数被调用, 7 也可以作为构造器被调用。当使用 new 运算符来调用函数时,此时的函数就是一个构造器。 用 new 运算符来创建对象的过程,实际上也只是先克隆 Object.prototype 对象,再进行一些其他额 外操作的过程。

对象会记住它的原型

JavaScript 给对象提供了一个名为__proto__的隐藏属性,某个对象的__proto__属性默认会指 向它的构造器的原型对象,即{Constructor}.prototype。 我们通过代码来验证:

var objA = {    name: 'jason'}console.log(objA.__proto__ === Object.prototype); //true复制代码

再来

var objB = function(age) {    this.age = age;}var b = new objB();console.log(b.__proto__ === objB.prototype); //true复制代码

实际上,__proto__就是对象跟“对象构造器的原型”联系起来的纽带 切记这句话,对未来理解js原型链很有帮助。

如果对象无法响应某个请求,它会把这个请求委托给它的构造器的原型

虽然 JavaScript 的对象最初都是由 Object.prototype 对象克隆而来的,但对象构造器的原型并不仅限于 Object.prototype 上,可以动态指向其他对象。

var obj = { name: 'sven' };var A = function(){};A.prototype = obj;var a = new A();console.log(a.__proto__ === obj); //trueconsole.log(a.name); // 输出:sven复制代码

上面的代码中,第一行和第二行本没有任何关联,obj是个对象字面量创建的对象,A是个空方法,在第三行代码执行之前,obj__proto__指向Object.prototypeAprototype指向自身的构造器,A.prototype = obj;将引用指向了obj,所以在代码执行完后,对象a的原型指向obj,虽然a本身没有name属性,但原型上拥有name属性

总结

现在,让我们来总结一下js创建对象的几种方式:

  1. 对象字面量
var obj = {};复制代码
  1. 通过对象构造器创建
var Co = function(){};var obj = new Co();复制代码
  1. 通过Object.create();创建(ES5以后版本支持)
var Co = function(){};var obj = Object.create(Co);复制代码
  1. 通过class创建(ES6以后版本支持)
class An{    constructor(name){        this.name = name;    }        getName() {        return this.name;    }}class Dog extends Animal {     constructor(name) {        super(name);     }    speak() {        return "woof";    } }var dog = new Dog("Scamp");console.log(dog.getName()); // Scampconsole.log(dog.speak()); // woof复制代码

上面的几种创建对象的方式有本质的区别,这里先不做详细说明,后续学完作用域和闭包后再统一说明。

转载于:https://juejin.im/post/5a9c0cc3f265da2375065c94

你可能感兴趣的文章
如何在Linux中压缩及解压缩.bz2文件
查看>>
数据为王的时代 大数据对消费金融的影响
查看>>
万事达启动“用你自己替代密码”计划
查看>>
阿里巴巴“NASA计划”新进展:一个世界纪录!
查看>>
Linux下使用USB网络
查看>>
5G标准化进程提速 射频器件市场空间将打开
查看>>
《分布式系统:概念与设计》一3.3.6 拥塞控制
查看>>
“烧烤模式”下 安防设备如何经得住考验?
查看>>
人工智能时代,语音技术怎么赚钱?
查看>>
美大选被攻击 未来选举要回归纸质选票?
查看>>
SCRM火了,SaaS服务再现新风口
查看>>
黑客站在ATM机面前就直接吐出钞票 如何做到的?
查看>>
TensorFlow教程之资源 4.5 术语表
查看>>
菜鸟网络算法专家朱礼君:为你的淘宝订单选外箱的是人工智能 | CCF-GAIR 2017
查看>>
CMU教授金出武雄演讲:户外机器人系统 | CCF-GAIR 2017
查看>>
未来3年内25G将成数据中心主流网络
查看>>
《云计算:原理与范式》一1.3 云计算的架构与类型
查看>>
Strategy Analytics:视频监控是安防系统成功的关键
查看>>
四家企业联合发起 OPEN AI LAB,听听余凯等大佬怎么说
查看>>
永不晕车的法子, 快告诉你身边晕车的人!
查看>>