這篇文章帶給大家的內容是關於es7中修飾器(Decorator)的詳解(附範例),有一定的參考價值,有需要的朋友可以參考一下,希望對你有幫助。
1. Decorator基本知識
在許多框架和函式庫中看到它的身影,尤其是React和Redux,還有mobx中,那什麼是裝飾器呢。
修飾器(Decorator)是一個函數,用來修改類別的行為。不是很理解這種抽象概念,還是看程式碼講解實際些。
//定义一个函数,也就是定义一个Decorator,target参数就是传进来的Class。 //这里是为类添加了一个静态属性 function addAge(target) { target.age = 2; } //在Decorator后面跟着Class,Decorator是函数的话,怎么不是addAge(MyGeekjcProject)这样写呢? //我只能这样理解:因为语法就这样,只要Decorator后面是Class,默认就已经把Class当成参数隐形传进Decorator了(就是所谓的语法糖)。 @addAge class MyGeekjcProject {} console.log(MyGeekjcProject.age) // 2
1.1 傳參修飾類別
但上面這樣只傳了一個Class當參數不夠靈活怎麼辦?
我們可以在外層套一個函數,只要最後回傳的是一個Decorator即可,管你套多少個函數傳多少個參數都無所謂。
function addAge(age) { return function(target) { target.age = age; } } //注意这里,隐形传入了Class,语法类似于addAge(2)(MyGeekjcProject) @testable(2) class MyGeekjcProject {} MyGeekjcProject.age // 2 @addAge(3) class MyGeekjcProject {} MyGeekjcProject.age // 3
1.2 修飾類別的屬性
下面是修改類別的prototype物件
function description(target) { target.prototype.url = 'https://www.geekjc.com'; } @description class MyGeekjcProject {} let geekjc = new MyGeekjcProject(); geekjc.url // https://www.geekjc.com
概念大概理解了,修飾器不僅可以修飾類,還可以修飾類別的屬性
//假如修饰类的属性则传入三个参数,对应Object.defineProperty()里三个参数,具体不细说 //target为目标对象,对应为Class的实例 //name为所要修饰的属性名,这里就是修饰器紧跟其后的name属性 //descriptor为该属性的描述对象 //这里的Decorator作用是使name属性不可写,并返回修改后的descriptor function readonly(target, name, descriptor){ descriptor.writable = false; return descriptor; } class Person { @readonly name() { return `${this.first} ${this.last}` } }
關於Object.defineProperty()可以看文章學習Object.defineProperty
#再看一個複雜的例子
//定义一个Class并在其add上使用了修饰器 class Math { @log add(a, b) { return a + b; } } //定义一个修饰器 function log(target, name, descriptor) { //这里是缓存旧的方法,也就是上面那个add()原始方法 var oldValue = descriptor.value; //这里修改了方法,使其作用变成一个打印函数 //最后依旧返回旧的方法,真是巧妙 descriptor.value = function() { console.log(`Calling "${name}" with`, arguments); return oldValue.apply(null, arguments); }; return descriptor; } const math = new Math(); math.add(2, 4);
看完上面的程式碼就知道decorator有什麼用了,修飾器不僅可以修飾類別的行為,還可以修飾類別的屬性。
2. react高階元件
更通俗地描述為,高階元件透過包裹(wrapped)被傳入的React元件,經過一連串處理,最終傳回一個相對增強(enhanced)的React元件,供其他元件呼叫。
3. 實作一個高階元件
面我們來實作一個最簡單的高階元件(函數),它接受一個React元件,包裹後再返回。
export default function withDescription(WrappedComponent) { return class HOC extends Component { render() { return <div> <div> 极客教程(https://www.geekjc.com)致力于推广各种编程语言技术,也为了未来数字化世界,让人更容易找到操作数字化的方式,为了未来而生的编程学习平台, 大部分资源是完全免费的,并且会根据当前互联网的变化实时更新本站内容。 </div> <wrappedcomponent></wrappedcomponent> </div> } } }
在其他元件裡,我們引用這個高階元件,用來強化它。
@withDescription export default class Geekjc extends Component { render() { return ( <div> 我是一个普通组件 </div> ); } }
在這裡使用了ES7裡的decorator(可以看第一節的Decorator),來提升寫法上的優雅,但是實際上它只是一個語法糖,下面這種寫法也是可以的。
const EnhanceDemo = withDescription(Geekjc);
隨後,觀察React組件樹發生了什麼變化,如圖所示,可以發現Geekjc組件被HOC組件包裹起來了,符合了高階組件的預期,即組件是層層包裹起來的,如同洋蔥一樣。
但是隨之帶來的問題是,如果這個高階元件被使用了多次,那麼在除錯的時候,將會看到一大堆HOC
,所以這個時候需要做一點小優化,就是在高階組件包覆後,應保留其原有名稱。
我們改寫一下上述的高階元件程式碼,增加了getDisplayName
函數以及靜態屬性displayName
,此時再去觀察DOM Tree
。
function getDisplayName(component) { return component.displayName || component.name || 'Component'; } export default function (WrappedComponent) { return class HOC extends Component { static displayName = `HOC(${getDisplayName(WrappedComponent)})` render() { return <div> <div> 极客教程(https://www.geekjc.com)致力于推广各种编程语言技术,也为了未来数字化世界,让人更容易找到操作数字化的方式,为了未来而生的编程学习平台, 大部分资源是完全免费的,并且会根据当前互联网的变化实时更新本站内容。 </div> <wrappedcomponent></wrappedcomponent> </div> } } }
此時,原本元件的名稱正確地顯示在了DOM Tree上。
這個簡單的例子裡高階元件只做了一件事,那便是為被包裹的元件添加一個標題樣式。這個高階元件可以用到任何一個需要加入此邏輯的元件上,只需要被此高階元件修飾即可。
由此可以看出,高階元件的主要功能是封裝並抽離元件的通用邏輯,讓此部分邏輯在元件間更好地被重複使用。
4. 高階元件的進階用法
4.1 元件參數
還是以上述範例為例,此高階元件只是展示了關於網站的描述,但是為了更好的抽象,此描述應可以被參數化,如下方式調用。@withDescription('欢迎大家访问收藏(https://www.geekjc.com)') export default class Geekjc extends Component { render() { return ( //... ); } }
withDescription需要被改寫成如下形式,它接受一個參數,然後回傳一個高階元件(函數)。
export default function (description) { return function (WrappedComponent) { return class HOC extends Component { render() { return <div> <div> {description ?description : '我是描述'} </div> <wrappedcomponent></wrappedcomponent> </div> } } } }
使用ES6寫法可以更簡潔。
export default(description) => (WrappedComponent) => class HOC extends Component { render() { return <div> <div> {description ? description : '我是描述'} </div> <wrappedcomponent></wrappedcomponent> </div> } }
如圖可以看到,傳入的參數已經反映在DOM Tree裡了。
柯里化 Curry
概念:只傳遞函數的一部分參數來呼叫它,讓它傳回一個函數去處理剩下的參數。
函數簽章:fun(params)(otherParams)
應用:在React裡,透過柯里化,我們可以透過傳入不同的參數來得到不同的高階元件。
4.2 基於屬性代理的方式
屬性代理是最常見的高階元件的使用方式,上述描述的高階元件就是這種方式。它透過做一些操作,將被包裹組件的props和新產生的props一起傳遞給此元件,這稱之為屬性代理。
export default function withHeader(WrappedComponent) { return class HOC extends Component { render() { const newProps = { test:'hoc' } // 透传props,并且传递新的newProps return <p> <wrappedcomponent></wrappedcomponent> </p> } } }
4.3 基於反向繼承的方式
這種方式回傳的React元件繼承了被傳入的元件,所以它能夠存取的區域、權限更多,相較於屬性代理方式,它更像打入組織內部,對其進行修改。具體的可以參考附錄裡提供的連結進行深入學習。
export default function (WrappedComponent) { return class Inheritance extends WrappedComponent { componentDidMount() { // 可以方便地得到state,做一些更深入的修改。 console.log(this.state); } render() { return super.render(); } } }
4.4 组合多个高阶组件
上述高阶组件为React组件增强了一个功能,如果需要同时增加多个功能需要怎么做?这种场景非常常见,例如我既需要增加一个组件标题,又需要在此组件未加载完成时显示Loading。
@withDescription @withLoading class Demo extends Component{ }
使用compose可以简化上述过程,也能体现函数式编程的思想。
const enhance = compose(withHeader,withLoading); @enhance class Demo extends Component{ }组合 Compose
compose可以帮助我们组合任意个(包括0个)高阶函数,例如compose(a,b,c)返回一个新的函数d,函数d依然接受一个函数作为入参,只不过在内部会依次调用c,b,a,从表现层对使用者保持透明。
基于这个特性,我们便可以非常便捷地为某个组件增强或减弱其特征,只需要去变更compose函数里的参数个数便可。
compose函数实现方式有很多种,这里推荐其中一个recompact.compose,详情见下方参考类库,也可以看我之前写的一篇文章reduce与redux中compose函数
5. 与父组件区别
高阶组件作为一个函数,它可以更加纯粹地关注业务逻辑层面的代码,比如数据处理,数据校验,发送请求等,可以改善目前代码里业务逻辑和UI逻辑混杂在一起的现状。父组件则是UI层的东西,我们先前经常把一些业务逻辑处理放在父组件里,这样会造成父组件混乱的情况。为了代码进一步解耦,可以考虑使用高阶组件这种模式。
6. 开源的高阶组件使用
6.1 recompact
recompact提供了一系列使用的高阶组件,可以增强组件的行为,可以利用此库学习高阶组件的写法。
import recompact from 'recompact' import { pure, withProps } from 'recompact' const enhance = recompact.compose( withProps({ className: 'beautiful' }), pure, ) @enhance class Demo extends Component{ }
6.2 React Sortable
通过使用此库提供的高阶组件,可以方便地让列表元素可拖动。
以上是es7中修飾器(Decorator)的詳解(附範例)的詳細內容。更多資訊請關注PHP中文網其他相關文章!

在es6中,可以利用“Array.isArray()”方法判断对象是否为数组,若判断的对象是数组,返回的结果是true,若判断对象不是数组,返回的结果是false,语法为“Array.isArray(需要检测的js对象)”。

es6中遍历跟迭代的区别是:遍历强调的是要把整个数据依次全部取出来,是访问数据结构的所有元素;而迭代虽然也是依次取出数据,但是并不保证取多少,也不保证把所有的数据取完,是遍历的一种形式。

在es6中,可用Object对象的is()方法来判断两个对象是否相等,该方法检测两个变量的值是否为同一个值,判断两个对象的引用地址是否一致,语法“Object.is(对象1,对象2)”;该方法会返回布尔值,若返回true则表示两个对象相等。

转换方法:1、利用“+”给数字拼接一个空字符,语法“数字+""”;2、使用String(),可把对象的值转换为字符串,语法“String(数字对象)”;3、用toString(),可返回数字的字符串表示,语法“数字.toString()”。

在es6中,assign用于对象的合并,可以将源对象的所有可枚举属性复制到目标对象;若目标对象与源对象有同名属性,或多个源对象有同名属性,则后面的属性会覆盖前面的属性,语法为“Object.assign(...)”

改变方法:1、利用splice()方法修改,该方法可以直接修改原数组的内容,语法为“数组.splice(开始位置,修改个数,修改后的值)”;2、利用下标访问数组元素,并重新赋值来修改数组数据,语法为“数组[下标值]=修改后的值;”。

sort排序是es6中的;sort排序是es6中用于对数组的元素进行排序的方法,该方法默认不传参,按照字符编码顺序进行排序,排序顺序可以是字母或数字,并按升序或降序,语法为“array.sort(callback(a,b))”。

在es6中,import as用于将若干export导出的内容组合成一个对象返回;ES6的模块化分为导出与导入两个模块,该方法能够将所有的导出内容包裹到指定对象中,语法为“import * as 对象 from ...”。


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

MantisBT
Mantis是一個易於部署的基於Web的缺陷追蹤工具,用於幫助產品缺陷追蹤。它需要PHP、MySQL和一個Web伺服器。請查看我們的演示和託管服務。

VSCode Windows 64位元 下載
微軟推出的免費、功能強大的一款IDE編輯器

Dreamweaver Mac版
視覺化網頁開發工具

SublimeText3 英文版
推薦:為Win版本,支援程式碼提示!

記事本++7.3.1
好用且免費的程式碼編輯器