国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

深入理解 React 中的上下文 this

Magicer / 2722人閱讀

摘要:寫在前面中的作用域和上下文是這門語言的獨(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

注意:上面代碼中,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。

箭頭函數(shù)的原理

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);
}

上面代碼中,轉(zhuǎn)換后的ES5版本清楚地說明了,箭頭函數(shù)里面根本沒有自己的this,而是引用外層的this

兩道經(jīng)典的面試題
// 請(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ù)沒有自己的this,所以bind方法無效,內(nèi)部的this指向外部的this。所以上面的代碼最終輸出 ["outer"]

函數(shù)綁定 ::

箭頭函數(shù)可以綁定this對(duì)象,大大減少了顯式綁定this對(duì)象的寫法(callapplybind)。但是,箭頭函數(shù)并不適用于所有場(chǎng)合,所以ES7提出了“函數(shù)綁定”(function bind)運(yùn)算符,用來取代callapplybind調(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?this.onSelectAll()} onCancel={()=>this.offSelectAll()} />:""}{item.title}]
        })
    }

    _renderBody() {
        let _renderRow = this.renderRow;
        return this.state.dataSource.map((item) => {
            return _renderRow && _renderRow(item)
        })
    }

    render() {
        let state = this.state;
        return (
            
{this._renderHead()} {this._renderBody()}
) } } export default Table

ComponentReact內(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)建,支持全選和取消全選的功能、允許外部傳遞classNamestyle對(duì)象來修改樣式。

從這個(gè)例子我們可以發(fā)現(xiàn):只要不采用function定義函數(shù),Component所有方法內(nèi)部的this對(duì)象始終指向該類自身。

container 調(diào)用 component 時(shí)傳遞的 this

還是繼續(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 (
        
            {row.key}
            {row.name}
            {row.age}
            {row.birthday}
            {row.job}
            {row.address}
            
                
<ul id="0ouo0"></ul>
<fieldset id="0ouo0"><menu id="0ouo0"></menu></fieldset>
      <