当Node.对象在顶层被调用时,'this'的范围是什么?



我读了"为什么以及如何绑定 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();

正是someprint之间的.神奇地将some作为this传递给printgoing()没有句号,所以没有过去。

请注意,您可以指定对对象的 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.如果点运算符不存在,您仍然可以使用callapply设置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和箭头函数制作的两个函数都不能使用callapply更改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 种常见方式处理。

  1. 在构造函数中使用bind(见上文)

  2. 在渲染中使用bind

    render() {
    return (<button onClick={this.handleClick.bind(this)}>click me</button>);
    }
    
  3. 在渲染中使用箭头函数

    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不是任何类的成员,所以thisundefined

const going = some.print;
going(); // 'this' is undefined.

在这里,在第一行中,您将在变量going中存储对some对象的print函数(属性)的引用。

请注意,您只是存储对该特定属性的引用(print),而不是与所有者(some一起存储)。

this总是指"所有者"。

所以当你执行going时,所有者(this)是未知的(或者称之为undefined)。

最新更新