我们需要记住,this是在运行时进行绑定的,并不是在定义时绑定,它的context取决于函数调用时的各种条件,简单来说this的绑定和函数声明的位置没有任何关系,只取决于函数的调用方式,再简单来说this永远指向调用者,但箭头函数除外,接下来我们介绍一下五种this的使用情况。
默认绑定最常用的函数调用类型即独立函数调用,这个也是优先级最低的一个,此时this指向全局对象,注意如果使用严格模式strict mode,那么全局对象将无法使用默认绑定,因此this会变为undefined。
var a = 1; // 变量声明到全局对象中 function f1() { return this.a; } function f2() { "use strict"; return this; } console.log(f1()); // 1 // 实际上是调用window.f1()而this永远指向调用者即window console.log(f2()); // undefined // 实际上是调用 window.f2() 此时由于严格模式use strict所以在函数内部this为undefined 隐式绑定对象属性引用链中只有最顶层或者说最后一层会影响this,同样也是this永远指向调用者,具体点说应该是指向最近的调用者,当然箭头函数除外,另外我们可能有意无意地创建间接引用地情况,这个情况下同样也适用于this指向调用者,在上文分析那部分使用的示例就属于间接引用的情况。
function f() { console.log(this.a); } var obj1 = { a: 1, f: f }; var obj2 = { a: 11, obj1: obj1 }; obj2.obj1.f(); // 1 // 最后一层调用者即obj1 function f() { console.log(this.a); } var obj1 = { a: 1, f: f }; var obj2 = { a: 11, }; obj2.f = obj1.f; // 间接引用 obj2.f(); // 11 // 调用者即为obj2 显示绑定如果我们想把某个函数强制在某个环境即对象上,那么就可以使用apply、call、bind强制绑定this去执行即可,每个Function对象都存在apply()、call()、bind()方法,其作用都是可以在特定的作用域中调用函数,等于设置函数体内this对象的值,以扩充函数赖以运行的作用域,此外需要注意使用bind绑定this的优先级是大于apply和call的,即使用bind绑定this后的函数使用apply和call是无法改变this指向的。
window.name = "A"; // 挂载到window对象的name document.name = "B"; // 挂载到document对象的name var s = { // 自定义一个对象s name: "C" } var rollCall = { name: "Teacher", sayName: function(){ console.log(this.name); } } rollCall.sayName(); // Teacher // apply rollCall.sayName.apply(); // A // 不传参默认绑定window rollCall.sayName.apply(window); // A // 绑定window对象 rollCall.sayName.apply(document); // B // 绑定document对象 rollCall.sayName.apply(s); // C // 绑定自定义对象 // call rollCall.sayName.call(); // A // 不传参默认绑定window rollCall.sayName.call(window); // A // 绑定window对象 rollCall.sayName.call(document); // B // 绑定document对象 rollCall.sayName.call(s); // C // 绑定自定义对象 // bind // 最后一个()是为让其执行 rollCall.sayName.bind()(); //A // 不传参默认绑定window rollCall.sayName.bind(window)(); //A // 绑定window对象 rollCall.sayName.bind(document)(); //B // 绑定document对象 rollCall.sayName.bind(s)(); // C // 绑定自定义对象 new绑定在JavaScript中new是一个语法糖,可以简化代码的编写,可以批量创建对象实例,在new的过程实际上进行了以下操作。
创建一个空的简单JavaScript对象即{}。
链接该对象(即设置该对象的构造函数)到另一个对象。
将步骤1新创建的对象作为this的上下文context。
如果该函数没有返回对象,则返回步骤1创建的对象。
function _new(base,...args){ var obj = {}; obj.__proto__ = base.prototype; base.apply(obj, args); return obj; } function Funct(a) { this.a = a; } var f1 = new Funct(1); console.log(f1.a); // 1 var f2 = _new(Funct, 1); console.log(f2.a); // 1 箭头函数箭头函数没有单独的this,在箭头函数的函数体中使用this时,会取得其上下文context环境中的this。箭头函数调用时并不会生成自身作用域下的this,它只会从自己的作用域链的上一层继承this。由于箭头函数没有自己的this指针,使用apply、call、bind仅能传递参数而不能动态改变箭头函数的this指向,另外箭头函数不能用作构造器,使用new实例化时会抛出异常。
window.name = 1; var obj = { name: 11, say: function(){ const f1 = () => { return this.name; } console.log(f1()); // 11 // 直接调用者为window 但是由于箭头函数不绑定this所以取得context中的this即obj对象 const f2 = function(){ return this.name; } console.log(f2()); // 1 // 直接调用者为window 普通函数所以 return this.name; } } console.log(obj.say()); // 11 // 直接调用者为obj 执行过程中的函数内context的this为obj对象 示例 function s(){ console.log(this); } // window中直接调用 // 非 use strict s(); // Window // 等同于window.s(),调用者为window // window是Window的一个实例 // window instanceof Window //true // 新建对象s1 var s1 = { t1: function(){ // 测试this指向调用者 console.log(this); // s1 s(); // Window // 此次调用仍然相当 window.s(),调用者为window }, t2: () => { // 测试箭头函数,this并未指向调用者 console.log(this); }, t3: { // 测试对象中的对象 tt1: function() { console.log(this); } }, t4: { // 测试箭头函数以及非函数调用this并未指向调用者 tt1: () => { console.log(this); } }, t5: function(){ // 测试函数调用时箭头函数的this的指向,其指向了上一层对象的调用者 return { tt1: () => { console.log(this); } } } } s1.t1(); // s1对象 // 此处的调用者为 s1 所以打印对象为 s1 s1.t2(); // Window s1.t3.tt1(); // s1.t3对象 s1.t4.tt1(); // Window s1.t5().tt1(); // s1对象 每日一题 https://github.com/WindrunnerMax/EveryDay 参考 https://juejin.cn/post/6882527259584888845 https://www.cnblogs.com/raind/p/10767622.html