浅谈-this
this简单而又神秘,使用场景多变而又复杂,这造就了它成为了初级javascript开发人员不愿接触的东西,高级javascript都想探究的东西。文本亦是对this的致敬。
- this是什么?
this是当前执行环境的上下文,通常由函数的调用方式决定其值。 -
this的原理
首先,从函数的调用开始
函数的调用一般有三种情况(ES5中)
1.foo(p1,p2);
2.obj.foo(p1,p2);
3.foo().call(context,p1,p2);//或者apply通常情况下,大部分童鞋常用的都是前两种方式,只有在不得不绑定context的情况下才会使用第三种。其中,第三种方式,才是函数调用的正确方式。
foo().call(context,p1,p2);
之所以,我们在使用前两种方式也能正常的运行,这其实就是一种语法糖而已,在js运行机制中,会把前两种调方式,当做下面的方式进行处理
foo(p1, p2) 等价于 foo.call(undefined, p1, p2) obj.foo(p1, p2) 等价于 obj.foo.call(obj, p1, p2)
因此,如果我们以后再调用函数时,能准确的找到context,就可以唯一的确认this的值
- this的使用场景
-
3.1 全局环境使用
无论是否在严格模式下,在全局执行环境中(在任何函数体外部)this 都指向全局对象。
// 在浏览器中, window 对象同时也是全局对象:
console.log(this === window); // true
var a=1;
console.log(this.a) //1
console.log(window.a) //1 -
3.2 普通函数中调用
在非严格模式下,且this的值没有被调用函数设置,this 的值默认指向全局对象。
funnction func(){ console.log(this) } //浏览器环境中 func() // window //node环境中 func() // global
在严格模式下,this的值如果不设置,就是是undefined
funnction func(){ "use strict" console.log(this) } func() === undefined; // true
-
3.3 作为对象中的方法调用
var obj={
name:'我是对象obj', ofun:function(){ console.log(this.name) } } var name='我是全局name'; var func= obj.ofun; obj.ofun(); //我是对象obj func(); //我是全局name
这里有两点需要注意:
1.函数作为对象的方法进行调用时,this指向当前的对象。 2.对象中的函数方法名存储的仅是一个函数的地址(地址中会有该函数的属性:[[value]、[[writable]]、[[enumerable]]、[[congifable]]) 当把一个对象的方法赋值给另一个变量(例如:func)时,引擎会将函数单独保存在内存中,然后再将函数的地址赋值给foo属性的value属性。
-
3.4 构造函数中使用
this在构造函数中使用,值为实例化的对象
function Fun(name){ this.name=name; this.speak=function(){ console.log(this.name); } } var fun1=new Fun('jack'); fun1.speak(); var fun2=new Fun('rose'); fun2.speak();//rose
-
3.5 bind/call/apply
this再被bind/call/apply强制绑定上下文执行环境时,属于硬绑定。
function foo(somerthing){ console.log(this.a); } var obj={ a:2 } var bar = function(){ foo.call(obj); }; bar(); //2 //硬绑定的bar不可能再修改它的this bar.call(window);//2 }
硬绑定的典型应用场景就是创建一个包裹函数,传入所有的参数并返回接收到的所有值;
function foo(somerthing){ console.log(this.a,something); return this.a +something; } var obj={ a:2 } var bar = function(){ return foo.apply(obj,arguments); }; var b=bar(3);// 2 3 console.log(b);//5
由于硬绑定是一种非常常用的模式,所以在ES5中提供了内置的方法Function.prototype.bind,它的用法如下;
function foo(somerthing){ console.log(this.a,something); return this.a +something; } var obj={ a:2 } var bar=foo.bind(obj); var b=bar(3);// 2 3 console.log(b);//5
call,apply,bind方法的区别:
a:第一个参数都是要绑定的上下文执行环境,即this的值。
b:都可在函数调用时传递参数。call,bind方法需要直接传入,而apply方法需要以数组的形式传入。
c:call,apply方法是在调用之后立即执行函数,而bind方法没有立即执行,需要将函数再执行一遍。有点闭包的味道。
d:改变this对象的指向问题不仅有call,apply,bind方法,也可以使用that/self等变量来固定this的指向。
-
3.6 回调函数中调用
在回调函数中一般有两种情况: 1. 回调函数为匿名函数时,回调函数的this会指向window,需要对回调函数bind(this)。 2. 回调函数为箭头函数时,回调函数的this会指向他的直接上层。
var obj = { name: "test for call-func", ofun: function(){ //undefined 丢失 setTimeout(function(){ console.log(this.name) }, 100); //work well 硬绑定 setTimeout(function(){ console.log(this.name) }.bind(this), 100);//test for call-func // arrow function test for call-func setTimeout( () => console.log(this.id), 100) } }; obj.ofun();
-
3.7 自执行函数
自执行或者立即执行函数中的this指向Window
首先自执行函数的几种调用方式//常见的有两种: (function(){})() (function(){}())//w3c推荐方式 //只有表达式才能被执行符号()执行 +function test(){}();//+ - ! =等运算符可以使前面的函数成为表达式,加上后面()可立即执行。 //函数声明无法立即执行一个函数,但是函数表达式可以 var a = function(){}();//这种是函数表达式,后面有()可以立即执行。成为一个立即执行函数。
var obj = { number: 3, xxx: (function () { console.log(this + '--')//立即执行函数中的this指向window,因为立即执行函数是window调用的 console.log(this.number + '~~~')//删除上面一行,此处this.number返回undefined return function () { console.log(this.number + '】') this.number += 7; console.log(this + '+') } })() } obj.xxx() //相当于obj.xxx.call(),所以此处的this指向obj console.log(obj.number) //10
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。