其实this是一个老生常谈的问题了。关于this的文章很是多,其实我本觉得本身早弄明白了它,不过昨天在作项目的过程当中,仍是出现了一丝疑惑,想到大概以前在JavaScript weekly里收藏待看的一篇详解this的文章(后有连接,也附上了稀土上的中文译文)和另外一篇一位前辈推荐的文章,就把它们看了看,对this的认识确实提高了一些。javascript
JavaScript 中的’this‘是动态的,它在函数运行时被肯定而非在函数声明时被肯定。全部的函数均可以调用'this',这无关于该函数是否属于某个对象。关于this,主要有如下四种状况。java
若是该函数是被当作某一个对象的方法,那么该函数的this指向该对象;数组
var john = { firstName: "John" } function func() { alert(this.firstName + ": hi!") } john.sayHi = func john.sayHi() // this = john
这里有一点值得注意,当一个对象的方法被取出来赋值给一个变量时,该方法变为函数触发,this指向window或underfind(严格模式)。浏览器
当函数中有 this
,其实就意味着它被当作方法调用,之间调用至关于把他当作window对象的方法,this指向window,值得注意的是ES5实际上是规定这种状况this=undefined
的,只浏览器大多仍是按照老的方法执行(本人在最新版的Chrome,Safari,Firefox中测试都指向window(201607)),在火狐下使用严格模式指向undefined;app
func() function func() { alert(this) // [object Window] or [object global] or kind of.. }
为了传递this
,()以前应该为引用类型,相似于obj.a
或者 obj['a']
,不能是别的了。函数
这里还存在一个小坑,当对象的方法中还存在函数时,该函数实际上是当作函数模式触发,因此其this
默认为window(严格模式下为undefined)解决办法是给该函数绑定this。测试
var numbers = { numberA: 5, numberB: 10, sum: function() { console.log(this === numbers); // => true function calculate() { // this is window or undefined in strict mode console.log(this === numbers); // => false return this.numberA + this.numberB; } return calculate(); } }; numbers.sum(); // => NaN or throws TypeError in strict mode
var numbers = { numberA: 5, numberB: 10, sum: function() { console.log(this === numbers); // => true function calculate() { console.log(this === numbers); // => true return this.numberA + this.numberB; } // use .call() method to modify the context return calculate.call(this); } }; numbers.sum(); // => 15
new
中调用一个引用对象的变量实际上保存了对该对象的引用,也就是说变量实际保存的是对真实数据的一个指针。
使用new关键字时this
的改变其实有如下几步:this
建立 this = {}
.prototype
new执行的过程当中可能改变this
,而后添加属性和方法;指针
返回被改变的this
.
function Animal(name) { this.name = name this.canWalk = true } var animal = new Animal("beastie") alert(animal.name)
须要注意的是若是构造函数返回一个对象,那么this指向返回的那个对象;
function Animal() { this.name = 'Mousie'; this.age = '18'; return { name: 'Godzilla' } // <-- will be returned } var animal = new Animal() console.log(animal.name) // Godzilla console.log(animal.age)//undefined
这里须要注意的是不要忘记使用new,不然不会建立一个新的函数。而是只是执行了函数,至关于函数调用,this其实指向window
function Vehicle(type, wheelsCount) { this.type = type; this.wheelsCount = wheelsCount; return this; } // Function invocation var car = Vehicle('Car', 4); car.type; // => 'Car' car.wheelsCount // => 4 car === window // => true
this
,使用call
和apply
这是最具JavaScript特点的地方。
以下代码:
func.call(obj, arg1, arg2,...)
第一个参数将做为this
的指代对象,以后的参数将被做为函数的参数,解决方法是使用bind。
function Animal(type, legs) { this.type = type; this.legs = legs; this.logInfo = function() { console.log(this === myCat); // => true console.log('The ' + this.type + ' has ' + this.legs + ' legs'); }; } var myCat = new Animal('Cat', 4); // logs "The Cat has 4 legs" setTimeout(myCat.logInfo.bind(myCat), 1000); // setTimeout??
var john = { firstName: "John", surname: "Smith" } function func(a, b) { alert( this[a] + ' ' + this[b] ) } func.call(john, 'firstName', 'surname') // "John Smith"
至于apply,其只是以数组的方传入参数,其它部分是同样的,以下:
func.call(john, 'firstName', 'surname') func.apply(john, ['firstName', 'surname'])
它们也可用于在 ES5 中的类继承中,调用父级构造器。
function Runner(name) { console.log(this instanceof Rabbit); // => true this.name = name; } function Rabbit(name, countLegs) { console.log(this instanceof Rabbit); // => true // 间接调用,调用了父级构造器 Runner.call(this, name); this.countLegs = countLegs; } var myRabbit = new Rabbit('White Rabbit', 4); myRabbit; // { name: 'White Rabbit', countLegs: 4 }
.bind()
对比方法 .apply() 和 .call(),它俩都当即执行了函数,而 .bind() 函数返回了一个新方法,绑定了预先指定好的 this ,并能够延后调用。
.bind() 方法的做用是建立一个新的函数,执行时的上下文环境为 .bind() 传递的第一个参数,它容许建立预先设置好 this 的函数。
var numbers = { array: [3, 5, 10], getNumbers: function() { return this.array; } }; // Create a bound function var boundGetNumbers = numbers.getNumbers.bind(numbers); boundGetNumbers(); // => [3, 5, 10] // Extract method from object var simpleGetNumbers = numbers.getNumbers; simpleGetNumbers(); // => undefined or throws an error in strict mode
使用.bind()
时应该注意,.bind() 建立了一个永恒的上下文链并不可修改。一个绑定函数即便使用 .call() 或者 .apply()传入其余不一样的上下文环境,也不会更改它以前链接的上下文环境,从新绑定也不会起任何做用。
只有在构造器调用时,绑定函数能够改变上下文,然而这并非特别推荐的作法。
箭头函数并不建立它自身执行的上下文,使得 this 取决于它在定义时的外部函数。
箭头函数一次绑定上下文后便不可更改,即便使用了上下文更改的方法:
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);
这是由于箭头函数拥有静态的上下文环境,不会由于不一样的调用而改变。所以不要使用箭头函数定义方法
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); walkPeriod.format(); // => 'undefined hours and undefined minutes'
强烈推荐以为没弄明白的同窗看看上面三篇文章,其中第三篇是第二篇的译文。若是你们对this还有疑问,也欢迎你们一块儿讨论,交流促进思考,共同进步。