为什么 ES6 的 Proxy 在 class 的方法中使用时不起作用?

Why do ES6's Proxy doesn't work when used inside a method of class?

当在 class 的方法内部使用时,逻辑不起作用,但如果我以函数式风格使用它,则逻辑在外面起作用。

class Hook {
    constructor(object) {
        this.object = object;
    }

    toStringProperty() {
        const handler = {
            apply: function (target, thisArg, args){
                if (thisArg === Function.prototype.toString) {
                    return 'function toString() { [native code] }'
                }

                if (thisArg === this.object) {
                    return "Hooked String"
                }

                return target.apply(thisArg, args)
            }
        }

        Function.prototype.toString = new Proxy(Function.prototype.toString, handler)
    }
}

let hook = new Hook(HTMLAudioElement);

hook.toStringProperty();

// Interesting enough this when called (I use Devtools) logs Proxy Object itself but only happen if I use a Class
console.log(Function.prototype.toString)

console.log(HTMLAudioElement.toString())

我应该怎么做才能让它在 class 中工作?

“问题”不在于代理 - 所有调用都已通过它。问题是 how the this keyword works. In short, it's determined at call time, so this.object will have a different meaning depending on when and how the function is called. In this case, the value of this is "lost" not unlike how you lose it in a callback.

的古老细节

如果你需要具体指代某事,你有几个选择

词法绑定 this 使用箭头函数 () => {}

一个箭头函数 在创建时,所以它在调用时不会变化:

class Hook {
    constructor(object) {
        this.object = object;
    }

    toStringProperty() {
        const handler = {
            apply: (target, thisArg, args) => { //<--- arrow function
                if (thisArg === Function.prototype.toString) {
                    return 'function toString() { [native code] }'
                }

                if (thisArg === this.object) {
                    return "Hooked String"
                }

                return target.apply(thisArg, args)
            }
        }

        Function.prototype.toString = new Proxy(Function.prototype.toString, handler)
    }
}

let hook = new Hook(HTMLAudioElement);

hook.toStringProperty();

// Interesting enough this when called (I use Devtools) logs Proxy Object itself but only happen if I use a Class
console.log(Function.prototype.toString)

console.log(HTMLAudioElement.toString())

使用 Function#bind

手动绑定 this

这对于箭头函数来说基本上是多余的,但仍然是一个选项:

class Hook {
    constructor(object) {
        this.object = object;
    }

    toStringProperty() {
        const handler = {
            apply: function (target, thisArg, args){
                if (thisArg === Function.prototype.toString) {
                    return 'function toString() { [native code] }'
                }

                if (thisArg === this.object) {
                    return "Hooked String"
                }

                return target.apply(thisArg, args)
            }.bind(this) //<--- bind `this` from creation time
        }

        Function.prototype.toString = new Proxy(Function.prototype.toString, handler)
    }
}

let hook = new Hook(HTMLAudioElement);

hook.toStringProperty();

// Interesting enough this when called (I use Devtools) logs Proxy Object itself but only happen if I use a Class
console.log(Function.prototype.toString)

console.log(HTMLAudioElement.toString())

捕获变量中的值

这避免了 this 的使用,方法是在创建时使用 const obj = this.object 捕获 this.object 的值,然后仅使用 obj ,后者将始终具有相同的值:

class Hook {
    constructor(object) {
        this.object = object;
    }

    toStringProperty() {
        const obj = this.object; //<--- capture 
        const handler = {
            apply: function (target, thisArg, args){
                if (thisArg === Function.prototype.toString) {
                    return 'function toString() { [native code] }'
                }

                if (thisArg === obj) { //<--- use
                    return "Hooked String"
                }

                return target.apply(thisArg, args)
            }
        }

        Function.prototype.toString = new Proxy(Function.prototype.toString, handler)
    }
}

let hook = new Hook(HTMLAudioElement);

hook.toStringProperty();

// Interesting enough this when called (I use Devtools) logs Proxy Object itself but only happen if I use a Class
console.log(Function.prototype.toString)

console.log(HTMLAudioElement.toString())