博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
2.2 .this的绑定规则
阅读量:5806 次
发布时间:2019-06-18

本文共 3596 字,大约阅读时间需要 11 分钟。

2.this的绑定规则

1.默认绑定

1 function foo(  )2 {3    console.log(this.a);4 }5 var a=1;6 foo();  //1

在代码中,foo()函数不带任何修饰的引用进行调用的,那么只能使用默认绑定。

2.隐式绑定

1 function foo1()2 {3    console.log( this.a );4 }5 var obj = {6    a: 1,7    foo: foo18 };9 obj.foo();  //1

调用位置使用obj上下文来引用函数foo2,故可以说函数被调用时obj对象“拥有”或“包含”该函数foo2()。
那么foo2函数被调用时,确实加上了对obj的引用。当函数引用有上下文对象时,
隐式绑定规则会把函数调用中的this绑定到这个上下文对象。
故上文中的this.a等同于obj.a。
PS1:对象属性引用链中只有上一层或者最后一层在调用位置中起作用。

1 function foo2() 2 { 3    console.log( this.a ); 4 } 5 var obj2 = { 6    a: 2, 7    foo: foo2 8 }; 9 var obj1 = {10    a: 3,11    obj2: obj212 };13 obj1.obj2.foo();    //2

距离this最近的对象上下文时obj2,故this.a等同与obj2.a,同时等同于obj1.obj2.a。
PS2:隐式丢失

1 function foo3() 2 { 3    console.log( this.a ); 4 } 5 var obj3 = { 6    a: 33, 7    foo: foo3 8 }; 9 var bar=obj3.foo;10 var a=3;11 bar();  //3

这里的要点就是关注var bar=obj3.foo;
虽然bar只是obj3.foo的一个引用,但bar实际引用的时foo3()函数本身。
因此此时的bar()其实是一个不带任何修饰的函数调用,所以应用默认绑定。
PS2:隐式丢失(发生在函数调用时)。

1 function foo4(  ) 2 { 3    console.log(this.b); 4 } 5 function doFOO( fn ) 6 { 7    var b=44; 8    fn(); 9 }10 var obj4={11    b:10,12    foo:foo413 };14 var b=4;15 doFOO(obj4.foo);    //4

参数传递其实就是一个隐式赋值,故传入函数也会被隐式赋值。
所以结果与上面例子一致。
同理,这里传入的是自定义函数。即使传入的是内置函数,结果也是一样的。
PS3:如上所见,回调函数丢失this绑定是很常见的。
与此同时,另一种丢失情况更加出人意料:调用回调函数可能会修改this。
在一些流行的js库中,事件处理器经常会把回调函数的this绑定到DOM元素上。

3.显式绑定

显式绑定,即是通过call,apply等方法来强制绑定。
call与apply的第一个参数都是thisObj,表示this的指向。第二个参数,call是输入单个参数,apply是输入参数数组。多个参数时,后者性能更优。

1 function foo()2 {3    console.log( this.a );4 }5 var obj = {6    a: 37 };8 foo.call( obj );   //2     函数没有参数输入时,call只需要一个参数thisObj输入。

通过foo.call(),可以在调用foo时强制把它的this绑定到obj上。
PS1:“装箱”
如果thisObj参数传入的是一个原始值(简单数据类型),这个原始值会被转换成它的对象形式(如new String(..),new Boolean(..),或者new Number(..))。这通常称为“装箱”。
PS2:解决之前提出的丢失绑定问题。
1.硬绑定(显式绑定的一个变种)

1 function foo2() 2 { 3    console.log( this.a ); 4 } 5 var obj2 = { 6    a: 3 7 }; 8 var bar = function() 9 {10    foo2.call( obj );11 };12 bar();  //313 setTimeout( bar, 100 ); //314 15 //切记,硬绑定的bar不可能在修改它的this。16 bar.call( window );   //3

硬绑定的典型应用场景就是创建一个包裹函数,负责接收参数并返回值;
另一个使用方法就是创建一个可以重复应用的辅助函数:

1 function foo3( something ) 2 { 3    console.log( this.d, something ); 4    return this.d + something; 5 } 6 function bind( fn, obj ) 7 { 8    return function() 9    {10       return fn.apply( obj, arguments );11    };12 }13 var obj3 = {14    d: 215 };16 var bar3 = bind( foo3, obj3 );17 var e = bar3( 3 );  //2 318 console.log( e ); //5

由于硬绑定是一个非常常用的模式,故ES5提供了一个内置方法bind,和上述用法类似。

1 function foo4( something ) 2 { 3    console.log( this.a4, something ); 4    return this.a4 + something; 5 } 6 var obj4 = { 7    a4: 2 8 }; 9 var bar4 = foo4.bind( obj4 );10 var b4 = bar4( 5 ); //2 511 console.log( b4 );    //7

bind(..)会返回一个硬编码的新函数(切记,新函数),这会将指定的参数设置为this的上下文并调用原始函数。
2.API调用的“上下文”
第三方库的许多函数,以及JS语言和宿主环境(如浏览器环境)中许多内置函数,都提供了一个可选参数,通常称为“上下文(context)。
其作用与bind(..)类似,确保回调函数使用指定的this。

1 function foo5( el ) 2 { 3    console.log( el, this.id ); 4 } 5 var obj5 = { 6    id: "awesome" 7 }; 8  9 //调用foo5(..)函数是将this绑定到obj。10 [ "1", 2, 3 ].forEach( foo5, obj5 );    //console.log输出数字和字符串时,数字在前,字符串会有双引号;反之则没有。11 // 1 awesome; 2 awesome; 3 awesome

 

4.new绑定

在此先纠正js中new与其他语言中new的区别。
在其它面向类语言中,”构造函数“是类中的一些特殊方法,使用new 初始化类时调用类中的构造函数。
在JS中,构造函数只是一些使用new操作符时被调用的函数。它们不属于某个类,也不会实例化一个类。
JS中使用new来调用函数,或者说发生构造函数调用时,会自动执行以下操作。
  1.创建(或者说构造)一个全新的对象。
  2.这个新对象会被执行[[Prototype]]链接。
  3.这个新对象会绑定到函数调用的this。
  4.如果函数没有返回其他对象,那么new表达式中的函数调用会自动返回这个新对象。

1 function foo( a )2 {3    this.a = a;4 }5 var bar = new foo( 4 );6 console.log( bar.a );7 8 //使用new来调用foo(..)时,会构建一个新对象并将它绑定到foo(..)调用的this上,并将该对象返回给bar。

 

转载于:https://www.cnblogs.com/Tiancheng-Duan/p/8296908.html

你可能感兴趣的文章
SpringMvc文件下载
查看>>
BZOJ 1597: [Usaco2008 Mar]土地购买 [斜率优化DP]
查看>>
Check the quota usage
查看>>
tomcat 服务形式检测
查看>>
cassandra mongodb选择——cassandra:分布式扩展好,写性能强,以及可以预料的查询;mongodb:非事务,支持复杂查询,但是不适合报表...
查看>>
REST开放接口生成文档工具之apidoc
查看>>
常见的前端UI框架
查看>>
centos7下Redis3的安装与使用
查看>>
按窗口获得最大数 和 中位数
查看>>
几种判断一个整数是否是2的n次方幂的方法
查看>>
Android真机测试、乐视手机启用开发者模式
查看>>
MySQL更改relay-bin名称导致同步停止的解决办法
查看>>
utf-8编码
查看>>
在Ubuntu上为Android增加硬件抽象层(HAL)模块访问Linux内核驱动程序【转】
查看>>
【Java】创建线程对象两种方式
查看>>
1083 Cantor表
查看>>
字符集对应表
查看>>
apicloud,aliyunlive,测试成功
查看>>
判断一个数是否含有相同的数字
查看>>
Logstash读写性能调整优化
查看>>