Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537
今天的JavaScript世界中,有一股風頭如熱鋒在飛,那就是React,尤其是它強大的Component機制,它在前端開發界以靈活、高效和愉悅的體驗吸引了前端開發者出現一頓風results。今天,讓我們一起看看React中的巔峰之lassmac——組件,并探討一下為何這個功能擁有這么高的相信空間。
一、初識React組件
React是一個開源的JavaScript庫,由Facebook開發,旨在構建用戶界面。React的核心是基于組件的開發模型,組件是獨立且可復用的HTML片段,它們可以傳遞狀態和事件回調,能夠讓應用程序的UI和數據保持同步。
二、React組件的特點
三、React組件的制作方法
讀前建議先了解以下知識點:
如果你對前面提到的先備知識有所了解, 你應該知道 JavaScript 中的函數其實是一種對象。 而作為對象, 函數是可以有方法的, 包括非常強大的 `Apply`, `Call`以及 `Bind` 方法。一方面, Apply 方法和 Call 方法的用途幾乎相同, 在 JavaScript 中被頻繁使用于方法借用和明確 this 關鍵字指向等場景. 我們也將 Apply 用于參數可變的函數; 在后文中你將會對此有更多的了解。 另一方面, 我們會使用 Bind 來給方法指定 this 指向值或者函數柯里化 (currying functions)。
我們會討論在 JavaScript 中使用這三種方法的每一個場景。 Apply 和 Call 方法是在 ECMAScript 3 標準中出現的(可以在IE 6, 7, 8 以及現代瀏覽器中使用), ECAMScript 5 標準添加了 Bind 這個方法。由于三種方法都非常強大, 開發時你一定會用到其中一個。讓我們先從 Bind 方法說起。
Bind 方法
我們用 Bind() 來實現在指明函數內部 this 指向的情況下去調用該函數, 換句話說, bind() 允許我們非常簡單的在函數或者方法被調用時綁定 this 到指定對象上。
當我們在一個方法中用到了 this, 而這個方法調用于一個接收器對象, 我們會需要使用到 bind() 方法; 在這種情況下, 由于 this 不一定完全如我們所期待的綁定在目標對象上, 程序有時便會出錯。
Bind 允許我們明確指定方法中的 this 指向
當以下按鈕被點擊的時候, 文本輸入框會被隨機填入一個名字。
```
// <button>Get Random Person</button>
// <input type="text">
var user={
data :[
{name:"T. Woods", age:37},
{name:"P. Mickelson", age:43}
],
clickHandler:function(event) {
var randomNum=((Math.random () * 2 | 0) + 1) - 1; // random number between 0 and 1
// 從 data 數組中隨機選取一個名字填入 input 框內
$("input").val(this.data[randomNum].name + " " + this.data[randomNum].age);
}
}
// 給點擊事件添加一個事件處理器
$("button").click(user.clickHandler);
```
當你點擊按鈕時, 會發現一個報錯信息: 因為 clickHandler() 方法中的 this 綁定的是按鈕 HTML 內容的上下文, 因為這才是 clickHandler 方法的執行時的調用對象。
在 JavaScript 中這種問題比較常見, JavaScript 框架中例如 Backbone.js, jQuery 都自動為我們做好了綁定的工作, 所以在使用時 this 總是可以綁定到我們所期望的那個對象上。
為了解決之前例子中存在的問題, 我們利用 bind() 方法將 `$("button").click(user.clickHandler);` 換成以下形式:
```
$("button").click(user.clickHandler.bind(user));
```
Tips: 再考慮另一個方法來修復 this 的值: 你可以給 click() 方法傳遞一個匿名回調函數, jQuery 會將匿名函數的 this 綁定到按鈕對象上.bind() 函數在 ECMA-262 第五版才被加入;它可能無法在所有瀏覽器上運行。你可以部份地在腳本開頭加入以下代碼,就能使它運作,讓不支持的瀏覽器也能使用 bind() 功能。
```
if (!Function.prototype.bind) {
Function.prototype.bind=function(oThis) {
if (typeof this !=="function") {
// closest thing possible to the ECMAScript 5
// internal IsCallable function
throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
}
var aArgs=Array.prototype.slice.call(arguments, 1),
fToBind=this, // 此處的 this 指向目標函數
fNOP=function() {},
fBound=function() {
return fToBind.apply(this instanceof fNOP
? this // 此處 this 為 調用 new obj() 時所生成的 obj 本身
: oThis || this, // 若 oThis 無效則將 fBound 綁定到 this
// 將通過 bind 傳遞的參數和調用時傳遞的參數進行合并, 并作為最終的參數傳遞
aArgs.concat(Array.prototype.slice.call(arguments)));
};
// 將目標函數的原型對象拷貝到新函數中,因為目標函數有可能被當作構造函數使用
fNOP.prototype=this.prototype;
fBound.prototype=new fNOP();
return fBound;
};
}
```
繼續之前的例子, 如果我們將包含 this 的方法賦值給一個變量, 那么 this 的指向也會綁定到另一個對象上, 如下所示:
```
// 全局變量 data
var data=[
{name:"Samantha", age:12},
{name:"Alexis", age:14}
]
var user={
// 局部變量 data
data :[
{name:"T. Woods", age:37},
{name:"P. Mickelson", age:43}
],
showData:function(event) {
var randomNum=((Math.random () * 2 | 0) + 1) - 1; // random number between 0 and 1
console.log(this.data[randomNum].name + " " + this.data[randomNum].age);
}
}
// 將 user 對象的 showData 方法賦值給一個變量
var showDataVar=user.showData;
showDataVar(); // Samantha 12 (來自全局變量數組而非局部變量數組)
```
當我們執行 showDataVar() 函數時, 輸出到 console 的數值來自全局 data 數組, 而不是 user 對象. 這是因為 showDataVar() 函數是被當做一個全局函數執行的, 所以在函數內部 this 被綁定位全局對象, 即瀏覽器中的 window 對象。
來, 我們用 bind 方法來修復這個 bug。
```
// Bind the showData method to the user object
var showDataVar=user.showData.bind(user);
```
Bind 方法允許我們實現函數借用
在 JavaScript 中, 我們可以傳遞函數, 返回函數, 借用他們等等, 而 bind() 方法使函數借用變得極其簡單. 以下為一個函數借用的例子:
```
// cars 對象
var cars={
data:[
{name:"Honda Accord", age:14},
{name:"Tesla Model S", age:2}
]
}
// 我們從之前定義的 user 對象借用 showData 方法
// 這里我們將 user.showData 方法綁定到剛剛新建的 cars 對象上
cars.showData=user.showData.bind(cars);
cars.showData(); // Honda Accord 14
```
這里存在一個問題, 當我們在 cars 對象上添加一個新方法(showData)時我們可能不想只是簡單的借用一個函數那樣, 因為 cars 本身可能已經有一個方法或者屬性叫做 showData 了, 我們不想意外的將這個方法覆蓋了. 正如在之后的 *Apply 和 Call 方法* 章節我們會介紹, 借用函數的最佳實踐應該是使用 Apply 或者 Call 方法。
Bind 方法允許我們柯里化一個函數
柯里化的概念很簡單, 只傳遞給函數一部分參數來調用它, 讓它返回一個函數去處理剩下的參數. 你可以一次性地調用 curry 函數, 也可以每次只傳一個參數分多次調用, 以下為一個簡單的示例. - [JS 函數是編程指南 第 4 章: 柯里化(curry)](https://llh911001.gitbooks.io/mostly-adequate-guide-chinese/content/ch4.html)
```
var add=function(x) {
return function(y) {
return x + y;
};
};
var increment=add(1);
var addTen=add(10);
increment(2);
// 3
addTen(2);
// 12
```
現在, 我們使用 bind() 方法來實現函數的柯里化. 我們首先定義一個接收三個參數的 greet() 函數:
```
function greet(gender, age, name) {
// if a male, use Mr., else use Ms.
var salutation=gender==="male" ? "Mr. " : "Ms. ";
if (age > 25) {
return "Hello, " + salutation + name + ".";
}
else {
return "Hey, " + name + ".";
}
}
```
接著我們使用 bind() 方法柯里化 greet() 方法. bind() 接收的第一個參數指定了 this 的值:
```
// 在 greet 函數中我們可以傳遞 null, 因為函數中并未使用到 this 關鍵字
var greetAnAdultMale=greet.bind (null, "male", 45);
greetAnAdultMale("John Hartlove"); // "Hello, Mr. John Hartlove."
var greetAYoungster=greet.bind(null, "", 16);
greetAYoungster("Alex"); // "Hey, Alex."
greetAYoungster("Emma Waterloo"); // "Hey, Emma Waterloo."
```
當我們用 bind() 實現柯里化時, greet() 函數參數中除了最后一個參數都被預定義好了, 所以當我們調用柯里化后的新函數時只需要指定最后一位參數。
所以小結一下, bind() 方法允許我們明確指定對象方法中的 this 指向, 我們可以借用, 復制一個方法或者將方法賦值為一個可作為函數執行的變量. 我們以可以借用 bind 實現函數柯里化。
JavaScript 中的 Apply 和 Call 方法
作為 JavaScript 中最常用的兩個函數方法, apply 和 call 允許我們借用函數以及在函數調用中指定 this 指向. 除此外, apply 函數允許我們在執行函數時傳入一個參數數組, 以此使函數在執行可變參數的函數時可以將每個參數單獨的傳入函數并得到處理。
使用 apply 或者 call 設置 this
當我們使用 apply 或者 call 時, 傳入的第一個參數為目標函數中 this 指向的對象, 以下為一個簡單的例子:
```
// 全局變量
var avgScore="global avgScore";
// 全局函數
function avg(arrayOfScores) {
// 分數相加并返回結果
var sumOfScores=arrayOfScores.reduce(function(prev, cur, index, array) {
return prev + cur;
});
// 這里的 "this" 會被綁定到全局對象上, 除非使用 Call 或者 Apply 明確指定 this 的指向
this.avgScore=sumOfScores / arrayOfScores.length;
}
var gameController={
scores :[20, 34, 55, 46, 77],
avgScore:null
}
// 調用 avg 函數, this 指向 window 對象
avg(gameController.scores);
// 證明 avgScore 已經被設置為 window 對象的屬性
console.log(window.avgScore); // 46.4
console.log(gameController.avgScore); // null
// 重置全局變量
avgScore="global avgScore";
// 使用 call() 方法明確將 "this" 綁定到 gameController 對象
avg.call(gameController, gameController.scores);
console.log(window.avgScore); // 全局變量 avgScore 的值
console.log(gameController.avgScore); // 46.4
```
以上例子中 call() 中的第一個參數明確了 this 的指向, 第二參數被傳遞給了 avg() 函數.
apply 和 call 的用法幾乎相同, 唯一的差別在于當函數需要傳遞多個變量時, apply 可以接受一個數組作為參數輸入, call 則是接受一系列的單獨變量.。
在回掉函數中用 call 或者 apply 設置 this
以下為一個例子, 這種做法允許我們在執行 callback 函數時能夠明確 其內部的 this 指向
```
// 定義一個方法
var clientData={
id: 094545,
fullName: "Not Set",
// clientData 對象中的一個方法
setUserName: function (firstName, lastName) {
this.fullName=firstName + " " + lastName;
}
}
function getUserInput(firstName, lastName, callback, callbackObj) {
// 使用 apply 方法將 "this" 綁定到 callbackObj 對象
callback.apply(callbackObj, [firstName, lastName]);
}
```
如下樣例中傳遞給 callback 函數 中的參數將會在 clientData 對象中被設置/更新。
```
getUserInput("Barack", "Obama", clientData.setUserName, clientData);
console.log(clientData.fullName); // Barack Obama
```
使用 Apply 或者 Call 借用函數(必備知識)
相比 bind 方法, 我們使用 apply 或者 call 方法實現函數借用能夠有很大的施展空間. 接下來我們考慮從 Array 中借用方法的問題, 讓我們定義一個**類數組對象(array-like object)**然后從數組中借用方法來處理我們定義的這個對象, 不過在這之前請記住我們要操作的是一個對象, 而不是數組;
```
// An array-like object: note the non-negative integers used as keys
var anArrayLikeObj={0:"Martin", 1:78, 2:67, 3:["Letta", "Marieta", "Pauline"], length:4 };
```
接下來我們可以這樣使用數組的原生方法:
```
// Make a quick copy and save the results in a real array:
// First parameter sets the "this" value
var newArray=Array.prototype.slice.call(anArrayLikeObj, 0);
console.log(newArray); // ["Martin", 78, 67, Array[3]]
// Search for "Martin" in the array-like object
console.log(Array.prototype.indexOf.call(anArrayLikeObj, "Martin")===-1 ? false : true); // true
// Try using an Array method without the call () or apply ()
console.log(anArrayLikeObj.indexOf("Martin")===-1 ? false : true); // Error: Object has no method 'indexOf'
// Reverse the object:
console.log(Array.prototype.reverse.call(anArrayLikeObj));
// {0: Array[3], 1: 67, 2: 78, 3: "Martin", length: 4}
// Sweet. We can pop too:
console.log(Array.prototype.pop.call(anArrayLikeObj));
console.log(anArrayLikeObj); // {0: Array[3], 1: 67, 2: 78, length: 3}
// What about push?
console.log(Array.prototype.push.call(anArrayLikeObj, "Jackie"));
console.log(anArrayLikeObj); // {0: Array[3], 1: 67, 2: 78, 3: "Jackie", length: 4}
```
這樣的操作使得我們定義的對象既保留有所有對象的屬性, 同時也能夠在對象上使用數組方法.。
**arguments** 對象是所有 JavaScript 函數中的一個類數組對象, 因此 call() 和 apply() 的一個最常用的用法是從 arguments 中提取參數并將其傳遞給一個函數。
以下為 Ember.js 源碼中的一部分, 加上了我的一些注釋:
```
function transitionTo(name) {
// 因為 arguments 是一個類數組對象, 所以我們可以使用 slice()來處理它
// 參數 "1" 表示我們返回一個從下標為1到結尾元素的數組
var args=Array.prototype.slice.call(arguments, 1);
// 添加該行代碼用于查看 args 的值
console.log(args);
// 注釋本例不需要使用到的代碼
//doTransition(this, name, this.updateURL, args);
}
// 使用案例
transitionTo("contact", "Today", "20"); // ["Today", "20"]
```
以上例子中, args 變量是一個真正的數組. 從以上案例中我們可以寫一個得到快速得到傳遞給函數的所有參數(以數組形式)的函數:
```
function doSomething() {
var args=Array.prototype.slice.call(arguments);
console.log(args);
}
doSomething("Water", "Salt", "Glue"); // ["Water", "Salt", "Glue"]
```
考慮到字符串是不可變的, 如果使用 apply 或者 call 方法借用字符串的方法, 不可變的數組操作對他們來說才是有效的, 所以你不能使用類似 reverse 或者 pop 等等這類的方法. 除此外, 我們也可以用他們借用我們自定義的方法。
```
var gameController={
scores :[20, 34, 55, 46, 77],
avgScore:null,
players :[
{name:"Tommy", playerID:987, age:23},
{name:"Pau", playerID:87, age:33}
]
}
var appController={
scores :[900, 845, 809, 950],
avgScore:null,
avg :function() {
var sumOfScores=this.scores.reduce(function(prev, cur, index, array) {
return prev + cur;
});
this.avgScore=sumOfScores / this.scores.length;
}
}
// Note that we are using the apply() method, so the 2nd argument has to be an array
appController.avg.apply(gameController);
console.log(gameController.avgScore); // 46.4
// appController.avgScore is still null; it was not updated, only gameController.avgScore was updated
console.log(appController.avgScore); // null
```
這個例子非常簡單, 我們定義的 gameController 對象借用了 appController 對象的 avg() 方法. 你也許會想, 如果我們借用的函數定義發生了變化, 那么我們的代碼會發生什么變化. 借用(復制后)的函數也會變化么, 還是說他在完整復制后已經和原始的方法切斷了聯系? 讓我們用下面這個小例子來說明這個問題:
```
appController.maxNum=function() {
this.avgScore=Math.max.apply(null, this.scores);
}
appController.maxNum.apply(gameController, gameController.scores);
console.log(gameController.avgScore); // 77
```
正如我們所期望的那樣, 如果我們修改原始的方法, 這樣的變化會在借用實例的方法上體現出來. 我們總是希望如此, 因為我們從來不希望完整的復制一個方法, 我們只是想簡單的借用一下.。
使用 apply() 執行參數可變的函數
關于 Apply, Call 和 Bind 方法的多功能性和實用性, 我們將討論一下Apply方法的一個很簡單的功能: 使用參數數組執行函數。
Math.max() 方法是 JavaScript 中一個常見的參數可變函數:
```
console.log(Math.max(23, 11, 34, 56)); // 56
```
但如果我們有一個數組要傳遞給 Math.max(), 是不能這樣做的:
```
var allNumbers=[23, 11, 34, 56];
console.log(Math.max(allNumbers)); // NaN
```
使用 apply 我們可以像下面這樣傳遞數組:
```
var allNumbers=[23, 11, 34, 56];
console.log(Math.max.apply(null, allNumbers)); // 56
```
正如之前討論, apply() 的第一個參數用于設置 this 的指向, 但是 Math.max() 并未使用到 this, 所以我們傳遞 null 給他。
為了更進一步解釋 apply() 在 參數可變函數上的能力, 我們自定義了一個參數可變函數:
```
var students=["Peter Alexander", "Michael Woodruff", "Judy Archer", "Malcolm Khan"];
// 不定義參數, 因為我們可以傳遞任意多個參數進入該函數
function welcomeStudents() {
var args=Array.prototype.slice.call(arguments);
var lastItem=args.pop();
console.log("Welcome " + args.join (", ") + ", and " + lastItem + ".");
}
welcomeStudents.apply(null, students);
// Welcome Peter Alexander, Michael Woodruff, Judy Archer, and Malcolm Khan.
```
區別與注意事項
三個函數存在的區別, 用一句話來說的話就是: bind是返回對應函數, 便于稍后調用; apply, call則是立即調用. 除此外, 在 ES6 的箭頭函數下, call 和 apply 的失效, 對于箭頭函數來說:
更多關于箭頭函數的介紹在這里就不做過多介紹了, 詳情可以查看 [Arrow functions](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Functions/Arrow_functions).
總結
Call, Apply 和 Bind 在設置 this 指向, 聲稱與執行參數可變函數以及函數借用方面的強大之處已經非常明顯. 作為一名 JavaScript 開發者, 你一定會經常見到這種用法, 或者在開發中嘗試使用他. 請確保你已經很好的了解了如上所述的概念與用法.
原文鏈接:
http://javascriptissexy.com/javascript-apply-call-and-bind-methods-are-essential-for-javascript-professionals
們知道在 HTML 打開鏈接的方法就是定義 href
如下面的代碼:
<div>
<a href="javascript: void(0);" @click="openBlogDetail(item.id)" class="text-primary">Read
more <i class="mdi mdi-arrow-right"></i></a>
</div>
但是,有時候我們是希望通過單擊鏈接的方法調用一個 JS 函數。
上面就是定義了一個調用函數,并且傳遞 ID 到函數中。
在函數中打開鏈接的方法是:
window.open("/blog/detail/" + id, "_self")
需要注意的是,上面的打開方法我們添加了一個參數 _self。
這個參數的意思是在當前的標簽頁中打開。
按照現代瀏覽器的配置,如果不添加這個參數,將會把需要打開的鏈接在一個新的標簽頁中打開。
使用上面的方法,直接調用就可以了。
*請認真填寫需求信息,我們會在24小時內與您取得聯系。