摘要:寫在前面中的作用域和上下文是這門語言的獨(dú)到之處,每個(gè)函數(shù)有不同的變量上下文和作用域。不可以當(dāng)作構(gòu)造函數(shù),也就是說,不可以使用命令,否則會(huì)拋出一個(gè)錯(cuò)誤。正是因?yàn)樗鼪]有,所以也就不能用作構(gòu)造函數(shù)。
寫在前面
JavaScript中的作用域scope 和上下文 context 是這門語言的獨(dú)到之處,每個(gè)函數(shù)有不同的變量上下文和作用域。這些概念是JavaScript中一些強(qiáng)大的設(shè)計(jì)模式的后盾。在ES5規(guī)范里,我們可以遵循一個(gè)原則——每個(gè)function內(nèi)的上下文this指向該function的調(diào)用方。比如:
var Module = { name: "Jafeney", first: function() { console.log(this); // this對(duì)象指向調(diào)用該方法的Module對(duì)象 var second = (function() { console.log(this) // 由于變量提升,this對(duì)象指向Window對(duì)象 })() }, init: function() { this.first() } } Module.init()
但是,在ES6規(guī)范中,出現(xiàn)了一個(gè)逆天的箭頭操作符 => ,它可以替代原先ES5里function的作用,快速聲明函數(shù)。那么,在沒有了function關(guān)鍵字,箭頭函數(shù)內(nèi)部的上下文this是怎樣一種情況呢?
ES6 中的箭頭函數(shù)在阮一峰老師的《ECMAScript 6 入門》 中,對(duì)箭頭函數(shù)的做了如下介紹:
箭頭函數(shù)的基本介紹ES6 允許使用“箭頭”=> 定義函數(shù)。
var f = v => v; //上面的箭頭函數(shù)等同于: var f = function(v) { return v; };
如果箭頭函數(shù)不需要參數(shù)或需要多個(gè)參數(shù),就使用一個(gè)圓括號(hào)代表參數(shù)部分
var f = () => 5; // 等同于 var f = function () { return 5 }; var sum = (num1, num2) => num1 + num2; // 等同于 var sum = function(num1, num2) { return num1 + num2; };
如果箭頭函數(shù)的代碼塊部分多于一條語句,就要使用大括號(hào)將它們括起來,并且使用return語句返回(重要)
var sum = (num1, num2) => { return num1 + num2; }
由于大括號(hào)被解釋為代碼塊,所以如果箭頭函數(shù)直接返回一個(gè)對(duì)象,必須在對(duì)象外面加上括號(hào)(重要)
var getTempItem = id => ({ id: id, name: "Temp" });
箭頭函數(shù)可以與變量解構(gòu)結(jié)合使用
const full = ({ first, last }) => first + " " + last; // 等同于 function full(person) { return person.first + " " + person.last; }
箭頭函數(shù)使得表達(dá)更加簡潔
const isEven = n => n % 2 == 0; const square = n => n * n;
上面代碼只用了兩行,就定義了兩個(gè)簡單的工具函數(shù)。如果不用箭頭函數(shù),可能就要占用多行,而且還不如現(xiàn)在這樣寫醒目。
箭頭函數(shù)的一個(gè)用處是簡化回調(diào)函數(shù)
// 正常函數(shù)寫法 [1,2,3].map(function (x) { return x * x; }); // 箭頭函數(shù)寫法 [1,2,3].map(x => x * x);箭頭函數(shù)使用注意點(diǎn)
函數(shù)體內(nèi)的this對(duì)象,就是定義時(shí)所在的對(duì)象,而不是使用時(shí)所在的對(duì)象。
不可以當(dāng)作構(gòu)造函數(shù),也就是說,不可以使用new命令,否則會(huì)拋出一個(gè)錯(cuò)誤。
不可以使用arguments對(duì)象,該對(duì)象在函數(shù)體內(nèi)不存在。如果要用,可以用Rest參數(shù)代替。
不可以使用yield命令,因此箭頭函數(shù)不能用作Generator函數(shù)。
this 指向固定化ES5規(guī)范中,this對(duì)象的指向是可變的,但是在ES6的箭頭函數(shù)中,它卻是固定的。
function foo() { setTimeout(() => { console.log("id:", this.id); }, 100); } var id = 21; foo.call({ id: 42 }); // 輸出 id: 42
箭頭函數(shù)的原理注意:上面代碼中,setTimeout的參數(shù)是一個(gè)箭頭函數(shù),這個(gè)箭頭函數(shù)的定義生效是在foo函數(shù)生成時(shí),而它的真正執(zhí)行要等到100毫秒后。如果是普通函數(shù),執(zhí)行時(shí)this應(yīng)該指向全局對(duì)象window,這時(shí)應(yīng)該輸出21。但是,箭頭函數(shù)導(dǎo)致this總是指向函數(shù)定義生效時(shí)所在的對(duì)象(本例是{id: 42}),所以輸出的是42。
this指向的固定化,并不是因?yàn)榧^函數(shù)內(nèi)部有綁定this的機(jī)制,實(shí)際原因是箭頭函數(shù)根本沒有自己的this,導(dǎo)致內(nèi)部的this就是外層代碼塊的this。正是因?yàn)樗鼪]有this,所以也就不能用作構(gòu)造函數(shù)。所以,箭頭函數(shù)轉(zhuǎn)成ES5的代碼如下:
// ES6 function foo() { setTimeout(() => { console.log("id:", this.id); }, 100); } // ES5 function foo() { var _this = this; setTimeout(function () { console.log("id:", _this.id); }, 100); }
兩道經(jīng)典的面試題上面代碼中,轉(zhuǎn)換后的ES5版本清楚地說明了,箭頭函數(shù)里面根本沒有自己的this,而是引用外層的this。
// 請(qǐng)問下面有幾個(gè)this function foo() { return () => { return () => { return () => { console.log("id:", this.id); }; }; }; } var f = foo.call({id: 1}); var t1 = f.call({id: 2})()(); // 輸出 id: 1 var t2 = f().call({id: 3})(); // 輸出 id: 1 var t3 = f()().call({id: 4}); // 輸出 id: 1
上面代碼之中,其實(shí)只有一個(gè)this,就是函數(shù)foo的this,所以t1、t2、t3都輸出同樣的結(jié)果。因?yàn)樗械膬?nèi)層函數(shù)都是箭頭函數(shù),都沒有自己的this,它們的this其實(shí)都是最外層foo函數(shù)的this。另外,由于箭頭函數(shù)沒有自己的this,所以也不能用call()、apply()、bind()這些方法去改變this的指向。
// 請(qǐng)問下面代碼執(zhí)行輸出什么 (function() { return [ (() => this.x).bind({ x: "inner" })() ]; }).call({ x: "outer" });
函數(shù)綁定 ::上面代碼中,箭頭函數(shù)沒有自己的this,所以bind方法無效,內(nèi)部的this指向外部的this。所以上面的代碼最終輸出 ["outer"]。
箭頭函數(shù)可以綁定this對(duì)象,大大減少了顯式綁定this對(duì)象的寫法(call、apply、bind)。但是,箭頭函數(shù)并不適用于所有場(chǎng)合,所以ES7提出了“函數(shù)綁定”(function bind)運(yùn)算符,用來取代call、apply、bind調(diào)用。雖然該語法還是ES7的一個(gè)提案,但是Babel轉(zhuǎn)碼器已經(jīng)支持。
函數(shù)綁定運(yùn)算符是并排的兩個(gè)雙冒號(hào)(::),雙冒號(hào)左邊是一個(gè)對(duì)象,右邊是一個(gè)函數(shù)。該運(yùn)算符會(huì)自動(dòng)將左邊的對(duì)象,作為上下文環(huán)境(即this對(duì)象),綁定到右邊的函數(shù)上面。
foo::bar; // 等同于 bar.bind(foo); foo::bar(...arguments); // 等同于 bar.apply(foo, arguments); const hasOwnProperty = Object.prototype.hasOwnProperty; function hasOwn(obj, key) { return obj::hasOwnProperty(key); }
如果雙冒號(hào)左邊為空,右邊是一個(gè)對(duì)象的方法,則等于將該方法綁定在該對(duì)象上面。
var method = obj::obj.foo; // 等同于 var method = ::obj.foo; let log = ::console.log; // 等同于 var log = console.log.bind(console);
由于雙冒號(hào)運(yùn)算符返回的還是原對(duì)象,因此可以采用鏈?zhǔn)綄懛ā?/p>
// 例一 import { map, takeWhile, forEach } from "iterlib"; getPlayers() ::map(x => x.character()) ::takeWhile(x => x.strength > 100) ::forEach(x => console.log(x)); // 例二 let { find, html } = jake; document.querySelectorAll("div.myClass") ::find("p") ::html("hahaha");React 中的各種 this
目前React的編寫風(fēng)格已經(jīng)全面地啟用了ES6和部分ES7規(guī)范,所以很多ES6的坑在React里一個(gè)個(gè)浮現(xiàn)了。本篇重點(diǎn)介紹 this,也是近期跌得最疼的一個(gè)。
Component 方法內(nèi)部的 this還是用具體的例子來解釋吧,下面是我 Royal 項(xiàng)目里一個(gè)Table組件(Royal正在開發(fā)中,歡迎fork貢獻(xiàn)代碼 ^_^)
import React, { Component } from "react" import Checkbox from "../../FormControls/Checkbox/" import "./style.less" class Table extends Component { constructor(props) { super(props) this.state = { dataSource: props.dataSource || [], columns: props.columns || [], wrapClass: props.wrapClass || null, wrapStyle: props.wrapStyle || null, style: props.style || null, className: props.className || null, } this.renderRow = props.renderRow || null } onSelectAll() { for (let ref in this.refs) { if (ref!=="selectAll") { this.refs[ref].setState({checked:true}) } } } offSelectAll() { for (let ref in this.refs) { if (ref!=="selectAll") { this.refs[ref].setState({checked:false}) } } } _renderHead() { return this.state.columns.map((item,i) => { return [{i===0? ] }) } _renderBody() { let _renderRow = this.renderRow; return this.state.dataSource.map((item) => { return _renderRow && _renderRow(item) }) } render() { let state = this.state; return (this.onSelectAll()} onCancel={()=>this.offSelectAll()} />:""}{item.title} ) } } export default Table
{this._renderHead()} {this._renderBody()}
Component是React內(nèi)的一個(gè)基類,用于繼承和創(chuàng)建React自定義組件。ES6規(guī)范下的面向?qū)ο髮?shí)現(xiàn)起來非常精簡,class關(guān)鍵字 可以快速創(chuàng)建一個(gè)類,而Component類內(nèi)的所有屬性和方法均可以通過this訪問。換而言之,在Component內(nèi)的任意方法內(nèi),可以通過this.xxx的方式調(diào)用該Component的其他屬性和方法。
接著分析上面的代碼,寥寥幾行實(shí)現(xiàn)的是對(duì)一個(gè)Table組件的封裝,借鑒了ReactNative組件的設(shè)計(jì)思路,通過外部傳遞dataSource(數(shù)據(jù)源)、columns(表格的表頭項(xiàng))、renderRow(當(dāng)行渲染的模板函數(shù))來完成一個(gè)Table的構(gòu)建,支持全選和取消全選的功能、允許外部傳遞className和style對(duì)象來修改樣式。
container 調(diào)用 component 時(shí)傳遞的 this從這個(gè)例子我們可以發(fā)現(xiàn):只要不采用function定義函數(shù),Component所有方法內(nèi)部的this對(duì)象始終指向該類自身。
還是繼續(xù)上面的例子,下面在一個(gè)做為Demo的container里調(diào)用之前 的Table。
import Table from "../../components/Views/Table/"
接著編寫renderRow函數(shù)并傳遞給Table組件
_renderRow(row) { // ------------ 注意:這里對(duì)callback函數(shù)的寫法 ----------- let onEdit = (x)=> { console.log(x+x) }, onDelete = (x)=> { console.log(x*x) } // --------------------------------------------------- return () } //... 省略一大堆代碼 render() { let dataSource = [{ key: "1", name: "胡彥斌", age: 32, birthday: "2016-12-29", job: "前端工程師", address: "西湖區(qū)湖底公園1號(hào)" }, { key: "2", name: "胡彥祖", age: 42, birthday: "2016-12-29", job: "前端工程師", address: "西湖區(qū)湖底公園1號(hào)" }],columns = [{ title: "編號(hào)", dataIndex: "key", key: "key", },{ title: "姓名", dataIndex: "name", key: "name", }, { title: "年齡", dataIndex: "age", key: "age", }, { title: "生日", dataIndex: "birthday", key: "birthday", }, { title: "職務(wù)", dataIndex: "job", key: "job", },{ title: "住址", dataIndex: "address", key: "address", }, { title: "操作", dataIndex: "operate", key: "operate", }]; return ( {row.key} {row.name} {row.age} {row.birthday} {row.job} {row.address} ); }
顯示效果如下:
分析上面的代碼,有幾處容易出錯(cuò)的地方:
_renderRow 作為component的方法來定義,然后在對(duì)應(yīng)的render函數(shù)內(nèi)通過this來調(diào)用。很重要的一點(diǎn),這里this._renderRow作為的是函數(shù)名方式傳遞。
_renderRow 內(nèi)部Button組件的callback是按鈕點(diǎn)擊后觸發(fā)的回調(diào),也是一個(gè)函數(shù),但是這個(gè)函數(shù)沒有像上面一樣放在component的方法里定義,而是作為一個(gè)變量定義并通過匿名函數(shù)的方式傳遞給子組件:
let onEdit = (x)=> { console.log(x+x) } // ..... callback={()=>onEdit(row.key)}這樣就避開了使用this時(shí)上下文變化的問題。這一點(diǎn)是很講究的,如果沿用上面的寫法很容易這樣寫:
onEdit(x) { console.log(x+x) } // ... callback={()=>this.onEdit(row.key)}但是很遺憾,這樣寫this傳遞到子組件后會(huì)變成undefined,從而報(bào)錯(cuò)。
父組件如要調(diào)用子組件的方法,有兩種方式:
第一種 通過匿名函數(shù)的方式
callback = {()=>this.modalShow()}
第二種 使用 bind
callback = {this.modalShow.bind(this)}參考注意:如果要綁定的函數(shù)需要傳參數(shù),可以這么寫: xxx.bind(this,arg1,arg2...)
《ECMAScript 6 入門》
@歡迎關(guān)注我的 github 和 個(gè)人博客 -Jafeney
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/87806.html
相關(guān)文章
React中setState幾個(gè)現(xiàn)象---先知道再理解
摘要:原生事件中的在按鈕原生事件中定義的和定時(shí)器效果一樣,每次都會(huì)引起新的事件是合并的成一次的。原生事件事件生成計(jì)時(shí)器點(diǎn)擊按鈕,先執(zhí)行原生事件,再執(zhí)行事件,但是原生事件會(huì)觸發(fā)兩次,事件觸發(fā)一次。 常規(guī)情況 在同一個(gè)方法中多次setState是會(huì)被合并的,并且對(duì)相同屬性的設(shè)置只保留最后一次的設(shè)置; import React from react; export class Test exte...
前端文檔收集
摘要:系列種優(yōu)化頁面加載速度的方法隨筆分類中個(gè)最重要的技術(shù)點(diǎn)常用整理網(wǎng)頁性能管理詳解離線緩存簡介系列編寫高性能有趣的原生數(shù)組函數(shù)數(shù)據(jù)訪問性能優(yōu)化方案實(shí)現(xiàn)的大排序算法一怪對(duì)象常用方法函數(shù)收集數(shù)組的操作面向?qū)ο蠛驮屠^承中關(guān)鍵詞的優(yōu)雅解釋淺談系列 H5系列 10種優(yōu)化頁面加載速度的方法 隨筆分類 - HTML5 HTML5中40個(gè)最重要的技術(shù)點(diǎn) 常用meta整理 網(wǎng)頁性能管理詳解 HTML5 ...
前端文檔收集
摘要:系列種優(yōu)化頁面加載速度的方法隨筆分類中個(gè)最重要的技術(shù)點(diǎn)常用整理網(wǎng)頁性能管理詳解離線緩存簡介系列編寫高性能有趣的原生數(shù)組函數(shù)數(shù)據(jù)訪問性能優(yōu)化方案實(shí)現(xiàn)的大排序算法一怪對(duì)象常用方法函數(shù)收集數(shù)組的操作面向?qū)ο蠛驮屠^承中關(guān)鍵詞的優(yōu)雅解釋淺談系列 H5系列 10種優(yōu)化頁面加載速度的方法 隨筆分類 - HTML5 HTML5中40個(gè)最重要的技術(shù)點(diǎn) 常用meta整理 網(wǎng)頁性能管理詳解 HTML5 ...
【進(jìn)階1-1期】理解JavaScript 中的執(zhí)行上下文和執(zhí)行棧
摘要:首次運(yùn)行代碼時(shí),會(huì)創(chuàng)建一個(gè)全局執(zhí)行上下文并到當(dāng)前的執(zhí)行棧中。執(zhí)行上下文的創(chuàng)建執(zhí)行上下文分兩個(gè)階段創(chuàng)建創(chuàng)建階段執(zhí)行階段創(chuàng)建階段確定的值,也被稱為。 (關(guān)注福利,關(guān)注本公眾號(hào)回復(fù)[資料]領(lǐng)取優(yōu)質(zhì)前端視頻,包括Vue、React、Node源碼和實(shí)戰(zhàn)、面試指導(dǎo)) 本周正式開始前端進(jìn)階的第一期,本周的主題是調(diào)用堆棧,,今天是第一天 本計(jì)劃一共28期,每期重點(diǎn)攻克一個(gè)面試重難點(diǎn),如果你還不了解本進(jìn)...
發(fā)表評(píng)論
0條評(píng)論
Magicer
男|高級(jí)講師
TA的文章
閱讀更多
多態(tài)
閱讀 2571·2021-11-24 09:38
CSS實(shí)例詳解:Flex布局
閱讀 2601·2019-08-30 15:54
white-space、word-wrap和word-break的簡單整理
閱讀 915·2019-08-30 15:52
前端面試每日3+1——第118天
閱讀 1908·2019-08-30 15:44
Flex布局入門
閱讀 2712·2019-08-30 13:48
css選擇器總結(jié)
閱讀 768·2019-08-29 16:21
Sass 語法規(guī)則
閱讀 996·2019-08-29 14:03
「CSS」DOM2 級(jí)樣式
閱讀 2211·2019-08-28 18:15
閱讀需要支付1元查看支付并查看