您的位置:首页 > Web前端 > JavaScript

【笔记】探索js 的this 对象 (第二部分)

2017-03-21 23:22 477 查看
了解this对象之后我们明白了this对象就是指向调用函数的作用域
那么接下来我们便要清除函数究竟在哪个作用域调用
找到调用的作用域首先要了解一下几点:

1、调用栈:调用栈就是一系列的函数,表明当前函数是由哪些上层函数调用的包括它自身,我们关心的调用位置就在当前正在执行的函数的前一个调用中。

functionbaz(){
//调用栈:baz函数的内部

console.log("baz函数内部");
bar();
}

functionbar(){
//调用栈分别是baz->bar
//调用位置为baz因此bar的this指针指向baz

console.log("bar函数内部");
foo();
}

functionfoo(){
//调用栈分别为baz->bar->foo
//调用位置是bar内部因此foo的this指针指向了bar

console.log("foo函数的内部");
}

baz()//baz的调用位置是全局作用域


这段代码的意思是,函数再哪个区域被调用那个区域就是它的执行环境

找到了调用位置就要理解一下函数对this对象的绑定了
一般分为一下4种:

1、默认绑定

指函数在调用的过程中没有任何干涉调用位置的代码浏览器引擎根据调用栈默认绑定

默认绑定,函数的this对象会指向了window对象而在严格模式下函数访问this会显示undefined

例子:

functionfoo(){
console.log(this.a);
}
vara="i'mwindow";
foo();//输出2因为全局变量也是window对象的属性


2、隐式绑定

函数没有调用任何方法绑定而是在调用时根据对象上下文绑定了this的作用域

看一下下面的例子

functionfoo(){
console.log(this.a);
}

varobj={
a:"i'mobj",
foo:foo//对象obj中包含着foo的引用
}

obj.foo();

//通过obj调用函数foo,所以foo的this对象绑定在了obj上
//严格来说函数foo不属于obj对象但由于函数是通过obj引用并调用的所以this指向了obj



另外对象调用链中只有最靠近被调用函数那层会影响函数的调用位置

即:函数在某一个对象里面通过那个对象调用函数this永远指向那个对象

例如:

functionbar(){
console.log(this.a);
}

varobj2={
a:"i'mobj2",
bar:bar,
}

varobj1={
a:"i'mobj1",
obj2:obj2,
bar:bar
}

obj1.obj2.bar();


这里的意思是:obj1调用obj2然后obj2再调用bar这个函数引用

从逻辑上说bar这个函数调用怎么也是属于obj2吧

另外dom的事件绑定回调函数也会把this绑定在执行事件的dom元素对象上

小分支:

2.1、隐式丢失

隐式绑定的函数会丢失原来的this所引用的对象
丢失后的this会引用到全局对象或undefined

例1:
把函数的引用赋值给全局变量会丢失原来的this绑定

functionfoo(){
console.log(this.a);
}

varobj={
a:"i'mobj",
foo:foo
}

varbar=obj.foo;

//把objfoo属性里面对foo函数的引用赋值给了全局变量bar

vara="globalwindow";
bar();


这里的函数foo的引用虽然是obj里面的一个属性值,但是后面却把函数的引用赋值给了一个全局变量并且在全局环境执行,所以foo的this对象指向了window对象

其实这里真正的误区是我们一直以为在对象字面量中属性值为函数就是函数this绑定的区域,其实不然

回顾一下第一部分的话,函数在哪里调用,那里才是它this对象绑定的环境

所以这次函数foo的引用在(保存在了bar中)在全局调用所以this指向了全局

例2:

把函数的引用当作参数传进另外一个函数被执行会造成隐式丢失

functionfoo(){
console.log(this.a);
}

functiondofoo(fn){
fn();
}

varobj={
a:"i'mobj",
foo:foo
}

vara="globalwindow";

dofoo(obj.foo);


这里dofoo的输出结果是"globalwindow"

同时,回调函数也会造成隐式丢失

setTimeout(obj.foo,1000);//"globalwindow"


3、显式绑定

通过语言内置的方法将函数硬性绑定在了某一对象上执行
例如通过call(),apply(),bind()方法
此时函数的this对象绑定在了bind()和apply()方法的参数上

例如:

functionfoo(){
console.log(this.a);
}

varobj={
a:"i'mobj",
}

//把foo函数绑定在obj上执行

foo.call(obj);//i'mobj


3.1、硬绑定

通过提供的方法,将函数绑定在某一对象上执行
例:

functionfoo(){
console.log(this.a);
}

varobj={
a:"i'mobj",
}

setTimeout(functiontimer(){
//回调函数通常会丢失this
//在回调函数中使用硬性绑定就不会出现这个现状了

foo.call(obj);//把foo硬性绑定在obj上执行

},1000);//i'mobj

obj.call(window);//硬性绑定之后无法再绑定其他对象


apply和call方法能够执行这种硬性绑定

除了这两个es5语法还定义了新方法bind()

例:

functionfoo(num){
returnthis.a+num;
}

varobj={
a:2
}

varbar=foo.bind(obj);//把foo函数的引用绑定在了obj上

varresult=bar(3);

console.log(result);//5


new绑定:

js的new关键字不等同于其他面向对象语言里的new
js的new关键字仅仅是调用它的目标函数而已
1、调用new关键字会创建一个新对象
2、新对象会与函数的原型连接
3、将函数的this绑定到了新对象上
4、如果函数没有返回指定的新对象那么就会返回一个默认的对象

例:

functionfoo(){
this.a="whocantakemego";
}

varbar=newfoo();
console.log(bar.a);


上例中我们通过new将foo函数实例化并将this指针指向了bar
于是bar便拥有了函数中的a属性

以上四种绑定的优先级

默认绑定最低
隐式绑定低于显示绑定
隐式绑定又低于new绑定
而new与显示绑定其实两个本身的并没有什么影响很难说明优先级

如下例:

functionfoo(arg1){
this.a=arg1;
}

varobj={};

varbar=foo.bind(obj);
//将foo函数的引用绑定在obj上
//然后将obj上对函数foo的引用传值给bar

bar(2);
console.log(obj.a);

//现在将引用这foo.bind(obj)的变量bar实例化
varbaz=newbar(5);
console.log(baz.a);//5
//此操作没有修改obj的属性a值,并且把新值添加到了新的对象上baz


小结:

这部分内容主要讲述了函数的this绑定位置以及各种的绑定方式,有如下结论

1、当函数被new实例化时,this对象指向新返回的对象

2、当函数调用了call(),apply()或者bind(),this绑定在指定的对象

3、函数在某个对象字面量里面调用,this绑定在了这个对象字面量里面

4、以上都没有被操作,在非严格模式下函数的this指向了window对象,严格模式下为undefined
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: