在 JavaScript 中,this 是指当前函数中正在执行的上下文环境:
- 函数调用
- 方法调用
- 构造函数调用
- 间接调用
- 绑定函数调用
- 箭头函数
- 易错场景
函数调用
函数调用 代表了该函数接收以成对的引号包含,用逗号分隔的不同参数组成的表达式。
函数调用中的this
function sum(a, b) { console.log(this === window); // => true this.myNumber = 20; // 在全局对象中添加 'myNumber' 属性 return a + b; } // sum() 为函数调用 // this 在 sum() 中是全局对象 (window) sum(15, 16); // => 31 window.myNumber; // => 20
当 sum(15, 16) 被调用时,JavaScript 自动将 this 设置为全局对象,即 window。
严格模式下,函数调用中的 this
严格模式由 ECMAScript 5.1 引进,用来限制 JavaScript 的一些异常处理,提供更好的安全性和更强壮的错误检查机制。使用严格模式,只需要将 'use strict' 置于函数体的顶部。这样就可以将上下文环境中的 this 转为 undefined。这样执行上下文环境不再是全局对象,与非严格模式刚好相反。
function multiply(a, b) { 'use strict'; // 开启严格模式 console.log(this === undefined); // => true return a * b; } // 严格模式下的函数调用 multiply() // this 在 multiply() 中为 undefined multiply(2, 5); // => 10
严格模式不仅在当前作用域起到作用,它还会影响内部作用域,即内部声明的一切内部函数的作用域。
function execute() { 'use strict'; // 开启严格模式 function concat(str1, str2) { // 内部函数也是严格模式 console.log(this === undefined); // => true return str1 + str2; } // 在严格模式下调用 concat() // this 在 concat() 下是 undefined concat('Hello', ' World!'); // => "Hello World!" } execute();
方法调用
当在一个对象里调用方法时,this 代表的是对象它自身。
严格模式不仅在当前作用域起到作用,它还会影响内部作用域,即内部声明的一切内部函数的作用域。var calc = { num: 0, increment: function() { console.log(this === calc); // => true this.num += 1; return this.num; } }; // 方法调用,this 指向 calc calc.increment(); // => 1 calc.increment(); // => 2
构造函数调用
function Foo () { console.log(this instanceof Foo); // => true this.property = 'Default Value'; } // 构造函数调用 var fooInstance = new Foo(); fooInstance.property; // => 'Default Value'
间接调用
间接调用表现为当一个函数使用了 .call() 或者 .apply() 方法。.call() 和 .apply() 被用来配置当前调用的上下文环境。
var rabbit = { name: 'White Rabbit' }; function concatName(string) { console.log(this === rabbit); // => true return string + this.name; } // 间接调用 concatName.call(rabbit, 'Hello '); // => 'Hello White Rabbit' concatName.apply(rabbit, ['Bye ']); // => 'Bye White Rabbit'
两个方法最主要的区别为 .call() 接收一组参数,而 .apply() 接收一串参数作为类数组对象传递。
绑定函数调用
绑定函数调用是将函数绑定一个对象,它是一个原始函数使用了 .bind() 方法。
方法 .bind(thisArg[, arg1[, arg2[, ...]]]) 接收第一个参数 thisArg 作为绑定函数在执行时的上下文环境,以及一组参数 arg1, arg2, ... 作为传参传入函数中。 它返回一个新的函数,绑定了 thisArg。
function multiply(number) { 'use strict'; return this * number; } // 创建绑定函数,绑定上下文2 var double = multiply.bind(2); // 调用间接调用 double(3); // => 6 double(10); // => 20
var numbers = { array: [3, 5, 10], getNumbers: function() { return this.array; } }; // 创建一个绑定函数 var boundGetNumbers = numbers.getNumbers.bind(numbers); boundGetNumbers(); // => [3, 5, 10] // 从对象中抽取方法 var simpleGetNumbers = numbers.getNumbers; simpleGetNumbers(); // => undefined 或者严格模式下抛出错误
.bind() 创建了一个永恒的上下文链并不可修改。一个绑定函数即使使用 .call() 或者 .apply()传入其他不同的上下文环境,也不会更改它之前连接的上下文环境,重新绑定也不会起任何作用。
function getThis() { 'use strict'; return this; } var one = getThis.bind(1); // 绑定函数调用 one(); // => 1 // 使用 .apply() 和 .call() 绑定函数 one.call(2); // => 1 one.apply(2); // => 1 // 重新绑定 one.bind(2)(); // => 1 // 利用构造器方式调用绑定函数 new one(); // => Object
补充bind用法 bind()的另一个最简单的用法是使一个函数拥有预设的初始参数。这些参数(如果有的话)作为bind()的第二个参数跟在this(或其他对象)后面,之后它们会被插入到目标函数的参数列表的开始位置,传递给绑定函数的参数会跟在它们的后面。
function list() { return Array.prototype.slice.call(arguments); } var list1 = list(1, 2, 3); // [1, 2, 3] // Create a function with a preset leading argument var leadingThirtysevenList = list.bind(undefined, 37); var list2 = leadingThirtysevenList(); // [37] var list3 = leadingThirtysevenList(1, 2, 3); // [37, 1, 2, 3]
箭头函数
箭头函数的设计意图是以精简的方式创建函数,并绑定定义时的上下文环境。
箭头函数并不创建它自身执行的上下文,使得 this 取决于它在定义时的外部函数。
class Point { constructor(x, y) { this.x = x; this.y = y; } log() { console.log(this === myPoint); // => true setTimeout(()=> { console.log(this === myPoint); // => true console.log(this.x + ':' + this.y); // => '95:165' }, 1000); } } var myPoint = new Point(95, 165); myPoint.log();
箭头函数一次绑定上下文后便不可更改,即使使用了上下文更改的方法.
var numbers = [1, 2]; (function() { var get = () => { console.log(this === numbers); // => true return this; }; console.log(this === numbers); // => true get(); // => [1, 2] // 箭头函数使用 .apply() 和 .call() get.call([0]); // => [1, 2] get.apply([0]); // => [1, 2] // Bind get.bind([0])(); // => [1, 2] }).call(numbers);
易错场景
this 在内部函数中
var numbers = { numberA: 5, numberB: 10, sum: function() { console.log(this === numbers); // => true function calculate() { console.log(this === numbers); // => false return this.numberA + this.numberB; } return calculate(); } }; numbers.sum();
解决办法:return calculate.call(this);
this在回调函数中
function Animal(type, legs) { this.type = type; this.legs = legs; this.logInfo = function() { console.log(this === myCat); // => false console.log('The ' + this.type + ' has ' + this.legs + ' legs'); } } var myCat = new Animal('Cat', 4); setTimeout(myCat.logInfo, 1000);
解决办法:setTimeout(myCat.logInfo.bind(myCat), 1000);
把包含this的方法赋给一个变量
var name = "aa"; var user = { name: 'hhh', sayName: function(){ console.log(this.name); } } var test = user.sayName; test(); // aa
解决办法:var test = user.sayName.bind(user);