这里来个倒叙,先说一下this的几种状况指向:javascript
下面开始:php
我本身结合网上的定义,给出了this的定义:java
函数中this指向了执行时,调用它的而且离它最近的对象
不过这个定义只适用于大部分状况:数组
先看一段代码:markdown
function fn() {
console.log(this);
}
fn();//window
若是你知道在全局做用域中定义函数变量和函数实际是在window变量上添加属性和方法的话,那么上面的代码就很好理解了,上面的代码至关于:app
function fn() {
console.log(this);
}
window.fn();//window
最后调用这个函数的对象是window对象,因此this就指向了window,再看:dom
var obj = {
fn: function() {
console.log(this);
}
}
obj.fn(); //this指向obj,至关于window.obj.fn();
上面的代码咱们能够看到是window的obj对象调用了fn这个函数,因此定义的时候咱们强调是离它最近的调用对象,这里obj离得近,this就指向了obj。函数
定义时还强调了是执行时,是什么意思呢?接着看代码:ui
function fn() {
console.log(this)
}
var obj = {
fn: fn } obj.fn(); //this指向obj
定义时,函数是在全局中定义的,可是执行时咱们是利用obj来调用,它就指向了obj,若是还不太肯定,咱们再看:this
var obj = {
fn: function() {
console.log(this)
}
}
var fn = obj.fn;
fn(); //this指向了window
这下相信了吧,下面介绍几种比较特殊的状况:
匿名函数具备全局性,因此this指向的是window。
(function(){ console.log(this); //window })()
var obj = {
fn: function() {
(function(){
console.log(this);
})()
}
}
obj.fn(); //window
上面第二段代码中,尽管是obj调用了fn函数,可是在fn函数中的匿名函数仍然具备全局性,因此this仍然指向window。
普通调用是什么意思呢?就是形如:fn();不是做为某个对象的方法。上面有段代码:
var obj = {
fn: function() {
console.log(this);
}
}
obj.fn(); //this指向obj
咱们稍微修改一下:
var obj = {
fn: function() {
function innerFn() {
console.log(this);
}
innerFn();
}
}
obj.fn(); //输出window
是否是有些懵逼了,尽管innerFn是在obj的fn函数中被调用的,可是它的做用域链上活动对象只有innerFn和全局自己(ES6之前,JavaScript做用域只有函数域),我猜想,在利用obj.fn()调用的时候,JavaScript内部是作了this指向处理的,而普通调用就指向了全局。
有人可能会问,若是我要调用外层中的this怎么办?一般咱们会使用一个变量来保存this,例如:
var obj = {
fn: function() {
var self = this;
function innerFn() {
console.log(self);
}
innerFn();
}
}
obj.fn(); //输出obj
setTimeout(function(){
console.log(this); //window
}, 1000)
var obj = {
fn: function() {
console.log(this);
}
}
setTimeout(obj.fn, 1000) //输出window
setTimeout回调函数你能够看作是下面这样:
var obj = {
fn: function() {
console.log(this);
}
}
var callback = obj.fn;
//在设定时间后执行回调函数
callback();
上面你可能就很熟悉了,调用fn函数的是全局对象,因此指向了window对象,若是你想改变定时器中函数this的指向,可使用bind函数:
var obj = {
fn: function() {
console.log(this);
}
}
setTimeout(obj.fn.bind(obj), 1000) //输出obj
在JavaScript中咱们能够这样绑定一个事件:
<div id="div">这是一个div元素</div>
function doClickDiv(e) {
//to do something
console.log(this);
}
var oDiv = document.getElementById('div');
//绑定点击事件
oDiv.addEventListener('click', doClickDiv, false);
当点击时,输出时this指向了div这个节点,早期绑定事件的写法能够帮助咱们理解:
function doClickDiv(e) {
//to do something
console.log(this);
}
var oDiv = document.getElementById('div');
//绑定点击事件
oDiv.onclick = doClickDiv;
当点击div时,会触发oDiv.onclick函数,至关于oDiv.onclick(),这和使用对象调用是同样的。
JavaScript中的函数是能够做为构造函数的,使用new便可,那么this在这种状况下指向是什么?
function Person() {
this.age = 18;
this.job = 'student';
}
var person = new Person();
console.log(person)//{age: 18, job: 'student'}
咱们能够知道person是一个实例,因此函数做为构造函数时,this是指向实例的,在构造函数中实际是这样的:
function Person() {
//隐藏着的语句
//this = {} 这里只是简单说明this是一个对象,它还要关联Person函数的原型
this.age = 18;
this.job = 'student';
//隐藏着的语句
//return this;
}
var person = new Person();
console.log(person)//{age: 18, job: 'student'}
从上面咱们能够看出,构造函数隐式return了this,因此person就是this,可是当构造函数有return语句时,this并不必定指向person。
当return返回一个对象时:
function Person() {
this.age = 18;
this.job = 'student';
return {
tip: 'this is an object'
}
}
var person = new Person();
console.log(person)//{tip: "this is an object"}
当return回一个非对象值时:
function Person() {
this.age = 18;
this.job = 'student';
return 1;
}
var person = new Person();
console.log(person)//{age: 18, job: 'student'}
有时咱们须要改变this的指向,就能够经过这三个方法函数来实现:
call和apply:
var prop = 'window';
function fn() {
console.log(this.prop);
}
var obj1 = {
prop: 'obj1',
fn: function(){
console.log(this.prop);
}
}
var obj2 = {
prop: 'obj2',
fn: function(){
console.log(this.prop);
}
}
fn(); // 'window'
obj1.fn(); // 'obj1'
obj2.fn(); // 'obj2'
fn.call(obj1) // 'obj1'
fn.apply(obj1) // 'obj1'
obj1.fn.call(obj2); // 'obj2'
obj1.fn.apply(obj2); // 'obj2'
从改变this指向来讲,call和apply是同样的,它们两个函数的区别在于传参数形式的不一样:
var obj = {
a: 1,
b: 2,
c: 3
}
function add(a, b, c) {
console.log(this);
return a + b + c;
}
add.call(obj, obj.a, obj. b, obj.c);
add.apply(obj, [obj.a, obj.b, obj.c]);
能够清楚的看出:call接受的参数是一个一个传进去的,而apply是传一个参数数组进去的。这里插播一个问题:为何要有call、apply同时存在?其实,有时候改变参数的形式是颇有必要,下面分享一个小技巧,当你要求一个数组的最大值时,你会怎么作?传统的作法是用for遍历一遍数组,而后挑出最大值,可是利用apply你就能够直接利用js的内置函数:
var arr = [3, 4, 2, 78];
var max = Math.max.apply(Math, arr); //Math.max的用法是Math.max(1, 3, 6, 2) //6
console.log(max); //78
bind:
bind函数的做用是绑定参数,其中第一个参数就是传入this指向对象,当时undefined或者null时,this指向window,它会返回一个新函数,看代码;
function add(a,b,c){
return a + b + c;
}
add(1,2,3) //6
var addOne = add.bind(null, 1);
addOne(2, 3) //6,至关于add(1, 2, 3);
bind和call的区别在于call是参入参数并执行函数,而bind是传入绑定参数,返回一个新函数。