We have discussed the difference in scope between python and js when implementing closures.
→ https://blog.csdn.net/qq_16181837/article/details/104805151
I encountered a related problem today
How does js access the variable with the same name in the external scope?
In python, we have nolocal and global keywords, which can easily declare the scope of a variable:
def outer():
outer_var = 'outer'
def inner():
# 闭包
nolocal outer_var
outer_var = 'inner'
return outer_var
print(inner())
outer()
Output:
inner
But as far as I know, js does not have such keywords.
Checked a lot of information
I offer a few methods here:
Method one, access the object of external packaging
function outer() {
var variable = 'outer';
var obj = {
outer_var: variable
};
function inner() {
var variable = 'inner';
console.log(obj.outer_var);
}
inner();
}
outer()
Output:
inner
Method two, access external variables through functions
function outer() {
var variable = 'outer';
function get_outer() {
return variable;
}
function inner() {
var variable = 'inner';
console.log(get_outer());
}
inner();
}
outer()
Output:
inner
You may ask, why do we have to make internal variables have the same names as external ones?
Indeed, in some cases we can declare a variable such as local_variable internally to show distinction, but in some cases it is not possible
Look at this example:
class test {
constructor(btn) {
// 给传进来的 btn 绑定一个回调事件
// 调用对象的 method 方法
$(btn).on('click', function(outer_this) {
// 但是这里的 this 是调用回调函数的对象,即 btn 而不是指向对象的 this
// 如何访问外部作用域的 this?
return function() {
this.method();
}
}(this));
}
method() {
console.log('Calling method.');
}
}
var obj = new test($('.btn-1')[0])
Error:
Let's take a look at who this this points to: When
registering an event, the function is bound to the object .btn-1 as a property.
So naturally, this will point to this object when calling back.
We all say js everything is an object, so why do I say
In some cases, we can declare a variable such as local_variable internally to show distinction, but in some cases it is not possible
How to do it?
The first method above can be used, that is, to access the externally packaged object,
but the second method is not possible, because the this in the get_outer function points to its caller, that is, the global object or undefined.
For this situation, I would like to introduce three methods:
Method one, change the function this point
Here we use a method bind of the function , which provides an object argument for the bind method, which returns a copy of the function bound to the object.
That is, fun.bind(obj) will return an obj.fun for later calling.
class test {
constructor(btn) {
btn.addEventListener('click', function() {
this.method();
}.bind(this));
}
method() {
console.log('Calling method.');
}
}
var obj = new test($('.btn-1')[0]);
Output:
Calling method.
Two methods are expanded here:
fun.call(obj[, arg1[, arg2[, arg3…]]])
fun.apply(obj, arg1, arg2, arg3…)
These two methods will execute the function immediately, and change the this at runtime to the first parameter obj, and pass in the specified parameters.
The difference is that the second parameter of the apply method is an array, while call is a list.
Method two, transfer parameters
Ashamed, I have been thinking about how to directly access external variables like python, ignoring the simplest method,
but there is a problem. The addEventListener method of native webapi does not provide an interface for passing in the parameters of the callback function, so Here you need to use closures to achieve:
Primitive webapi:
class test {
constructor(btn) {
btn.addEventListener('click', function(outer_this) {
return function() {
outer_this.method();
}
}(this));
}
method() {
console.log('Calling method.');
}
}
var obj = new test($('.btn-1')[0]);
jQuery:
In comparison, jquery is much more convenient because it provides an interface for passing in parameters:
class test {
constructor(btn) {
$(btn).on('click', {
outer_this: this
}, function(jq_event) {
jq_event.data.outer_this.method();
});
}
method() {
console.log('Calling method.');
}
}
var obj = new test($('.btn-1')[0]);
Method three
There is a simpler one. This should be the most natural way of thinking, but I think it’s complicated
class test {
constructor(btn) {
var that = this;
$(btn).on('click', function(jq_event) {
that.method();
});
}
method() {
console.log('Calling method.');
}
}
var obj = new test($('.btn-1')[0]);
The on of jq allows you to pass in an object. When the function is called back, jq will call the function and pass in a jq event object. The data attribute in it is the object you passed in when you registered.