关于 javascript:setTimeout 和此绑定

setTimeout and this binding

除了切换类之外,我对使用 jquery 不是很熟悉。也就是说,我会尽量解释我的问题。我有三个div。单击其中一个后,另外两个应该翻转 90 度,然后将它们的高度降低到 0

我向 youTube 上传了一个简短的动画,向您展示最终动画的外观
https://youtu.be/4ImmHJ04d0w

所以我过于复杂的脚本现在看起来是这样的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// Add Temporarily Class
(function($){

    $.fn.extend({

        addTemporaryClass: function(className, duration) {
            var elements = this;
            setTimeout(function() {
                elements.removeClass(className);
            }, duration);

            return this.each(function() {
                $(this).addClass(className);
            });
        }
    });

})(jQuery);



$('.review--1').on('click', function() {
\t\t\t$('[class^=review--]').not(this).addClass('review__hidden review__square');\t
\t\t\t$('.review--2 ,.review--3, .review--4').removeClass('review__hidden');\t\t\t\t\t
\t\t});

\t\t// Animation
\t\t$('.review--1').on('click', function() {
\t\t\t
\t\t\t$('.review--2 ,.review--3, .review--4').addTemporaryClass('animate-in', 500);
\t\t\tsetTimeout(function() {
        \t\t$('.review--2 ,.review--3, .review--4').addClass('flip')
    \t\t\t}, 500);
\t\t\t\t\t\t\t\t
\t\t});


\t\t$('.review--2 ,.review--3, .review--4').on('click', function() {
\t\t\t$(this).removeClass('review__square');
\t\t\t$('.review--2 ,.review--3, .review--4').not(this).addTemporaryClass('animate-out', 500);
\t\t\tvar that = $(this);
  \t\t\tsetTimeout(function() {
        \t\t$('.review--2 ,.review--3, .review--4').not(that).removeClass('flip').addClass('review__hidden')
    \t\t\t}, 500);

        \t\t
\t\t\t});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
.review--button {
\toverflow: hidden;
\tcolor: #aa7f6f;
\twidth: 100%;
\tfloat: left;
\theight: 50px;
\tbackground-color: lightgrey;
}

.review__square {
\tmargin: 6px 3px;
\twidth: 190px;
\theight: 190px;
\ttext-align: center;
\ttransform: rotateY(90deg);
\tperspective: 80px;
\t-webkit-perspective: 80px;
\t/* transition: height .5s ease, transform .3s ease; */
}
.review__hidden {
\theight: 0;
\tmargin: 0;
\ttransform: rotateY(90deg);
}
.animate-in {
\tanimation: flip-in .5s forwards;
}



@keyframes flip-in {
\tfrom {transform: rotateY(90deg);}
\tto {transform: rotateY(0);}
}
.animate-out {
\tanimation: flip-out .5s forwards;
}



@keyframes flip-out {
\tfrom {transform: rotateY(0);}
\tto {transform: rotateY(90deg);}
}
.flip {
\ttransform: rotateY(0);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js">

\t\t\t\t1
\t\t\t

\t\t\t
\t\t\t\t2
\t\t\t

\t\t\t
\t\t\t\t3
\t\t\t

\t\t\t
\t\t\t\t4
\t\t\t

\t\t\t
\t\t\t\t5
\t\t\t

\t\t\t
\t\t\t\t6
\t\t\t

\t\t\t
\t\t\t\t7
\t\t\t

问题(除了我糟糕的代码)是 .not(this) 在超时内不起作用。有人可以告诉我怎么做吗?或者甚至更好地告诉我如何缓解我糟糕的代码:)


this 对象绑定在 JavaScript 中是易变的……也就是说,它并不总是指向同一个对象,它的绑定可以从一行代码更改为下一行。如何调用包含单词 this 的代码决定了它将绑定到哪个对象。

您是否像这样在 setTimeout 之前缓存了 this 对象引用:

1
var self = this;

然后您可以在 setTimeout 中引用 self 并且它会起作用。

Here's a checklist that you can follow to know what this will bind
to (your scenario is #3 and you can read more about it here
under the"The"this" problem" section)...

如果包含this的代码被调用:

  • 作为对象实例的方法或属性(通过实例变量):

    1
    2
    3
    4
    5
    6
    var o = new Object();

    //"this" will be bound to the"o" object instance
    // while"someProperty" and"someMethod" code executes
    o.someProperty = someValue;
    o.someMethod();
  • 通过 .call().apply().bind()Array.prototype.fn 调用:

    1
    2
    3
    4
    //"this" will be bound to the object suppled as the"thisObjectBinding"
    someFunction.call(thisObjectBinding, arg, arg);
    someFunction.apply(thisObjectBinding, [arg, arg]);
    var newFunc = someFunction.bind(thisObjectBinding, arg, arg);

    注意:调用回调函数(即事件处理程序)时,会在触发事件时隐式调用处理程序。在这些情况下,负责触发事件的对象成为绑定到 this.

    的对象

    此外,几个 Array.prototype 方法允许传递一个 thisObject,这将在方法调用期间改变绑定:

    1
    2
    3
    4
    5
    Array.prototype.every( callbackfn [ , thisArg ] )
    Array.prototype.some( callbackfn [ , thisArg ] )
    Array.prototype.forEach( callbackfn [ , thisArg ] )
    Array.prototype.map( callbackfn [ , thisArg ] )
    Array.prototype.filter( callbackfn [ , thisArg ] )

  • 如果其他场景都不适用,则发生默认绑定。

    3a。 "use strict" 生效时: thisundefined

    3b.没有 "use strict" 生效:this 绑定到全局对象

  • ** 注意:使用 eval() 也会影响 this 绑定,但作为一般最佳实践,应避免使用 eval()

    说了这么多,我不知道你为什么需要 setTimeout(如果我正确理解你的场景):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    var divs = document.querySelectorAll("div:not(#parent)");

    divs.forEach(function(div){
      div.addEventListener("click", function(){
       
        var self = this;
       
        // get other two divs, not this one
        var $otherDivs = $(divs).not(this);
       
        // Fade them out:
        $otherDivs.fadeOut(function(){
           // JQuery animations accept a callback function to run when the animation is complete
           $(self).addClass("clickedDiv");
        });
      });
    });
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    #parent { width:350px; border: 0; background:inherit;}

    div {
      width:100px;
      height:100px;
      background:#ff0;
      text-align:center;
      float:left;
      border:1px solid black;
    }

    .clickedDiv {
      background:#f99;
      width:100%;
    }
    1
    2
    3
    4
    5
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js">

       I'm DIV 1
       I'
    m DIV 2
       I'm DIV 3


    虽然 Scott Marcus 很好地解释了调用方法如何由于某种原因改变其 this 对象,但他不想更新他的答案以包括使用箭头函数调用方法如何影响 this对象。

    直接从 MDN 引用

    An arrow function does not create its own this context, so this has the original meaning from the enclosing context.

    所以为了扩展马库斯的很好的答案

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    var divs = document.querySelectorAll("div:not(#parent)");

    divs.forEach(function(div){
      div.addEventListener("click", function(){
        // get other two divs, not this one
        var $otherDivs = $(divs).not(this);

        // Fade them out:
        $otherDivs.fadeOut(() => {
           // JQuery animations accept a callback function to run when the animation is complete
           $(this).addClass("clickedDiv");
        });
      });
    });

    正如马库斯在评论中指出的,箭头函数不是在您希望动态绑定 this 的地方使用的工具,但是如果您发现自己必须经常使用 var self = this 因为您的回调有其上下文当您不希望它重新绑定时,箭头函数可能会很有用。

    无论哪种方式,它都是工具带和所有其他工具的另一个工具,


    也许吧,你应该使用这个链接吗? var self= $(this)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    $('.review--2 ,.review--3, .review--4').on('click', function() {
      $(this).removeClass('review__square');
      $('.review--2 ,.review--3, .review--4').not(this).addTemporaryClass('animate-out', 500);

      var self = $(this);

      setTimeout(function() {
          $('.review--2 ,.review--3, .review--4').not(self).removeClass('flip').addClass('review__hidden')
      }, 500);

      });
    });