开发者问题收集

在 JS 类的递归方法中访问类实例变量

2022-01-28
164

我有一个 JS 代码,该代码应该显示按顺序发送的消息类。有一个称为消息服务的类。它通过类方法 showMessage() 实现显示文本消息的逻辑。消息淡入和淡出需要 2 秒。该逻辑假设如果在前一条消息的服务尚未完成的情况下发送了新消息,则它会将消息添加到队列中。然后 setTimeOut 函数检查队列是否为空,如果不是,则在第一个队列项上调用消息服务。

问题是,在服务的第一次迭代中,代码无法访问类变量 this.service 和 this.messageStack 和 this.targetStack。它显示未定义。如何在 setTimeOut 函数中访问 this.service 和 this.messageStack 和 this.targetStack 变量,该函数递归调用 showMessage() 方法?

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <button id="show-message">Show message</button>
    <div id="holder">
    </div>
    <script>
        class MessageService {
            constructor() {
                this.service = 0;
                this.messageStack = []
                this.targetStack = []
                console.log('initiated')
            }


            showMessage(target, msg) {
                this.target = target
                this.msg = msg
                console.log(this.target + '  ' + this.msg)

                if (this.service === 0) {
                    console.log('here')
                    this.service = 1

                    let el = document.createElement('div')
                    el.setAttribute('id', 'thisMessage')
                    el.innerText = this.msg
                    let elStyle = document.createElement('style')
                    elStyle.innerHTML = ` #thisMessage {
                border: 1px solid purple;
                border-radius: 3px;
                width: 200px;
                height: 20px;
                display: flex;
                align-items: center;
                background-color: rgb(245, 202, 245);
                padding: 5px;
                padding-left: 10px;
                animation: fadeinout 2s linear forwards;
                -webkit-animation: fadeinout 2s linear forwards;
                opacity: 0;
                margin-top: 20px;
            }
            @keyframes fadeinout {
                50% {
                opacity: 1;
                }
            }
            @-webkit-keyframes fadeinout {
                0%, 
                100% {
                    opacity: 0;
                }
                50% {
                    opacity: 1;
                }
            }`
                    el.appendChild(elStyle)
                    document.getElementById(target).appendChild(el)

                    console.log(this.messageStack)

                    setTimeout(function () {
                        document.getElementById('thisMessage').remove();

                        this.service = 0

                        console.log(this.messageStack) // UNDEFINED ??????

                        if (this.messageStack.length >
                            0) { // CANNOT READ PROPERTIES OF UNDERFINED (READING "LENGTH") ?????
                            let newMessage = this.messageStack.pop()
                            let newTarget = this.targetStack.pop()
                            func.call.this.showMessage(newTarget, newMessage)
                        }
                    }, 2100)

                } else {
                    this.messageStack.push(this.msg)
                    this.targetStack.push(this.target)
                }
            }
        }

        let msg = new MessageService();
        msg.showMessage('holder', 'Print out all papers')
        msg.showMessage('holder', 'Update completed')
        msg.showMessage('holder', 'Server error')
    </script>

</body>
<style>

</style>

</html>
1个回答

问题是 setTimeout 中的 this 引用了不同的对象。您要做的就是传入对所需 this 的引用。

我已在下文中通过在 setTimeout 之外创建一个新变量 that 来实现此目的,并改为引用 that

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <button id="show-message">Show message</button>
    <div id="holder">
    </div>
    <script>
        class MessageService {
            constructor() {
                this.service = 0;
                this.messageStack = []
                this.targetStack = []
                console.log('initiated')
            }


            showMessage(target, msg) {
                this.target = target
                this.msg = msg
                console.log(this.target + '  ' + this.msg)

                if (this.service === 0) {
                    console.log('here')
                    this.service = 1

                    let el = document.createElement('div')
                    el.setAttribute('id', 'thisMessage')
                    el.innerText = this.msg
                    let elStyle = document.createElement('style')
                    elStyle.innerHTML = ` #thisMessage {
                border: 1px solid purple;
                border-radius: 3px;
                width: 200px;
                height: 20px;
                display: flex;
                align-items: center;
                background-color: rgb(245, 202, 245);
                padding: 5px;
                padding-left: 10px;
                animation: fadeinout 2s linear forwards;
                -webkit-animation: fadeinout 2s linear forwards;
                opacity: 0;
                margin-top: 20px;
            }
            @keyframes fadeinout {
                50% {
                opacity: 1;
                }
            }
            @-webkit-keyframes fadeinout {
                0%, 
                100% {
                    opacity: 0;
                }
                50% {
                    opacity: 1;
                }
            }`
                    el.appendChild(elStyle)
                    document.getElementById(target).appendChild(el)

                    console.log(this.messageStack)
                    
                    let that = this;

                    setTimeout(function () {
                        document.getElementById('thisMessage').remove();

                        that.service = 0

                        console.log(that.messageStack) // UNDEFINED ??????

                        if (that.messageStack.length >
                            0) { // CANNOT READ PROPERTIES OF UNDERFINED (READING "LENGTH") ?????
                            let newMessage = that.messageStack.pop()
                            let newTarget = that.targetStack.pop()
                            //func.call.this.showMessage(newTarget, newMessage)
                        }
                    }, 2100)

                } else {
                    this.messageStack.push(this.msg)
                    this.targetStack.push(this.target)
                }
            }
        }

        let msg = new MessageService();
        msg.showMessage('holder', 'Print out all papers')
        msg.showMessage('holder', 'Update completed')
        msg.showMessage('holder', 'Server error')
    </script>

</body>
<style>

</style>

</html>
K Scandrett
2022-01-28