Know this,use this!¶
摘录自:link
this应该是一个讨论了很久的话题了。其中,关于this的文章,在很多的博客当中也有很多介绍,但是,以前我都是一知半解的去了解它,就是看博客当中,只介绍了一些情况下的 this 的使用方式,但是也并没有自己去做过总结。刚好是在掘金当中有看到一篇关于this的一些详细文章,文末会附上链接以及英文原文,这里纯粹是自己进行一个总结,以后方便自己进行回顾以及加深印象。希望这篇文章对于你了解this有一定的帮助,文末还有一些练习题噢~希望真的对你们有帮助。(因为写项目过程中,一直被 this 坑过,却找了很久的 bug ,我真是 乐了狗)
在了解this之前,相信大家都应该会知道作用域这个知识点的存在,函数在创建之后,会构建自己的执行环境以及作用域,这是一开始就确定了。但是实际的上下文(context)环境,也可以理解为就是this,它是动态确定的,即在函数运行时才确定this所指向的对象,而非声明时所指向的对象。
关于this,总结起来,主要有以下几个途径能够被运用到。
对象方法中调用this¶
如果函数被当中对象的一个方法进行调用,则this值指向该对象。
var person = { name: 'Alice', sayName: function() { alert('welcome ' + this.name); } } person.sayName(); // this == person, alert: 'welcome Alice'
var name = 'Bob'; var person; // 即上面的定义,此不拓展详细,直接使用 var say = person.sayName; // this == window || undefined say(); // 'welcome Bob' || throw an error: Cannot read property 'name' of undefined(...)
函数内部使用¶
在函数内部当中使用了 this,即函数被当做方法使用,不同于 1 当中作为对象的方法使用,此时调用,是在全局作用域下进行调用,即在window下进行调用,由定义可以知道,在全局作用域下声明一个函数,其自动加为window的一个属性。this此时名正言顺的会指向window,严格模式下为 undefined
function sayThis() { alert(this == window);// true }
那么,当函数中返回一个函数,此时在对象当中调用该方法,其就相当于是函数触发,此时的 this,在不做任何上下文绑定的前提之下,其指向 window(非严格模式)。
var name = 'Bob', person = { name: 'Alice', sayName: function() { console.log(this === person); // true return function() { console.log(this === person); // false console.log(this === window); // true console.log(this.name); // Bob }; } }; person.sayName()();
var name = 'Bob', person = { name: 'Alice', sayName: function() { console.log(this === person);// true return function() { console.log(this === person); // true console.log(this === window); // false console.log(this.name); // Alice }.bind(this); } }; person.sayName()();
new 当中进行使用¶
我们知道在使用 new 方法创建对象的时候,会经过如下这些个过程: 创建对象,将 this 值赋予新的对象 调用构造函数,为 this 添加属性和方法 返回 this 给当前的对象
function Person(name, age) { this.name = name; this.age = age; } var person1 = new Person('Alice', 29); console.log(person1.name); // Alice
function Person(name, age) { this.name = name; this.age = age; return this; } var person1 = Person('Alice', 29); console.log(person1.name); // Alice console.log(window.name); // Alice console.log(person1 === window); // true
function Person(name, age) { this.name = name; this.age = age; return { name: 'Bob' }; } var person1 = new Person('Alice'); console.log(person1.name); // Bobconsole.log(person1.age);// undefined
使用 call、apply 或 bind 改变 this¶
在引用类型 Function当中,函数存在两个方法属性,call 和 apply,在 ECMAScript5当中,加入了 bind 方法。题外话,他们三者区别,应该都知道了吧,不知道的加紧补习呀。
var name = 'Bob';var person = { name: 'Alice', age: 29 } function sayName() { console.log(this.name); } sayName.call(person); // Alice
func.call(context, arg1, arg2, ...) func.apply(context, [arg1, arg2, ...])
并且,使用 bind()方法创建的 上下文,其为永久的上下文环境,不可修改,即使是使用 call 或者 apply方法,也无法修改 this 所指向的值。
var name = 'Bob'; var person = { name: 'Alice', age: 29 } function sayName() { console.log(this.name); } var say = sayName.bind(person); say(); // Alice sayName(); // Bob
箭头函数¶
箭头函数并不创建其自身的上下文,其上下文 this,取决于其在定义时的外部函数。
并且,箭头函数拥有静态的上下文,即一次绑定之后,便不可再修改,即使是用了 第 4 种用途当中的改变上下文的方法,也不为之动容。
var num = [1, 2, 3]; (function() { var showNumber = () => { console.log(this === num); // true console.log(this); // [1, 2, 3] } console.log(this === num); // true showNumber(); // true && [1, 2, 3] showNumber.call([1, 2]); // true && [1, 2, 3] showNumber.apply([1, 2]); // true && [1, 2, 3] showNumber.bind([1, 2])(); // true && [1, 2, 3] }).call(num);
function Period (hours, minutes) { this.hours = hours; this.minutes = minutes; } Period.prototype.format = () => { console.log(this === window); // => true return this.hours + ' hours and ' + this.minutes + ' minutes'; }; var walkPeriod = new Period(2, 30); console.log(walkPeriod.hours); walkPeriod.format(); // => 'undefined hours and undefined minutes'
在全局环境下,还是选用 函数表达式 来进行函数的定义,可以保证正确的上下文环境
function Period (hours, minutes) { this.hours = hours; this.minutes = minutes; } Period.prototype.format = function() { console.log(this === walkPeriod); // => true return this.hours + ' hours and ' + this.minutes + ' minutes'; }; var walkPeriod = new Period(2, 30); walkPeriod.format(); // '2 hours and 30 minutes'
总结¶
this可以说是javascript设计给大家的坑,他的指向令人迷惑,饱受意义。作为前端开发,需要充分理解this的含义,以及他在不同情形下的指向,才能真正避免踩坑。总结一下最常见的坑:
this丢失¶
person = { name: 'gzyejiaqi', sayName: function () { setTimeout(function() { console.log('My name is ' + this.name) }) } }
此时发生了this丢失的问题,
解决方案1: that
person = { name: 'gzyejiaqi', sayName: function () { var that = this // 保存this setTimeout(function() { console.log('My name is ' + that.name) }) } } person.sayName() // "my name is gzyejiaqi"
解决办法2: 箭头函数
person = { name: 'gzyejiaqi', sayName: function () { setTimeout(() => { console.log('My name is ' + this.name) }) } } person.sayName() // "my name is gzyejiaqi"
全局作用域污染¶
function Person(name, age) { this.name = name; this.age = age; }
Person('gzyejiaqi'. '18') console.log(window.name) // gzyejiaqi console.log(window.age) // 18
练习¶
// 练习1 var func = (function(a) { this.a = a; return function(a) { a += this.a; return a; } })(function(a, b) { return a; }(1, 2)) func(4) // ? // 练习2 var x = 10, foo = { x: 20, bar: function() { var x = 30; return this.x; } } console.log(foo.bar()); console.log((foo.bar)()); console.log((foo.bar = foo.bar)()); console.log((foo.bar, foo.bar)());