this关键字是JavaScript中最复杂的机制之一。它是一个很特别的关键字,被自动定义在全部函数的做用域中。对于那些没有投入时间学习this机制的JavaScript开发者来讲,this的指向一直是一件很是使人困惑的事。javascript
学习this的第一步是明白this既不指向函数自身也不指向函数的词法做用域,你也许被这样的解释误导过,但其实它们都是错误的。随着函数使用场合的不一样,this的值会发生变化。但总有一条原则就是JS中的this表明的是当前行为执行的主体,在JS中主要研究的都是函数中的this,但并非说只有在函数里才有this,this其实是在函数被调用时发生的绑定,它指向什么彻底取决于函数在哪里被调用。如何的区分this呢?java
这要分状况讨论,常见有五种状况:git
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();
(function(){ //this->window })(); ~function(){ //this->window }();
oDiv.onclick=function(){ //this->oDiv };
oDiv.addEventListener("click",function(){ //this->oDiv },false);
oDiv.attachEvent("click",function(){ //this->window });
咱们大多数时候,遇到事件绑定,以下面例子这种,对于IE6~8下使用attachEvent没必要太较真github
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 };
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
咱们先来看一个问题,想在下面的例子中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方法:app
①首先咱们让原型上的call方法执行,在执行call方法的时候,咱们让fn方法中的this变为第一个参数值obj;而后再把fn这个函数执行。函数
②call还能够传值,在严格模式下和非严格模式下,获得值不同。post
//在非严格模式下 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
执行,并且在严格模式下和非严格模式下对于第一个参数是null/undefined这种状况的规
律也是同样的。**学习
二者惟一的区别:call在给fn传递参数的时候,是一个个的传递值的,而apply不是一个个传,而是把要给fn传递的参数值统一的放在一个数组中进行操做。可是也至关子一个个的给fn的形参赋值。总结一句话:call第二个参数开始接受一个参数列表,apply第二个参数开始接受一个参数数组
fn.call(obj,100,200); fn.apply(obj,[100,200]);
fn.call(obj,1,2);//->改变this和执行fn函数是一块儿都完成了 fn.bind(obj,1,2);//->只是改变了fn中的this为obj,而且给fn传递了两个参数值一、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。
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
文章于2018.9.25从新修改,若是文章对你有些许帮助,欢迎在个人GitHub博客点赞和关注,感激涕零!