对this的理解,我一直都是用一句话归纳:谁调用它,它就指向谁。javascript
好像也没有什么问题,但仔细看了<你不知道的JavaScript>这本书和网上一些文章后,发现this的原理还挺讲究的。因而决定写个概括。
(对了,无知的我实在没想到原来bind也和this扯上关系。。)html
this的绑定规则有4种。分别是:
一、默认绑定
二、隐式绑定
三、显示绑定
四、new绑定java
须要明确:this的值虽然会随着函数使用场合的不一样而发生变化,但有一个原则,它指向的是调用它所在的函数的那个对象。数组
function test(){ console.log(this.a); } var a = 1; test();
当调用test()时,由于应用了this的默认绑定,this.a被解析成全局变量a,this指向全局对象window。因此结果为1。网络
怎么知道应用了默认绑定呢?当前test()是直接使用不带任何修饰的函数引用进行调用的。这个简单来讲就是没有任何前缀啊等东西,很纯粹!而其调用位置是全局做用域,更能肯定除了默认绑定,没法应用其余规则了。app
若是懂了,那么下面的例子也就会作了函数
function test(){ this.a = 2; console.log(a); } test();
已知调用函数test()的对象是window,因此this指向window,即this.a===window.a。因为window能够省略,所以简写成a。
至关于在全局做用域声明了变量a,而且赋值a=2。
this.a = 2
-->window.a = 2
-->a = 2
因此结果为2。oop
可能说得太啰嗦了,直接说下一种绑定。this
通俗地说就是一个函数,被看成引用属性添加到了一个对象中了,而后以 “对象名.函数名()” 形式进行调用,这时若是函数引用有上下文对象,隐式绑定规则会把函数调用中的this绑定到这个上下文对象。.net
下面看下例子。
function test(){ console.log(this.a); } var obj = { a:3, test:test }; obj.test();
对象obj中包含两个属性a和test,其值分别是3和一个函数test()。
obj.test()表示函数引用时有上下文对象(也就是这里的obj)。
根据隐式绑定规则,会把test()中的this绑定到这个上下文对象,即this被绑定到obj,this.a===obj.a。
因此结果为3。
若是懂了,那么下面的例子也就会作了
function test(){ console.log(this.a); } var obj2 = { a:4, test:test }; var obj1 = { a:400, obj2:obj2 } obj1.obj2.test()
看起来复杂了些,不过只要记住下面这个准则就能够了。
对象属性引用链中,只有上一层或者说最后一层在调用位置中起做用
因此只有obj2这个上下文对象生效,结果为4。
这个网上貌似不多说起,隐式丢失,通俗来说就是“变low”了!
本来应用隐式绑定的,由于丢失绑定对象,变回应用默认绑定了!把this绑定到全局对象或undefined。
当对象将其引用属性给了新的引用,再次调用这个新的引用时,本来this指向的对象就会改成指向window。
到底在说什么,看个例子。
function foo(){ console.log(this.a); } var obj = { a:5, foo:foo }; var bar = obj.foo; //这句就是关键 var a = "oops, global"; bar(); // "oops, global"
b引用了test()函数自己,此时的b()是一个不带任何修饰的函数调用,所以应用了默认绑定。
若是懂了,那么下面的例子也就会作了
function foo(){ console.log(this.a); } function doFoo(fn){ fn(); } var obj = { a:5, foo:foo } var a = "oops, global"; doFoo(obj.foo); // "oops, global"
其实和上面的没什么区别,只是这里把函数做为参数传递了,参数传递是一种隐式赋值。此时的this指向的是调用它的函数的对象即全局对象,所以应用了默认绑定。
回顾一下隐式绑定,其关键是把函数看成引用属性添加到了对象中,经过这个属性间接引用这个函数,把this简介(隐式)绑定到这个对象上。
若是不想在对象内部包含函数引用,而想简单粗暴地在某个对象强制调用一个函数,这时就用到了函数的call()和apply()方法。
call和apply,以往我单纯理解为“控制this的指向”,如今才发现是原来是this绑定规则中的一种。
call和apply区别
apply接收的是数组参数,call接收的是连续参数。因此当传入的参数数目不肯定时,多使用apply。
(tips:这里推荐个方法记忆apply和call各自接收的参数:apply为a开头,数组Array也是a开头,因此apply接收的是数组参数)
看下面例子。
function test(){ console.log(this.a); } var obj = { a:6 }; test.call(obj);
当调用test时强制把它的this绑定到obj上。因此this.a===obj.a,结果为6。
注意:后续参数传入的是原始值的话,会被转换成它的对象形式(如字符串类型-->new String()如此类推)
此次咱们不用call,用apply。
var a = 0; function test(){ console.log(this.a); } var obj = {}; obj.a = 7; obj.m = test; obj.m.apply(); obj.m.apply(obj);
一样的,对象obj包含了两个属性a和m,m引用了函数test()。
obj.m.apply(obj)即把test()这个函数中的this绑定到对象obj上。即this指向的是obj。因此this.a===obj.a。
“apply没有参数怎么办,基本语法都是包含参数的啊,至少给个对象,让this有个指向啊。”
apply定义了当没有参数时,全局对象会自动默认成为其第一个参数,apply()等价于apply(window)。
所以obj.m.apply(),test()中的this就指向了全局对象window,结果为0。
干吗用的?解决前面提到的隐式丢失问题。
回顾当初隐式丢失的第二个例子。
function foo(){ console.log(this.a); } function doFoo(fn){ fn(); } var obj = { a:5, foo:foo } var a = "oops, global"; doFoo(obj.foo); // "oops, global"
将其改一下变成:
function foo(){ console.log(this.a); } function doFoo(fn){ fn.call(obj); } var obj = { a:5, foo:foo } var a = "oops, global"; doFoo(obj.foo);
依旧是建立了doFoo()这个函数,但在其内部手动调用了 obj.foo.call(obj),
把foo()强制绑定到了obj对象,以后不管如何调用doFoo(),它总会手动在obj上调用foo。
对于硬绑定,ES5提供了一个内置方法Function.prototype.bind,咱们把上面的例子再改!
function foo(){ console.log(this.a); } function doFoo(fn){ fn.bind(obj); } var obj = { a:5, foo:foo } var a = "oops, global"; doFoo(obj.foo);
一执行,个人天!啥也没有啊??
这就对了,这是bind的“效果”,也是和apply、call的主要区别——延迟调用。
也就是说bind其实只是函数的引用,要想执行须要进行回调,即fn.bing(obj)(),
此时就能输出5了。
好,如今把隐式丢失的第一个例子改动
function foo(){ console.log(this.a); } var obj = { a:5, foo:foo }; var bar = obj.foo.bind(obj); var a = "oops, global"; bar(); //5
为了能证实bind的特色,函数在回调时执行,把bind改为call,最后3行代码变成
var bar = obj.foo.call(obj); var a = "oops, global"; bar(); //Uncaught TypeError: bar is not a function
结果报错了,对,由于call和apply都是绑定后马上执行的,都执行完了,bar就只是一个没有赋值的变量而已。
总结下“显示绑定三人组”:
共同点: 一、都用于控制this指向; 二、第一个参数都是this须要指向的对象,也就是上下文; 三、均可之后续参数传递; 四、没有任何参数时,this都指向全局对象window 区别: 一、call、apply绑定后马上执行,bind是延迟执行。换言之,当你但愿改变上下文环境以后并不是当即执行,而是回调执行的时候,就使用bind()方法吧。
什么是构造函数,一个函数被new调用时,该函数就是构造函数。
(变身前:普通函数;使用地摊货new变身器,变身后:构造函数)
function test(){ this.x = 8; } var obj = new test(); console.log(obj.x);
你能够简单粗暴理解为:就至关于test()被对象obj调用,其this指向obj。
但貌似很不规范。。
实际上,调用函数的是new关键字。
使用new来调用函数,即函数的“构造调用”时,咱们会构造一个新对象,
并把它绑定到test()调用中的this上。因此在代码中,this就指向了新对象obj
判断this的4种规则根据优先级从高到低排序以下:
一、函数在 new 中调用,this绑定的是这个新对象
二、函数经过 call、apply或bind 调用,this绑定的是指定对象
三、函数在某上下文对象中调用,this绑定的是这个上下文对象
四、以上都不是,使用默认绑定。(在全局做用域调用,this绑定window对象)
关于this书中还说起到不少其余知识,例如软绑定、this词法箭头函数等,就不概括到这里了
概括前看过的相关书籍或文章:
一、<你不知道的JavaScript>(上)第二部分第二章 this全面解析
二、阮一峰的网络日志-JavaScript的this用法
http://www.ruanyifeng.com/blo...
三、chanzen的我的博客-call,apply,bind用法和意义
http://ovenzeze.coding.me/use...
四、脚本之家-JS中的this变量的使用介绍
http://www.jb51.net/article/4...