共计 4775 个字符,预计需要花费 12 分钟才能阅读完成。
一、前言
this 关键字是 JavaScript 中最简单的机制之一。它是一个很特地的关键字,被主动定义在所有函数的作用域中。对于那些没有投入工夫学习 this 机制的 JavaScript 开发者来说,this 的绑定始终是一件十分令人困惑的事。
二、理解 this
学习 this 的第一步是明确 this 既不指向函数本身也不指向函数的词法作用域,你兴许被这样的解释误导过,但其实它们都是谬误的。随着函数应用场合的不同,this 的值会发生变化。但总有一条准则就是JS 中的 this 代表的是以后行为执行的主体,在 JS 中次要钻研的都是函数中的 this,但并不是说只有在函数里才有 this,this 实际上是在函数被调用时产生的绑定,它指向什么齐全取决于函数在哪里被调用。如何的辨别 this 呢?
三、this 到底是谁
这要分状况探讨,常见有五种状况:
1、函数执行时首先看函数名后面是否有 ”.”,有的话,”.” 后面是谁,this 就是谁;没有的话 this 就是 window
function fn(){console.log(this);
}
var obj={fn:fn};
fn();//this->window
obj.fn();//this->obj
function sum(){fn();//this->window
}
sum();
var oo={sum:function(){console.log(this);//this->oo
fn();//this->window}
};
oo.sum();
2、自执行函数中的 this 永远是 window
(function(){//this->window})();
~function(){ //this->window}();
3、给元素的某一个事件绑定办法,当事件触发的时候,执行对应的办法,办法中的 this 是以后的元素,除了 IE6~8 下应用 attachEvent(IE 一个驰名的 bug)
- DOM 零级事件绑定
oDiv.onclick=function(){//this->oDiv};
- DOM 二级事件绑定
oDiv.addEventListener("click",function(){//this->oDiv},false);
- 在 IE6~8 下应用 attachEvent,默认的 this 就是指的 window 对象
oDiv.attachEvent("click",function(){//this->window});
咱们大多数时候,遇到事件绑定,如上面例子这种,对于 IE6~8 下应用 attachEvent 不用太较真
function fn(){console.log(this);
}
document.getElementById("div1").onclick=fn;//fn 中的 this 就是 #divl
document.getElementById("div1").onclick=function(){console.log(this);//this->#div1
fn();//this->window};
4、在构造函数模式中,类中 (函数体中) 呈现的 this.xxx=xxx 中的 this 是以后类的一个实例
function CreateJsPerson(name,age){
// 浏览器默认创立的对象就是咱们的实例 p1->this
this.name=name;//->p1.name=name
this.age=age;
this.writeJs=function(){console.log("my name is"+this.name +",i can write Js");
};
// 浏览器再把创立的实例默认的进行返回
}
var p1=new CreateJsPerson("尹华芝",48);
必须要留神一点:类中某一个属性值(办法),办法中的 this 须要看办法执行的时候,后面是否有 ”.”, 能力晓得 this 是谁。大家无妨看下接下来的这个例子,就可明确是啥意思。
function Fn(){
this.x=100;//this->f1
this.getX=function(){console.log(this.x);//this-> 须要看 getX 执行的时候才晓得
}
}
var f1=new Fn;
f1.getX();//-> 办法中的 this 是 f1,所以 f1.x=100
var ss=f1.getX;
ss();//-> 办法中的 this 是 window ->undefined
5.call、apply 和 bind
咱们先来看一个问题,想在上面的例子中 this 绑定 obj, 怎么实现?
var obj={name:"浪里行舟"};
function fn(){console.log(this);//this=>window
}
fn();
obj.fn();//->Uncaught TypeError:obj.fn is not a function
如果间接绑定 obj.fn(), 程序就会报错。这里咱们应该用 fn.call(obj)就能够实现 this 绑定 obj, 接下来咱们具体介绍下 call 办法:
- call 办法的作用:
①首先咱们让原型上的 call 办法执行,在执行 call 办法的时候,咱们让 fn 办法中的 this 变为第一个参数值 obj;而后再把 fn 这个函数执行。
②call 还能够传值,在严格模式下和非严格模式下,失去值不一样。
// 在非严格模式下
var obj={name:"浪里行舟"};
function fn(num1,num2){console.log(num1+num2);
console.log(this);
}
fn.call(100,200);//this->100 num1=200 num2=undefined
fn.call(obj,100,200);//this->obj num1=100 num2=200
fn.call();//this->window
fn.call(null);//this->window
fn.call(undefined);//this->window
// 严格模式下
fn.call();// 在严格模式下 this->undefined
fn.call(null);// 在严格模式 下 this->null
fn.call(undefined);// 在严格模式下 this->undefined
- **apply 和 call 办法的作用是截然不同的,都是用来扭转办法的 this 关键字并且把办法
执行,而且在严格模式下和非严格模式下对于第一个参数是 null/undefined 这种状况的规
律也是一样的。**
两者惟一的区别:call 在给 fn 传递参数的时候,是一个个的传递值的,而 apply 不是一个个传,而是把要给 fn 传递的参数值对立的放在一个数组中进行操作。然而也相当子一个个的给 fn 的形参赋值。总结一句话:call 第二个参数开始承受一个参数列表,apply 第二个参数开始承受一个参数数组
fn.call(obj,100,200);
fn.apply(obj,[100,200]);
- bind:这个办法在 IE6~8 下不兼容,和 call/apply 相似都是用来扭转 this 关键字的,然而和这两者有显著区别:
fn.call(obj,1,2);//-> 扭转 this 和执行 fn 函数是一起都实现了
fn.bind(obj,1,2);//-> 只是扭转了 fn 中的 this 为 obj,并且给 fn 传递了两个参数值 1、2,然而此时并没有把 fn 这个函数执行
var tempFn=fn.bind(obj,1,2);
tempFn(); // 这样才把 fn 这个函数执行
bind 体现了预处理思维:当时把 fn 的 this 扭转为咱们想要的后果,并且把对应的参数值也筹备好,当前要用到了,间接的执行即可。
call 和 apply 间接执行函数,而 bind 须要再一次调用。
var a ={
name : "Cherry",
fn : function (a,b) {console.log( a + b)
}
}
var b = a.fn;
b.bind(a,1,2)
上述代码没有执行,bind 返回扭转了上下文的一个函数,咱们必须要手动去调用:
b.bind(a,1,2)() //3
必须要申明一点:遇到第五种状况(call apply 和 bind), 后面四种全副退让。
四、箭头函数 this 指向
箭头函数正如名称所示那样应用一个“箭头”(=>)来定义函数的新语法,但它优于传统的函数, 次要体现两点:更简短的函数并且不绑定 this。
var obj = {
birth: 1990,
getAge: function () {
var b = this.birth; // 1990
var fn = function () {return new Date().getFullYear() - this.birth; // this 指向 window 或 undefined};
return fn();}
};
当初,箭头函数齐全修复了 this 的指向,箭头函数没有本人的 this,箭头函数的 this 不是调用的时候决定的,而是在定义的时候处在的对象就是它的 this。
换句话说,箭头函数的 this 看外层的是否有函数,如果有,外层函数的 this 就是外部箭头函数的 this,如果没有,则 this 是 window。
<button id="btn1"> 测试箭头函数 this_1</button>
<button id="btn2"> 测试箭头函数 this_2</button>
<script type="text/javascript">
let btn1 = document.getElementById('btn1');
let obj = {
name: 'kobe',
age: 39,
getName: function () {btn1.onclick = () => {console.log(this);//obj
};
}
};
obj.getName();
</script>
上例中,因为箭头函数不会创立本人的 this, 它只会从本人的作用域链的上一层继承 this。其实能够简化为如下代码:
let btn1 = document.getElementById('btn1');
let obj = {
name: 'kobe',
age: 39,
getName: function () {console.log(this)
}
};
obj.getName();
那如果上一层并不存在函数,this 指向又是谁?
<button id="btn1"> 测试箭头函数 this_1</button>
<button id="btn2"> 测试箭头函数 this_2</button>
<script type="text/javascript">
let btn2 = document.getElementById('btn2');
let obj = {
name: 'kobe',
age: 39,
getName: () => {btn2.onclick = () => {console.log(this);//window
};
}
};
obj.getName();
</script>
上例中,尽管存在两个箭头函数,其实 this 取决于最外层的箭头函数, 因为 obj 是个对象而非函数,所以 this 指向为 Window 对象
因为 this 在箭头函数中曾经依照词法作用域绑定了,所以,用 call()或者 apply()调用箭头函数时,无奈对 this 进行绑定,即传入的第一个参数被疏忽:
var obj = {
birth: 1990,
getAge: function (year) {
var b = this.birth; // 1990
var fn = (y) => y - this.birth; // this.birth 仍是 1990
return fn.call({birth:2000}, year);
}
};
obj.getAge(2018); // 28
扩大浏览
箭头函数 - 廖雪峰
JS 中的箭头函数与 this
this、apply、call、bind