Меня раньше смущал этот пункт setTimeout, и я не сталкивался с сопутствующими проблемами в проектной практике, и меня не спрашивали об этой проблеме на собеседовании, так что приходится проходить мимо, пока не столкнулся с проблемой, которая смутила меня в недавнем письменном тесте, чтобы получить более глубокое понимание. Я также еще раз напоминаю себе, что мое понимание точек знаний должно быть действительно дотошным, а не просто поверхностным.
Это описание setTimeout в "Elevation 3":Код, вызываемый таймаутом, выполняется в глобальной области видимости, поэтому значение this в функции указывает на объект окна в нестрогом режиме.,не определено в строгом режиме,Так почему же так?Просто можно понять, что setTimeout — это метод под объектом окна.В данной статье обсуждается ситуация только в нестрогом режиме. Согласно заключению Elevation 3, если мы действительно понимаем это, мы можем решить 90% возникающих проблем, таких как:
setTimeout(console.log(this),0)//window
let obj = {
print : function () {
setTimeout(function () {
console.log('setTimeout:'+this);
},0);
}
};
obj.print() //setTimeout: window
function say() {
console.log('setTimeout:'+this);
}let obj = {
print : function () {
setTimeout(say,0);
}
};
obj.print() //setTimeout: window
Будь то прямая ссылка, вызов метода объекта или ссылка на функцию, это легко понять.Иногда мы сталкиваемся с двумя случаями this, как показано ниже: один это в среде вызова setTimeout, а другой - это в среде вызова функция отложенного выполнения. В этот раз нам нужно обратить внимание на разницу. Мы можем понять, что первый параметр в setTimeout является простой ссылкой на функцию, и его направление зависит от среды, в которой он вызывается, как и наша общая функция звонки.
let obj = {
say : function () {
console.log(this); //延迟执行函数中的this
},
print : function () {
setTimeout(this.say,0); //setTimeout调用环境中的this,指向调用者即obj
}
};
obj.print() //setTimeout: window
Мы меняем способ написания так, чтобы это в среде вызова setTimeout в приведенном выше коде указывало на окно, и выполнение функции в это время не имело никакого эффекта:
let obj = {
say : function () {
console.log(this); //延迟执行函数中的this
},
print : function () {
setTimeout(this.say,0); //setTimeout调用环境中的this,指向调用者即obj
}
};
let func = obj.print;
func()
Смотри ниже:
var a = 1;
function func(){
let a = 2;
setTimeout(function(){
console.log(a);
console.log(this.a);
},0)
}
func(); //输出2 1
var a = 1;
function func(){
// let a = 2;
setTimeout(function(){
console.log(a);
console.log(this.a);
},0)
}
func(); //输出1 1
Можно видеть, что, когда это не используется, в вызове тайм-аута setTimeout переменная просматривается по области определения вместе с обычным вызовом функции.
Затем, при выполнении строки вроде как это
var a = 2
function say(a){
console.log(a)
}
function test(){
let a = 1;
setTimeout("say(a)",0)
}
test() //2
var a = 2
function test(){
let a = 1;
function say(a){
console.log(a)
}
setTimeout("say(a)",0)
}
test() //say is not defined
Видно, что сообщается об ошибке, когда метод say перемещается внутрь теста.say is not defined, причина в том, что javascript фактически вызывает внутренние вызовы, когда он выполняется как строкаeval(),Средой выполнения eval является окно глобальной области видимости, и в глобальной области видимости нет метода say, поэтому сообщается об ошибке.
Передача параметров напрямую в виде присваивания не сообщит об ошибке:
var a = 2;
function say(a){
console.log(a)
}
function test(){
let a = 1;
setTimeout("say('hhhh')",0)
}
test() //hhhh
Теперь давайте посмотрим, как это указывает в сочетании со стрелочной функцией es6.Все знают, что, поскольку стрелочная функция не привязана к этому, она захватит это значение контекста, в котором оно находится (то есть определенное местоположение), как собственное это значение. То же самое верно и в setTimeout.
let obj = {
name : "jay",
print : function () {
setTimeout(() => {
console.log(this.name)
},0);
}
};
obj.print() //jay
Как изменить этот указатель setTimeout
На самом деле есть два ответа на предыдущее обсуждение: использование промежуточных переменных для ссылки на это снаружи и применение стрелочных функций.
方法一
let obj = {
name : "jay",
print : function () {
let that = this;
setTimeout(function() {
console.log(that.name)
},0);
}
}; 方法二
let obj = {
name : "jay",
print : function () {
setTimeout(() => {
console.log(this.name)
},0);
}
};
Другой метод - использовать метод связывания:
方法三
var name = "window";
function say(){
console.log(this.name);
}
let obj = {
name : "jay",
print : function(){
setTimeout(say.bind(this),0)
}
}
obj.print(); //jay
Проблема с передачей параметра setTimeout
1.setTimeout(функция,миллисекунды,параметр1,параметр2,...); param1,param2,... являются необязательными, используются для предоставления дополнительных параметров функции, но обратите внимание, что эту функцию нельзя использовать в IE9 и более ранних версиях IE! !
function say(name) {
console.log(name)
}s
etTimeout(say,0,'jay')
2. Передать параметры в виде строки:
function say(a,b) {
console.log(a+b)
}
//let name = "jay"
setTimeout( "say(3,4)",3000) //三秒后输出7
Меры предосторожности
Старайтесь избегать первого параметра setTimeout в виде строки. setTimeout допускает строку в качестве первого параметра. Функция eval() будет вызываться внутри js для динамического выполнения строкового скрипта. eval() имеет много непредвиденных опасностей Эффективность eval Очень низкий Чтобы выполнить фрагмент кода, вам нужно сначала преобразовать строку в исполняемый код, что на один шаг больше, чем обычно, и может неявно создавать глобальные переменные.
При рекурсивном вызове setTimeout не забудьте применить clearTimeout, чтобы избежать утечек памяти, вызванных бесконечной рекурсией.