前端要给力之:原子,与原子联结的友类、友函数

限时加码!20+主流AI编程工具免费用 购周边加赠Coding Plan Lite,Claude Code、Cursor等即刻畅享,学习进阶更高效! 阅读详情

JavaScript中的原子(Atom)是QoBean中提出的一个重要概念,借鉴自erlang,但具有与后者不同的含义。在QoBean里,Meta(元)与Atom(原子)是一对概念,前者表明执行系统中的最小单位,后者表明数据系统中的最小单位。QoBean约定这两个东西为一切元编程的初始,即最小化的执行系统与数据系统模型。

有什么意义呢?没什么意义。这只具备理论上的完整性。为了描述这种完整性,QoBean写了两个相当无厘头的函数:

// Atom system
//  -  atom object for data
function Atom(atom) {
  return atom || {};
}
// Meta system
//  -  meta functional for code
function Meta(func, baseMeta) {
  func.meta = baseMeta || arguments.callee;
  return func;
}
// meta is meta for self.
//   Meta = Meta(Meta);
Meta(Meta);

好了。接下来的一切故事,从Atom开始,至于Meta(),我们今后再讲。

一、原子

====

Atom()函数只有一行代码,即:

return atom || {} 

atom是传入的参数。如果有该参数,则Atom()认为它是一个原子,返回之;如果没有,则创建一个空白对象作为原子,返回。

Atom()并没有检查atom参数的有效性,但在这里QoBean强制约定“atom参数必须是一个对象实例”。之所以使用强制约定,而不是参数类型检查,这与QoBean在元语言上的基本思想有关:仅从元语言角度,QoBean认为JavaScript只有对象和函数两种类型,且函数也是一种对象。所以,对于Atom()来说,以下三种情况是合法的:

// 函数(包括构造器)可以作为atom a1 = Atom(function() {}); // 对象实例可以作为atom a2 = Atom(new Object()); // 也可以直接获取一个atom对象 a3 = Atom(); 

当然有人会问:凭什么说一个函数从作为atom参数传入Atom(),再原封不动的传出来,就成了一个“原子”了呢?答案是:JavaScript固有的特性,任何两个对象既不相等(==),也不全等(===)。

也就是说,以JavaScript的固有性质来说,任何一个对象实例其实就是一个原子。即使任意两个空白的对象直接量,也是互不相等的。即:

alert( {} == {} ); // 显示false 

当然,由于任何函数其实都是Function()的实例,所以也具有相同的性质:

// 显示true, 函数作为对象,是Function()的实例 function foo() {}; alert(foo instanceof Function); 
// 显示false, 函数都以原子对象的形式存在,故而互不相等 alert((function(){}) == (function(){})); 

二、原子的应用(1):识别器

====

在《JavaScript语言精髓与编程实践》中,谈到过对象的原子性的一种作用,亦即是作为“识别器”。例如说,我们知道硬币有正反两面,所以我们可以写这样的一个对象:

=========
x = {
  front: true,
  back: true
}
=========

显然我们可以用('front' in x)或者(x['front'])来识别它。但是这就存在了一个问题,因为x对象本来就有toString这样的属性,所以是不是说('toString' in x)为true,因此就表明x有一个名为'toString'的面了呢?同理,如果有人为x添加了一'middle'属性,那么将无法检查是x原本就有middle这个面呢,还是被某些代码污染了?

显然,按照对象设计的原理来说,将”属性”暴露出来,并可以任意读写,是导致这一切的根源。解决它的法子也很简单:用特定的方法来读取之。例如我们要查询"有没有某个面":

x = { front: true, back: true, query: function(side) { reutnr side in this } } 

我们跨出了正确的一步。但是,与此前完全相同的原因,我们调用x.query('toString')时仍然返回true。这显然不是我们想要的,因为硬币显然没有'toString'这样的'面'。

好吧,我们再向前行一步。我们知道任何一个对象都具有原子性,也就是说唯一。我们只要创建一个对象,让他成为query所需要的一个“钥匙”;然后把这个钥匙藏起来,这样谁也找不到它,于是谁也不能干扰这个对象所约束的那些性质了。

实现起来也很简单:

var coin = function() { var exist = Atom(); 
var sides = { front: exist, back: exist } 
return 
{ query: function(side) { return sides[side] === exist } 
} 
}();  

好了,就这样。现在如果你调用coin.query('front')就一定返回true,而qoin.query('toString')则返回false了。

不过马上就会有人跳起来了:你这不是多此一举吗?既然sides已经是私有的了,就不必担心外面随意添加成员了呵!再则,不使用exist而使用类似true之类的值不也能判断吗?

是的,初看起来上述的置疑都对。不过在复杂的系统环境中,会存在三个问题,一个是sides[side]的效率不错,总比用数组实现来得强;第二个是,如果sides不在当前的函数内呢?第三个问题则更麻烦,如果你使用true之类的值,又如何避免第三方的代码通过Object.prototype来添加一个成员呢?

例如说,假定query()使用"sides[side] === true"来检测,的确可以避免toString之类的影响,但是如果有人写一行代码"Object.prototype.xxx = true",那coin.query('xxx')就将让人傻眼了。

所以,我们最好是找把钥匙藏起来,藏得好好的,别人都看不见。

三、原子的应用(2):友类与友函数

====

JavaScript没有明确的“类”的概念,所以这里讨论的友类与友函数其实是同一回事,只是Atom()作用于构造器还是普通函数的区别罢了。此外再强调一点,这里我们讨论的“友元”与“友函数”在名称上的确借鉴自C++,但概念上却有相当的差异。唯一与之相同的约定是,如果A是B的友函数,则A就能访问B的内部结构(例如私有成员)。

要实现这一点,我们得用Atom()把这两个函数联接起来。

举个例子来说,函数A内部有一个列表,记录了x,y,z三种状态。我们设定,有且只有函数B能修改之(当然A的一些内部的方法也能修改,但不是我们这里的主要问题),那么怎么办呢?

function A() { var state = { x: 100, y: 1000, z:5, set: function(n, v) 
{ this[n] = v } } // ... } function B() { // 如何在这里修改A中的state? } 

很自然的想法是让A公布一个方法出来。例如:

function A() { var state = ... // 公布一个方法 this.set = function(n, v) { state.set(n, v); } } 

这样可以通过构建一个对象并用obj.set()来修改。或者我们将A整个的放在一个闭包里,再返回一个函数来做"修改state"的事情。但无论如何,我们只能做到“修改state",而无法做到“只有B能修改,其它位置都无法调用修改state的代码”。

好吧。传统观念上的、终极的方法, 是我们将state从A()里面移出来。然后将A()与B()放在同一个闭包里面:

function() { var state = { .... }; function A() { ... }; function B() { // 这样就保证仅有A与B是可以修改state的了。 } } 

问题是,在组织大型的代码、类库或类继承树时,我们可能无法保证A与B处于同一个函数,或者他们根本就不是一个人写的,或者B会是将来的开发人员追加编写的……等等如此,反正A不见得总能与B在一个函数上下文里。

解决这个问题的方法呢?使用Atom()来为A()与B()建立一个友元关系,使它们成为友函数(如果在对象继承中,则可以实现为友类)。如下:

A = B = Atom(); 
A = function(atom) { return function() { var state = { ... }; 
if (arguments[0]===atom) return state; // ... 后续逻辑 } }(A); 
B = function(atom) 
{ return function() { var A_state = A(atom); // 后续逻辑 } 
}(B); 

现在,在最初的时候,A和B都指向一个原子。在得到“真实的A()、B()"的同时,函数A()、B()各持有了atom的一个引用。此后,系统中就再也找不到atom了——没有任何方法可以A、B之外再他们所使用的atom。

由于A()与B()各持有了一个atom,于是,当B()函数调用A(atom)时,A函数就绝对可以信任这是来自于B的调用,因此将state返回即可——当然也可以返回存取函数,或者别的什么东西。对于B()的调用A(atom),以及A()对arguments[0]进行的识别,一切都是安全的、不可能受外界其它任何因素影响的。

四、其它

====

1、同样的方法,我们可以对更多个的函数、构造器或子系统(使用同一个闭包上下文的大段代码块)建立友元关系。

2、原子性是JavaScript对象的固有特性,使用Atom()函数主要是可以上述技巧在系统中具有明确的语义,这比随处定义一个"{}"来得要好。

3、QoBean内部使用这一技术来构建类继承关系,从而使子类可以访问父类的特性,对非子类来说则完全隔离。

React css自动原子 闲来无事,开发一个css自动原子化工具玩玩。 阅读详情

相关推荐

前端原子设计

「1」微前端 (Miro frontends) "微前端" 应该是我们 2020 年里听的最多的一个前端技术。现在非常多的大厂都在尝试这个新技术来解决大型前端项目中的问题。 虽然我们前端开发中有模块化(modular)的组件(components),但是它相比后端的 “微服务” 是大有不同的。 在了解 “微前端” 之前,我们先给没有接触过后端的同学科补一下后端的 “微服务” 知识。 微服务是什么? 微服务是一种开发软件的架构和组织方法,其中软件由通过明确定义的 API 进行通信的小型独立服务组成。这些服.

马优晨 751

前端】一分钟带你了解原子化架构

原子化架构通过将 UI 设计从最小的原子单元逐步构建为完整的页面,不仅提升了代码的复用性和可维护性,也使得开发过程更加有序、高效。对于大型前端项目和需要统一设计语言的系统来说,原子化架构是一个非常理想的解决方案。希望通过这篇文章,你能深入理解原子化架构的原理、设计思路以及实际应用,并在你的项目中成功实践。

九日的博客 3517

UnoCSS原子CSS引擎—原子化真的是现代前端CSS利器?

但 Vue 或 React 各自的组件化设计思想,都可以通过各自的组件化拆分来解决代码冗余的问题,使其可适用于各种简单&复杂的业务场景,是一套完善的最佳实践,而原子化CSS 没有办法做组件化拆分的,所以随着业务复杂度的上升,代码冗余迟早会发生,这直接限制了此框架的普及,所以它无法作为前端CSS问题的根本解决方案。原子化CSS本身的概念和 Tailwind CSS、UnoCSS[2] 设计都比较简单,这里主要想聊一下在现代前端中,原子化CSS到底是不是完美的解决方案,是不是解决CSS问题的正确方向。

天天打码-16年老码农 3762

快速掌握原子化CSS框架:Tailwindcss

在Tailwind CSS中,PostCSS用于处理CSS文件并应用各种转换和插件。它负责处理CSS的自动前缀添加、嵌套、变量替换、支持CSS未来特性的转换等。通过使用PostCSS,Tailwind CSS可以提供一些强大的功能,例如自定义主题、响应式设计、浏览器兼容性等。Autoprefixer是一个PostCSS插件,用于自动添加CSS前缀。vite版本过低,最开始创建的vite版本是 1.0.0-rc.13。升级最新 vite 版本体验。

WXY19990803的博客 1674

受够了重复繁琐的css?来试试原子

前言 在使用css的过程中,遇到了如下的痛点: 给取名比较困难,刚开始语义化名,后面逐渐松懈,导致很多无意义名出现 使用预处理器时,嵌套的特别深 修改已有代码时,写在最后面或者直接加入行内样式,同一个组件的样式分开在不同的地方 名越写越多,一个名带有的样式越来越杂 颜色和字体属性不统一,导致页面一致性越来越差 css和html脱节,一边翻css一边翻html 重复代码过多,有些属性经常出现在许多地方 思考过程 在前端开发中,可能大部分人对于css的使用都经历过这样几步 原生css->使用

sc909090的博客 1143

前端使用原子的优劣?

作者:于江水链接:https://www.zhihu.com/question/22110291/answer/20328000来源:知乎著作权归作者所有,转载请联系作者获得授权。 《编写高质量代码 Web前段开发修炼之道》一书中,提倡并提供了一套原子,于是我就开始在个人和外包的项目中开始使用原子。用的多了,你就会明白原子的优劣以及如何使用了。下面是我的几点感悟:原子的缺点:1...

weixin_30252155的博客 195

前端前端 CSS 原子化,代码乐高随便搭

CSS原子化将样式拆分为最小、不可再分的原子,每个负责单一属性(如m-1表示margin: 1rem),通过组合快速构建复杂样式。优势包括:高代码复用性、减少样式冲突、易于维护、提升开发效率。在Vue 3项目中,使用Tailwind CSS实现原子化,通过安装、配置并引入Tailwind,即可在组件中直接使用原子,简化样式管理,提升开发体验。

JavaDog程序狗 900

前端 CSS 经典:CSS 原子

提供一系列的助记词,用名来代表样式。相当于用一两行定义一个名,不能再分解了。这就叫 CSS 原子化。.px-2 {

weixin_64684095的博客 657

CSS 原子化:带你一分钟了解什么是CSS原子化?

CSS 原子化:带你一分钟了解什么是CSS原子化?

我是 二川兄,对Web开发、GIS开发、3D模型、机器学习、面试技巧等方面都有一些涉猎~ 欢迎您加入技术交流圈!你可以在我的文章末尾找到我~ 8010

前端原子化架构-学习中

前端原子化架构是一种高效的开发范式,它通过将UI拆分为最小的可复用组件,极大地提升了开发效率和可维护性。原子化设计不仅适用于UI组件,还可以扩展到状态管理、工具链等多个层面,形成完整的原子化开发体系。快速构建高质量的界面提高代码复用性和可维护性降低团队协作成本应对复杂前端项目的挑战原子化架构已经成为现代前端开发的主流趋势,学习和掌握它将为你的前端开发能力带来质的飞跃。

weixin_43667390的博客 1180

原子化 CSS 实践

原子化 CSS 实践。

jclee95的个人博客 1571

〖大前端 - 基础入门三大核心之CSS篇②〗- CSS选择器之标签选择器、id选择器、class选择器原子

我们知道css是用来给HTML标签定义样式的,那么我们怎么给指定的标签定义想要的样式呢?这就需要用到css选择器 “选中” 这个标签。

易编橙 · 终身成长社群,相遇已是上上签! 2万+

第二章:引入 Tailwindcss 实现原子化CSS【前端工程化入门-----从零实现一个react+ts+vite+tailwindcss组件库】

原子化CSS是一种CSS的架构方式倾向于使用小巧、用途单一的class定义样式以class定义单一样式的方式,有效减少CSS包的体积。

henu_GM的博客 4742

关于使用原子css的优缺点

最近找到一个VUE的文档,它将VUE的各个知识点进行了总结,整理成了《Vue 开发必须知道的36个技巧》。内容比较详实,对各个知识点的讲解也十分到位。

小王的技术库 1073

JavaScript中创建原子的几种方法

本文是“JavaScript的元语言”系列中的一篇。其它内容参见: * JavaScript的元系统 JavaScript中可以通过Object.create(null)来创建原子,这是非常自然而又易于理解的方式。不过也有一些其它的方法来实现相同的效果,虽然在概念上有所不同,但是它们创建的一样是“原子对象”。 创建原子 使用Object.create() // 方法1...

aimingoo的专栏 2800

【 攻城略地 】使用原子css库atomic.css助力前端开发

介绍 提供 web 微信小程序 的原子CSS库,拿来即用,可快速搭建前端页面,也可以扩展属于自己的样式库。 统一的样式名称 内置 normalize.css 支持自定义选择器 支持自定义样式名称 支持自定义样式名称连接符 支持自定义生成带有数值后缀的样式 支持元素响应式可见 gitee atomic.css 下载atom.css atom.css atom.min.css 下载atom.wxss atom.wxss atom.min.wxss 使用说明 Position .pos, .pos-r

qq_34417216的博客 1011
上一篇: 前端要给力之:URL应该有多长?
下一篇: 前端要给力之:分解对象构造过程new()
aimingoo
博客等级 码龄21年 1755粉丝 213原创
评论 7
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值