浅谈-this

66次阅读

共计 3270 个字符,预计需要花费 9 分钟才能阅读完成。

浅谈 -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

正文完
 0