React高階組件
在開始聊高階組件之前我們先來看下高階函數(shù)和高階組件在形式上的差異:
一换薄、什么是裝飾器模式:
要想正確理解設(shè)計模式,首先必須明確它是為了解決什么問題而提出來的植锉。
那裝飾器模式到底是為了解決什么問題呢啥酱?在我們的開發(fā)過程中我們會為了一些通用功能在多個不同的組件、接口或者類中使用竖般,這個時候我們這些功能寫到每個組件咒唆、接口或者類中届垫,但是這樣非常不利于維護。
裝飾器就是為了解決在不修改原本組件全释、接口或者類的時候為其添加額外的功能装处。
從本質(zhì)上看裝飾器模式是一個包裝模式((Wrapper Pattern),它是通過封裝其他對象達到設(shè)計的目的的浸船。
react的高階組件本質(zhì)也是裝飾器模式妄迁,以后會有一篇文章會專門詳解react的高階組件。
理解了裝飾器的能解決了什么問題李命,那我們一遍在什么情況下考慮使用裝飾器模式呢登淘?我的理解是:
- 需要擴展一個類,為這個類附加一個方法或者屬性的時候封字;
- 需要修改一個類的功能黔州,或者重構(gòu)這個類中的某個方法耍鬓;
二、裝飾器的基本概念:
上面我們解釋了什么是裝飾器模式流妻,下面我們將要ES7的裝飾器的一些基本概念:如何定義裝飾器牲蜀、裝飾器執(zhí)行時機、裝飾器類型绅这。
1涣达、如何定義裝飾器
裝飾器本質(zhì)是一個函數(shù),可以分為帶參數(shù)和不帶參數(shù)(也叫裝飾器工廠)证薇,裝飾器使用 @expression這種形式度苔,expression求值后必須為一個函數(shù),它會在運行時被調(diào)用浑度,被裝飾的聲明信息做為參數(shù)傳入林螃。
@Test()
class Hello {}
function Test(target:any) {
console.log("I am decorator.")
}
2、裝飾器執(zhí)行時機
修飾器對類的行為的改變俺泣,是代碼編譯時發(fā)生的(不是TypeScript編譯,而是js在執(zhí)行機中編譯階段)完残,而不是在運行時伏钠。這意味著,修飾器能在編譯階段運行代碼谨设。也就是說熟掂,修飾器本質(zhì)就是編譯時執(zhí)行的函數(shù)
3、裝飾器的類型
類裝飾器扎拣、屬性裝飾器赴肚、方法裝飾器、參數(shù)裝飾器
三二蓝、裝飾器類型:
- 類修飾器
類裝飾器一般主要應(yīng)用于類構(gòu)造函數(shù)誉券,可以監(jiān)視、修改刊愚、替換類的定義踊跟,裝飾器用來裝飾類的時候。裝飾器函數(shù)的第一個參數(shù)鸥诽,就是所要裝飾的目標類本身商玫。
a、添加靜態(tài)屬性或方法
@Test()
class Hello {}
function Test(target) {
target.a = 1;
}
let o = new Hello();
console.log(o.a) ==>1
b牡借、添加實例屬性或方法
@Test()
class Hello {}
function Test(target) {
target.prototype.a = 1;
target.prototype.f = function(){
console.log("新增加方法")
};
}
let o = new Hello();
o.f() ==>"新增加方法"
console.log(o.a) ==>1
c拳昌、裝飾器工廠(函數(shù)柯里化)
很多文章說裝飾器工廠是閉包,其實不準確钠龙,關(guān)于高階函數(shù)炬藤、閉包御铃、函數(shù)柯里化可以參考:
理解運用JS的閉包、高階函數(shù)刻像、柯里化
@Test('hello')
class Hello {}
function Test(str) {
return function(){
target.prototype.a = str;
target.prototype.f = function(){
console.log(str)
};
}
}
let o = new Hello();
o.f() ==>"hello"
console.log(o.a) ==>"hello"
d畅买、重載構(gòu)造函數(shù)
@Test('hello')
class Hello {
constructor(){
this.a= 1
}
f(){
console.log('我是原始方法',this.a)
}
}
function Test(target) {
return class extends target{
f(){
console.log('我是裝飾器方法',this.a)
}
}
}
let o = new Hello();
o.f() ==>"我是裝飾器方法",1
- 方法裝飾器
它會被應(yīng)用到方法的 屬性描述符上,可以用來監(jiān)視细睡,修改或者替換方法定義谷羞。
方法裝飾會在運行時傳入下列3個參數(shù):
- target:對于靜態(tài)成員來說是類的構(gòu)造函數(shù),對于實例成員是類的原型對象溜徙。
- key: 成員的名字湃缎。
- descriptor:成員的屬性描述符。
descriptor:
// {
// value: specifiedFunction,
// enumerable: false,
// configurable: true,
// writable: true
// };
方法裝飾器最后必須返回descriptor
function nameDecorator(target, key, descriptor) {
descriptor.value = () => {
return 'jake';
}
return descriptor;
}
class Person {
constructor() {
this.name = 'jake'
}
@nameDecorator
getName() {
return this.name;
}
}
let p1 = new Person();
console.log(p1.getName())
另外一個經(jīng)典例子
class Math {
@log
add(a, b) {
return a + b;
}
}
function log(target, name, descriptor) {
var oldValue = descriptor.value;
descriptor.value = function(...list) {
console.log(list);
console.log(`Calling ${name} with`, ...list);
return oldValue.call(this, ...list);
};
return descriptor;
}
const math = new Math();
// passed parameters should get logged now
let result = math.add(2, 4);
console.log(result)