this 的指向
this
是 js 中定义的关键字, 它的指向并不是在函数定义的时候确定的,而是在调用的时候确定的。换句话说,函数的调用方式决定了 this 指向。在实际应用中,this
的指向大致可以分为以下四种情况。
this的指向在函数定义的时候是确定不了的,只有函数执行的时候才能确定this到底指向谁(取决于函数的调用位置),实际上this的最终指向的是那个调用它的对象(函数的直接调用者);
作为普通函数调用(直接调用):函数名()
当函数作为一个普通函数被调用,this
指向全局对象。在浏览器里,全局对象就是 window。
1 | window.name = 'cosyer'; |
可以看出,此时this
指向了全局对象 window。(NodeJS的全部对象是global)
在ECMAScript5的严格模式下,这种情况this
已经被规定不会指向全局对象了,而是undefined。
1 | ; |
作为对象的方法调用
当函数作为一个对象里的方法被调用,this
指向该对象
1 | var obj = { |
如果把对象的方法赋值给一个变量,再调用这个变量:
1 | var obj = { |
此时调用 fun2 方法 输出了 window 对象,说明此时this
指向了全局对象。给 fun2 赋值,其实是相当于:
1 | var fun2 = function(){ |
可以看出,此时的this
已经跟 obj 没有任何关系了。这时 fun2 是作为普通函数调用。
作为构造函数调用
js中没有类,但是可以从构造器中创建对象,并提供了new
运算符来进行调用该构造器。构造器的外表跟普通函数一样,大部分的函数都可以当做构造器使用。当构造函数被调用时,this
指向了该构造函数实例化出来的对象。
1 | var Person = function(){ |
如果构造函数显式的返回一个对象(function或者object),那么this
则会指向该对象。
1 | var Person = function(){ |
如果该函数不用new
调用,当作普通函数执行,那么this
依然指向全局对象。
call() 或 apply() 调用 Function.prototype.bind()将当前函数绑定到指定对象绑定返回新函数之后再进行调用(间接调用、显示绑定)
通过调用函数的 call() 或 apply() 方法可动态的改变this
的指向。
1 | var obj1 = { |
简单的实现bind方法
1 | const obj = {}; |
从上面的示例可以看到,首先,通过闭包,保持了 target,即绑定的对象;然后在调用函数的时候,对原函数使用了 apply 方法来指定函数的 this。
不过使用 apply 和 call 的时候仍然需要注意,如果目录函数本身是一个绑定了 this 对象的函数,那 apply 和 call 不会像预期那样执行
1 | const obj = {}; |
一道this练习题
1 | var length = 10; |
- 默认绑定
- 隐式绑定
- 显示绑定
- new绑定
默认绑定就是什么都匹配不到的情况下,非严格模式this绑定到全局对象window或者global,严格模式绑定到undefined; 隐式绑定就是函数作为对象的属性,通过对象属性的方式调用,这个时候this绑定到对象; 显示绑定就是通过bind、apply和call调用的方式; new绑定就是通过new操作符时将this绑定到当前新创建的对象中
优先级: new>显示>隐式>默认
箭头函数
箭头函数的引入有两个方面的作用:一是更简短的函数书写,二是对this
的词法解析。
在箭头函数出现之前,每个新定义的函数都有其自己的this
值(例如,构造函数的this
指向了一个新的对象;严格模式下的函数的this
值为 undefined;如果函数是作为对象的方法被调用的,则其this
指向了那个调用它的对象)。在面向对象风格的编程中,这会带来很多困扰。
ES6 的箭头函数 ()=>,指向与一般function定义的函数不同,比较容易绕晕,箭头函数this
的定义:箭头函数中的this
是在定义函数的时候绑定,而不是在执行函数的时候绑定。本质来说箭头函数没有自己的this
,它的this
是派生而来的。箭头函数会捕获其所在上下文的this
值,作为自己的this
值,即它会根据外层(函数或者全局)作用域继承 this(直接外层函数)。
箭头函数将this指向其封闭的环境(也称“词法作用域”)。
基础语法
1 | // 等价于: => { return expression; } |
高级语法
1 | // 只返回一个对象字面量,没有其他语句时, 应当用圆括号将其包起来: |
箭头函数不可以使用arguments对象,super或new.target
arguments对象在函数体内不存在,如果要用的话,可以用rest参数代替
箭头函数没有原型
1 | var Foo = () => {}; |
箭头函数无法构造函数
1 | var Foo = () => {}; |
箭头函数无法使用yield
yield 关键字通常不能在箭头函数中使用(除非是嵌套在允许使用的函数内)。因此,箭头函数不能用作生成器。