前言 最近在做一個(gè)項(xiàng)目的時(shí)候 關(guān)于class方法中 this 指向以及 外置prototype 的 this 指向 引發(fā)了我的思考棒坏!
image.png
ES6原生class
我們假設(shè) A 為 react B 為 我們創(chuàng)建的類 class B extends React.component{}
class A {
constructor() {
this.x = 1;
}
}
class B extends A {
constructor() {
super();
this.x = 2;
console.log('=====');
console.log(this); // B
console.log(this.x); // 2
console.log(super.x); // undefined
this.getme = this.getme.bind(this)
}
getName(){
console.log('getName');
console.log(this.x); // B
let m = this.getme
m()
}
getme(){
console.log('getme');
console.log(this.x); // B
}
}
// 類轉(zhuǎn)化為 es5的寫法如下:
function Es5B() {
this.x = 2
}
Es5B.prototype.getName = function () {
console.log(this);
console.log('getName');
console.log(this.x);
}
let b = new B();
b.getName()
let esb = new Es5B()
esb.getName()
打印結(jié)果
- 經(jīng)過打印我們發(fā)現(xiàn) B 中的 this 指向的都是 B 這個(gè)類
- 那么問題來了醋火,我們 都知道 react的 class 中需要綁定 this, 為什么需要?綁定 this 有哪些方式,以及這些方式有什么不同袁余?
http://note.youdao.com/noteshare?id=3d64b603405bcbb2c3cad3f750e5341d
react 高階 api => createElement
https://react.docschina.org/docs/react-without-jsx.html
jsx 語法
class Hello extends React.Component {
render() {
return <div>Hello {this.props.toWhat}</div>;
}
}
ReactDOM.render(
<Hello toWhat="World" />,
document.getElementById('root')
);
編譯成下面這段代碼
class Hello extends React.Component {
render() {
return React.createElement('div', null, `Hello ${this.props.toWhat}`);
}
}
ReactDOM.render(
React.createElement(Hello, {toWhat: 'World'}, null),
document.getElementById('root')
);
看我們最初的那一段代碼
// 如果我們將 constructor 中的那個(gè) bind 去掉之后
// this.getme = this.getme.bind(this)
// 執(zhí)行到這里 this的指向就變化了
let m = this.getme
m() // 此時(shí) this 變化為 undefined
將方法進(jìn)行賦值之后,丟失了上下文,導(dǎo)致 this 變成 undefined 农猬, this之所以沒有變?yōu)閣indow 是因?yàn)轭惵暶骱皖惐磉_(dá)式的主體以 嚴(yán)格模式 執(zhí)行却音,主要包括構(gòu)造函數(shù)改抡、靜態(tài)方法和原型方法。Getter 和 setter 函數(shù)也在嚴(yán)格模式下執(zhí)行系瓢。
譯文
為什么需要在 React 類組件中為事件處理程序綁定 this
未解之謎 原生 class 中 如果方法改為箭頭函數(shù)這種形式就會(huì)報(bào)錯(cuò) 但是在 react 的 class 中 是可以正常渲染的
class A {
constructor() {
this.x = 1;
}
}
class B extends A {
constructor() {
super();
this.x = 2;
}
getme=()=>{ // 這里會(huì)報(bào)錯(cuò)誤
console.log('getme');
console.log(this.x);
}
getName(){
console.log('getName');
console.log(this.x);
let m = this.getme
m()
}
}
let b = new B();
b.getName()
內(nèi)置箭頭函數(shù)與外置箭頭函數(shù)是有區(qū)別的
class B extends A {
constructor() {
super();
this.x = 2
}
getName(){
console.log('getName');
console.log(this.x); // B
let m = this.getme
m()
}
}
B.prototype.getme = ()=> {
console.log('getme');
console.log(this);
/*
箭頭函數(shù)的 this 指向定義時(shí)所在對(duì)象 定義的環(huán)境在 window
此時(shí) this 指向 window
如果是 react 創(chuàng)建的組件 此時(shí) this指向和類之外的 this 是一致的 (但不是 window)
如果prototype上掛載方法的時(shí)候 強(qiáng)烈建議大家用es5的方式就好阿纤!
*/
}
let b = new B();
b.getName()
react 創(chuàng)建組件(需要綁定 this 和綁定 this 的方法)
export default class ExtendsCompTable extends React.Component {
constructor (props) {
super(props)
this.state = {
name: 'react測試this指向'
}
this.handler = this.handler.bind(this)
}
handler () {
message.info('點(diǎn)擊了 bindthis),通過 bind 綁定 this')
}
renderDom () {
let { name } = this.state
return <Button>{name}</Button>
}
handlerArrow=()=> {
console.log(this);
message.info('點(diǎn)擊了箭頭函數(shù)綁定按鈕,通過箭頭函數(shù)綁定 this')
}
handleInnerArrow(){
console.log(this);
message.info('點(diǎn)擊了箭頭函數(shù)綁定,通過 bind 綁定 this')
}
handleBind(){
console.log(this);
message.info('點(diǎn)擊了bind')
}
render () {
return (
<div>
<h1>234567890</h1>
<Button type='primary' onClick={this.handler}>bind(this)</Button>
{/* 這種直接調(diào)用的方式不需要綁定 this 作為對(duì)象的方法被調(diào)用 this 指向?qū)ο?/}
{this.renderDom()}
{/* 這種 handlerArrow=()=> {...}的形式 雖然可以用 但是不太建議*/}
<Button type='primary' onClick={this.handlerArrow}>箭頭函數(shù)綁定</Button>
<Button type='primary' onClick={() => {
this.handleInnerArrow()
}}>點(diǎn)擊觸發(fā)方法</Button>
<Button type='primary' onClick={this.handleBind.bind(this)}>bind</Button>
<Table columns={columns} dataSource={data} />
</div>
)
}
}
箭頭函數(shù) ()=>
-
函數(shù)體內(nèi)的this對(duì)象,就是定義時(shí)所在的對(duì)象夷陋,而不是使用時(shí)所在的對(duì)象欠拾,this是繼承自父執(zhí)行上下文R刃俊!中的this
var x=11; var obj={ x:22, say:function(){ console.log(this.x) } } obj.say(); // 22
var x=11; var obj={ x:22, say:()=>{ console.log(this.x); } } obj.say();// 11
var a=11 function test1(){ this.a=22; let b=function(){ console.log(this.a); }; b(); } var x=new test1(); // 11
var x=11; var obj={ x:22, say:()=>{ console.log(this.x); } } obj.say(); // 22
箭頭函數(shù)中的 this 對(duì)象指向是固定的
不可以當(dāng)作構(gòu)造函數(shù)藐窄,也就是說资昧,不可以使用new命令,否則會(huì)拋出一個(gè)錯(cuò)誤
bind
無論是 call() 也好荆忍, apply() 也好格带,都是立馬就調(diào)用了對(duì)應(yīng)的函數(shù),而 bind() 不會(huì)刹枉, bind() 會(huì)生成一個(gè)新的函數(shù)叽唱,bind() 函數(shù)的參數(shù)跟 call() 一致,第一個(gè)參數(shù)也是綁定 this 的值微宝,后面接受傳遞給函數(shù)的不定參數(shù)尔觉。 bind() 生成的新函數(shù)返回后,你想什么時(shí)候調(diào)就什么時(shí)候調(diào)
var m = {
"x" : 1
};
function foo(y) {
alert(this.x + y);
}
foo.apply(m, [5]);
foo.call(m, 5);
var foo1 = foo.bind(m, 5);
foo1();