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

資訊專欄INFORMATION COLUMN

深入理解 React 中的上下文 this

Magicer / 2725人閱讀

摘要:寫在前面中的作用域和上下文是這門語言的獨到之處,每個函數有不同的變量上下文和作用域。不可以當作構造函數,也就是說,不可以使用命令,否則會拋出一個錯誤。正是因為它沒有,所以也就不能用作構造函數。

寫在前面

JavaScript中的作用域scope 和上下文 context 是這門語言的獨到之處,每個函數有不同的變量上下文和作用域。這些概念是JavaScript中一些強大的設計模式的后盾。在ES5規范里,我們可以遵循一個原則——每個function內的上下文this指向該function的調用方。比如:

var Module = {
    name: "Jafeney",
    first: function() {
        console.log(this);   // this對象指向調用該方法的Module對象
        var second = (function() {
            console.log(this)  // 由于變量提升,this對象指向Window對象
        })()
    },
    init: function() {
        this.first()
    }
}

Module.init()

但是,在ES6規范中,出現了一個逆天的箭頭操作符 => ,它可以替代原先ES5里function的作用,快速聲明函數。那么,在沒有了function關鍵字,箭頭函數內部的上下文this是怎樣一種情況呢?

ES6 中的箭頭函數

在阮一峰老師的《ECMAScript 6 入門》 中,對箭頭函數的做了如下介紹:

箭頭函數的基本介紹

ES6 允許使用“箭頭”=> 定義函數。

var f = v => v;
//上面的箭頭函數等同于:
var f = function(v) {
  return v;
};

如果箭頭函數不需要參數或需要多個參數,就使用一個圓括號代表參數部分

var f = () => 5;
// 等同于
var f = function () { return 5 };
var sum = (num1, num2) => num1 + num2;
// 等同于
var sum = function(num1, num2) {
  return num1 + num2;
};

如果箭頭函數的代碼塊部分多于一條語句,就要使用大括號將它們括起來,并且使用return語句返回(重要)

var sum = (num1, num2) => { return num1 + num2; }

由于大括號被解釋為代碼塊,所以如果箭頭函數直接返回一個對象,必須在對象外面加上括號(重要)

var getTempItem = id => ({ id: id, name: "Temp" });

箭頭函數可以與變量解構結合使用

const full = ({ first, last }) => first + " " + last;
// 等同于
function full(person) {
  return person.first + " " + person.last;
}

箭頭函數使得表達更加簡潔

const isEven = n => n % 2 == 0;
const square = n => n * n;

上面代碼只用了兩行,就定義了兩個簡單的工具函數。如果不用箭頭函數,可能就要占用多行,而且還不如現在這樣寫醒目。

箭頭函數的一個用處是簡化回調函數

// 正常函數寫法
[1,2,3].map(function (x) {
  return x * x;
});

// 箭頭函數寫法
[1,2,3].map(x => x * x);

箭頭函數使用注意點

函數體內的this對象,就是定義時所在的對象,而不是使用時所在的對象。

不可以當作構造函數,也就是說,不可以使用new命令,否則會拋出一個錯誤。

不可以使用arguments對象,該對象在函數體內不存在。如果要用,可以用Rest參數代替。

不可以使用yield命令,因此箭頭函數不能用作Generator函數。

this 指向固定化

ES5規范中,this對象的指向是可變的,但是在ES6的箭頭函數中,它卻是固定的。

function foo() {
  setTimeout(() => {
    console.log("id:", this.id);
  }, 100);
}

var id = 21;

foo.call({ id: 42 });   // 輸出 id: 42

注意:上面代碼中,setTimeout的參數是一個箭頭函數,這個箭頭函數的定義生效是在foo函數生成時,而它的真正執行要等到100毫秒后。如果是普通函數,執行時this應該指向全局對象window,這時應該輸出21。但是,箭頭函數導致this總是指向函數定義生效時所在的對象(本例是{id: 42}),所以輸出的是42。

箭頭函數的原理

this指向的固定化,并不是因為箭頭函數內部有綁定this的機制,實際原因是箭頭函數根本沒有自己的this,導致內部的this就是外層代碼塊的this。正是因為它沒有this,所以也就不能用作構造函數。所以,箭頭函數轉成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);
}

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

兩道經典的面試題
// 請問下面有幾個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

上面代碼之中,其實只有一個this,就是函數foo的this,所以t1、t2、t3都輸出同樣的結果。因為所有的內層函數都是箭頭函數,都沒有自己的this,它們的this其實都是最外層foo函數的this。另外,由于箭頭函數沒有自己的this,所以也不能用call()apply()bind()這些方法去改變this的指向

// 請問下面代碼執行輸出什么

(function() {
  return [
    (() => this.x).bind({ x: "inner" })()
  ];
}).call({ x: "outer" });

上面代碼中,箭頭函數沒有自己的this,所以bind方法無效,內部的this指向外部的this。所以上面的代碼最終輸出 ["outer"]

函數綁定 ::

箭頭函數可以綁定this對象,大大減少了顯式綁定this對象的寫法(callapplybind)。但是,箭頭函數并不適用于所有場合,所以ES7提出了“函數綁定”(function bind)運算符,用來取代callapplybind調用。雖然該語法還是ES7的一個提案,但是Babel轉碼器已經支持。

函數綁定運算符是并排的兩個雙冒號(::),雙冒號左邊是一個對象,右邊是一個函數。該運算符會自動將左邊的對象,作為上下文環境(即this對象),綁定到右邊的函數上面。

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

如果雙冒號左邊為空,右邊是一個對象的方法,則等于將該方法綁定在該對象上面。

var method = obj::obj.foo;
// 等同于
var method = ::obj.foo;

let log = ::console.log;
// 等同于
var log = console.log.bind(console);

由于雙冒號運算符返回的還是原對象,因此可以采用鏈式寫法。

// 例一
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的編寫風格已經全面地啟用了ES6和部分ES7規范,所以很多ES6的坑在React里一個個浮現了。本篇重點介紹 this,也是近期跌得最疼的一個。

Component 方法內部的 this

還是用具體的例子來解釋吧,下面是我 Royal 項目里一個Table組件(Royal正在開發中,歡迎fork貢獻代碼 ^_^)

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內的一個基類,用于繼承和創建React自定義組件。ES6規范下的面向對象實現起來非常精簡,class關鍵字 可以快速創建一個類,而Component類內的所有屬性和方法均可以通過this訪問。換而言之,在Component內的任意方法內,可以通過this.xxx的方式調用該Component的其他屬性和方法。

接著分析上面的代碼,寥寥幾行實現的是對一個Table組件的封裝,借鑒了ReactNative組件的設計思路,通過外部傳遞dataSource(數據源)、columns(表格的表頭項)、renderRow(當行渲染的模板函數)來完成一個Table的構建,支持全選和取消全選的功能、允許外部傳遞classNamestyle對象來修改樣式。

從這個例子我們可以發現:只要不采用function定義函數,Component所有方法內部的this對象始終指向該類自身。

container 調用 component 時傳遞的 this

還是繼續上面的例子,下面在一個做為Demo的container里調用之前 的Table

import Table from "../../components/Views/Table/" 

接著編寫renderRow函數并傳遞給Table組件

_renderRow(row) {
    // ------------ 注意:這里對callback函數的寫法 -----------
    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="yyeyq"><sup id="yyeyq"></sup></ul>
  • <ul id="yyeyq"><sup id="yyeyq"></sup></ul>
  • <strike id="yyeyq"><input id="yyeyq"></input></strike>
  • <del id="yyeyq"><dfn id="yyeyq"></dfn></del><strike id="yyeyq"><menu id="yyeyq"></menu></strike>
    <