我读了"为什么以及如何绑定 React 组件类中的方法?"并抓住了它被调用的作用域this
有多大差异的基本思想。
我做了一个简单的类来测试这一点。
class Something {
constructor(some) {
this.some = some;
}
print() {
console.log(
"print from ",
this,
this === undefined ? "!!this is undefined!!" : this.some
);
}
}
据我了解,this
的范围可能不同,尽管它来自同一个对象(实例)。
const some = new Something("is");
some.print(); // 'this' is defined. 'this.some' is 'is'.
const going = some.print;
going(); // 'this' is undefined.
going.call(some); // 'this' is defined. 'this.some' is 'is'.
但是,为什么this
(应该是对象本身)在后一种情况下undefined
呢?不应该是顶级this
,这是一个空对象{}
吗?
有一个函数print
当你这样称呼它时
some.print();
有效地转化为这个
print.call(some, ...args)
当你把它拉出来时
const going = some.print
您刚刚获得了对独立函数的引用,因此使用
going()
与调用相同
print();
正是some
和print
之间的.
神奇地将some
作为this
传递给print
。going()
没有句号,所以没有过去。
请注意,您可以指定对对象的 going 并使用句点运算符执行神奇的"将左侧的东西传递为this
"操作
function print() {
console.log(this);
}
const someObj = {
foo: print,
};
someObj.foo();
class
关键字所做的只是帮助将打印分配给Something
的原型
class Something {
print() { console.log(this); }
}
与
function Something() {} // the constructor
Something.prototype.print = function() { console.log(this} );
这实际上也与此相同
function print() { console.log(this); }
function Something() {} // the constructor
Something.prototype.print = print;
赛卡特展示使用bind
.绑定有效地创建了一个新函数来包装旧函数。 例
const going = print.bind(foo);
几乎与
function createAFuncitonThatPassesAFixedThis(fn, objectToUseAsThis) {
return function(...args) {
return fn.call(objectToUseAsThis, ...args);
}
}
const going = createAFunctionThatPassesAFixedThis(print, foo);
going(); // calls print with 'foo' as this
其他一些答案似乎对this
存在根本性的误解。this
不是"所有者",this
也不是当前对象。this
实际上只是函数内的另一个变量。如果将.
运算符与函数调用一起使用,则会自动设置它。所以a
.
b()
this
设置为a
.如果点运算符不存在,您仍然可以使用call
或apply
设置this
,如somefuntion.call(valueForThis, ...args)
或somefunction.apply(valueForThis, [...args])
;
function print() {
console.log(this.name);
}
print.call({name: "test"}); // prints test
const foo = {
name: "foo",
bar: print,
};
foo.bar(); // prints foo
function Something(name) {
this.name = name;
}
Something.prototype.someFunc = print;
const s = new Something("something");
s.someFunc(); // prints something
s.foobar = print;
s.foobar(); // prints something
另请注意,ES6 添加了=>
箭头运算符,该运算符将this
绑定到创建函数时的任何内容。换句话说,
const foo = () => { console.log(this); }
与
const foo = function() { console.log(this); }.bind(this);
还应该清楚的是,使用bind
和箭头函数制作的两个函数都不能使用call
或apply
更改this
因为他们实际上制作了一个包装器,该包装器始终this
设置包装器时的样子,就像上面所做的createAFuncitonThatPassesAFixedThis
一样。
让我们添加一些评论来说明我的意思
function createAFuncitonThatPassesAFixedThis(fn, objectToUseAsThis) {
// return a function that calls fn with objectToUsAsThis as this
return function(...args) {
// when we arrive here from the "going.call" line below
// `this` will be "someObject"
// but on the next line we're passing "objectToUseAsThis" to fn as this
// so "someObject" is effectively ignored.
return fn.call(objectToUseAsThis, ...args);
}
}
const going = createAFunctionThatPassesAFixedThis(print, foo);
going.call(someObject); // pass some object as `this`
还有一件事。使用bind
为事件侦听器或回调创建函数是很常见的。这在 React 中很常见。例
class Component extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
// 'this' will be the current component since we bound 'this' above
console.log("was clicked");
}
render() {
return (<button onClick={this.handleClick}>click me</button>);
}
}
这将以至少 3 种常见方式处理。
在构造函数中使用
bind
(见上文)在渲染中使用
bind
render() { return (<button onClick={this.handleClick.bind(this)}>click me</button>); }
在渲染中使用箭头函数
render() { return (<button onClick={() => { this.handleClick(); }}>click me</button>); }
上面我展示了bind
和=>
实际上对createAFuncitonThatPassesAFixedThis
做了什么。他们创建了一个新功能。因此,这应该清楚地表明,如果使用上面的样式 2 或 3,则每次调用render
时都会创建一个新函数。如果使用样式 1,则仅在构造函数中创建新函数。这是一个风格问题,至于这是否重要。创建更多函数意味着更多的垃圾,这意味着网页可能更慢,但在大多数情况下,除非您有一个不断渲染的疯狂复杂网页,否则您这样做的 3 种方式中的哪一种可能并不重要。
您应该将此行更正为
const going = some.print.bind(some);
否则,将获得不同的范围,并且它不是某个对象的成员this
因此undefined
注意
this
引用当前类对象。而且going
不是任何类的成员,所以this
undefined
const going = some.print;
going(); // 'this' is undefined.
在这里,在第一行中,您将在变量going
中存储对some
对象的print
函数(属性)的引用。
请注意,您只是存储对该特定属性的引用(print
),而不是与所有者(some
一起存储)。
this
总是指"所有者"。
所以当你执行going
时,所有者(this
)是未知的(或者称之为undefined
)。