this .fullscreenMode.bind(this)这是什么意思

this.fullscreenMode.bind(this) 是在 JavaScript 中出现的一种绑定函数上下文(context binding)的表达式,主要用于确保在调用函数时 this 关键字指向正确的对象。

在 JavaScript 中,this 关键词的值取决于函数调用时的上下文。如果不进行绑定,当函数作为事件处理器、定时器回调或者传递给其他函数调用时,this 可能会指向错误的对象,例如在事件处理器中可能会指向触发事件的 DOM 元素,而不是你期望的对象。

bind() 方法则是 JavaScript 中的一个内置方法,它会创建一个新的函数,这个新函数在调用时不论在哪里调用,其内部的 this 都会指向 bind() 方法的第一个参数所指定的对象。

所以,this.fullscreenMode.bind(this) 的含义是:

fullscreenMode 是一个对象(假设是当前对象)的成员函数。
.bind(this) 是对 fullscreenMode 函数的调用,目的是创建一个新的函数副本,确保无论何时调用这个副本函数,this 都会指向调用 bind() 方法时的当前对象。
通过这样的绑定,当你将 this.fullscreenMode.bind(this) 分配给事件处理器或其他回调函数时,即使在异步调用或事件循环中调用这个函数,它也能正确引用到当前上下文的对象。

这样做和不这样做有什么不同

在 JavaScript 中,this 关键字的上下文在函数调用时会根据调用位置的不同而变化。如果我们不使用 .bind(this) 来绑定上下文,可能会遇到如下几种情况:

匿名函数调用:在普通函数调用或作为表达式的一部分时,this 通常会指向全局对象(在浏览器环境中是 window 对象,在严格模式下是 undefined)。

对象方法调用:当函数作为对象的属性并以对象方式调用时,this 指向该对象。

事件处理函数:在 HTML 事件处理程序中,this 通常指向触发事件的 DOM 元素。

定时器回调:在 setTimeout 或 setInterval 的回调函数中,this 通常指向全局对象(非严格模式下的 window)。

构造函数:在构造函数中,this 指向新创建的对象实例。

使用 .bind(this) 的好处:

确保上下文一致性:当你需要确保 this 总是指向当前对象实例时,使用 .bind(this) 可以明确指定函数调用时的上下文,即使函数在将来以某种非预期方式(如事件处理程序、回调函数)被调用时也是如此。
例如,假设有一个对象:

Javascript
let obj = {
fullscreenMode: function() {
// 使用 this 操作对象内部的属性或方法
console.log(this.someProperty);
},
init: function() {
document.addEventListener('click', this.fullscreenMode); // 不使用.bind(this),在事件触发时,this 可能指向触发事件的 DOM 元素而非 obj
document.addEventListener('click', this.fullscreenMode.bind(this)); // 使用.bind(this),确保在事件处理程序中,this 始终指向 obj
},
someProperty: 'Hello'
};
在上述代码中,如果不使用 bind(this),当点击事件触发时,fullscreenMode 内部的 this 可能会指向触发事件的 DOM 元素。而使用了 bind(this) 后,this 将始终指向 obj 对象实例。这就是使用和不使用 .bind(this) 的主要区别。

如果使用箭头函数是不是就没这些问题吗

是的,使用箭头函数(Arrow Function)在很大程度上可以避免 this 上下文丢失的问题。箭头函数没有自己的 this 值,它会捕获其所在作用域(lexical scope)的 this 值。这意味着在对象方法中定义的箭头函数会自动继承其外层作用域(通常是该对象)的 this。

例如,对比普通函数和箭头函数:

Javascript
let obj = {
normalFunction: function() {
console.log(this); // 如果在事件处理器中调用,这里的 this 可能指向事件触发元素而非 obj
},

arrowFunction: () => {
console.log(this); // 在箭头函数中,这里的 this 永远指向 obj,因为它捕获了外层作用域的 this
},

init: function() {
document.addEventListener('click', this.normalFunction); // 若不使用 .bind(this),normalFunction 内部的 this 可能指向错误的对象
document.addEventListener('click', this.arrowFunction); // 箭头函数无需 .bind(this),其内部的 this 自动指向 obj
}
};

obj.init();
在上述示例中,当使用箭头函数作为事件处理器时,不需要使用 .bind(this),因为箭头函数的 this 已经自动绑定了正确的上下文。然而,箭头函数也有一些限制,例如它们不能作为构造函数,也不能使用 arguments 对象,因此在特定场景下可能仍需使用普通函数并结合 .bind(this) 来维护上下文。

class SampleModal extends Modal { constructor(app: App) { super(app); }

Code
onOpen() {
const {contentEl} = this;
contentEl.setText('Woah!');
}

onClose() {
const {contentEl} = this;
contentEl.empty();
}
} 这个类里的函数是箭头函数吗

这个类 SampleModal 中的 onOpen 和 onClose 函数不是箭头函数。它们是普通的 JavaScript 类方法,通过 function 关键字定义的。箭头函数的定义格式是 () => {},而这里的函数定义格式是 function onOpen() {} 和 function onClose() {}。

那就是说如果把这些类里的函数用到回调上必须 bind this

不一定必须使用 bind(this)。在这个 SampleModal 类中,由于 onOpen 和 onClose 方法是在类的实例方法中直接调用的,因此它们默认会绑定到调用它们的类实例上,也就是说,这里的 this 会指向正确的 SampleModal 实例。

但如果要在回调函数中调用这些方法,例如在事件监听器或者其他异步上下文中,此时 this 可能会改变,从而指向调用该函数的对象(如 DOM 元素或全局对象)。在这种情况下,如果需要保持对 SampleModal 实例的引用,就需要使用 .bind(this) 来手动绑定上下文,或者使用箭头函数来自动捕获当前上下文的 this。

举个例子:

Javascript
class SampleModal extends Modal {
constructor(app) {
super(app);

    document.addEventListener('click', this.onOpen.bind(this)); // 使用 bind 绑定 this
    // 或者
    document.addEventListener('click', () => this.onOpen()); // 使用箭头函数捕获 this
}

// ...

}
在以上代码中,若不使用 .bind(this) 或箭头函数,this.onOpen 直接作为事件处理器时,在事件触发时 this 可能不会指向 SampleModal 实例。而在使用了 .bind(this) 或箭头函数后,就能确保 this 在事件处理函数中指向正确的 SampleModal 实例。

如果类方法中调用命名函数,那匿名函数里的 this 是指向类本身吗

在类方法中调用命名函数(即非箭头函数的普通函数),该命名函数内部的 this 并不会自动指向类的实例。this 的指向取决于函数调用的位置和方式。

例如:

class MyClass {
    constructor() {
        this.value = 'FromClass';
    }

    myMethod() {
        const namedFunction = function() {
            console.log(this.value); // 这里的 this 不指向 MyClass 实例
        };

        namedFunction(); // 直接调用,此时 this 通常指向全局对象(浏览器环境为 window)
    }
}

const instance = new MyClass();
instance.myMethod(); // 输出 undefined 或报错,因为 this.value 在全局作用域中未定义

在上述例子中,namedFunction 内部的 this 不会指向 MyClass 的实例。如果需要在命名函数中访问类实例的属性和方法,你需要手动绑定 this,或者使用闭包等方式来保存对类实例的引用。

若要让 this 指向类实例,可以这样操作:

class MyClass {
    constructor() {
        this.value = 'FromClass';
    }

    myMethod() {
        const self = this; // 使用闭包保存对类实例的引用
        const namedFunction = function() {
            console.log(self.value); // 使用 self 访问类实例的属性
        };

        namedFunction.call(this); // 使用 call 或 apply 显式绑定 this
    }
}

const instance = new MyClass();
instance.myMethod(); // 输出 "FromClass"
或者使用箭头函数,箭头函数会捕获其所在作用域的 thisJavascript
class MyClass {
    constructor() {
        this.value = 'FromClass';
    }

    myMethod = () => { // 使用箭头函数声明类方法
        const namedFunction = () => {
            console.log(this.value); // 箭头函数内部的 this 指向 MyClass 实例
        };

        namedFunction();
    }
}

const instance = new MyClass();
instance.myMethod(); // 输出 "FromClass"

call 和 apply 有什么区别

call() 和 apply() 都是 JavaScript 中函数对象的方法,它们的主要功能是调用一个函数,并可以改变函数调用时的上下文(即 this 的值),同时还可以灵活地传递参数。两者的区别主要在于参数传递的方式:

call() 方法:

Javascript
function.call(thisArg, arg1, arg2, ..., argN);
call() 方法接受至少两个参数,第一个参数是要绑定的上下文对象(即 this 指向的对象),后续参数是传递给被调用函数的实际参数,每个参数单独列出。
apply() 方法:

Javascript
function.apply(thisArg, [arg1, arg2, ..., argN]);
apply() 方法同样接受至少两个参数,第一个参数也是要绑定的上下文对象。
第二个参数是一个数组或者类数组对象,其中包含了传递给被调用函数的所有参数。apply() 方法会将这个数组拆解成一个个单独的参数来传递给目标函数。
总结起来,call() 和 apply() 的主要区别在于:

当你知道确切的参数数量并且参数不需要封装在数组中时,可以使用 call() 方法。
当你需要传递的参数是以数组或类数组对象形式存在的时候,使用 apply() 方法更为方便,因为它可以一次性将数组的所有元素作为参数传递给目标函数。
另外,ES6 引入了 bind() 方法,它与 call() 和 apply() 类似,用于改变函数的上下文并预先设定参数,但它并不会立即执行函数,而是返回一个新的函数。

类中使用 binding

类(Class)本身不能直接使用 bind 方法,因为 bind 是函数对象的一个方法,用于创建一个新的函数,在新函数中绑定 this 的上下文以及预先设置参数。类是一种特殊的函数,但当我们直接对类使用 bind 时,并不会得到预期的效果,因为它并非可调用的对象。

如果你想在类的实例方法中保持对类实例 this 的引用,通常有以下几种做法:

  1. 在构造函数中绑定实例方法:
class Menu {
  constructor() {
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    // 这里的 this 会指向 Menu 类的实例
    console.log(this);
  }
}
  1. 使用箭头函数定义实例方法:
class Menu {
  handleClick = () => {
    // 这里的 this 会自动捕获外围作用域的 this,也就是 Menu 类的实例
    console.log(this);
  }
}
  1. 如果是类静态方法,你可以直接在方法内部使用 this,因为它指的是类自身而不是类的实例:
class Menu {
  static createMenuItem() {
    // 这里的 this 指向 Menu 类
    console.log(this);
  }
}

总之,bind 方法主要是针对函数的,而非类。如果你想在类的实例方法中确保 this 的指向,可以参考上述两种方法。