第33天 说说你对this的理解
js 中有两个重要概念:作用域和原型链
我个人感觉
作用域对应函数式开发,闭包是主要工具
原型链对应对象式开发,this 是主要工具,把一些操作封装在一个工具包上,然后用 this 来调用
this在不同场景下指向也不同,比如有可能指向new出来的对象,可能指向全局对象,通过apply/call/bind还可以指向传入的第一个参数,等等。。。
指的是当前运行环境的上下文。当然这个this是会发生改变的 如 bind call apply
基本上可以归为四类,
全局this 是window
函数this 是调用者
构造函数的this 是new 之后的新对象
call 和 apply bind的this第一个参数
不严谨说法,谁调用这个函数,this就指向谁。
通常来说,this指向该函数的执行上下文。
arrow function 指向它上一级的执行上下文。
JavaScript 中的 this
用一句话来概括,就是在执行时确定的。从现象上来看,就是谁调用了某个方法,那么这个方法中的 this
指向谁。
下面是最常见的例子:
const obj = {
sayThis: function() {
console.log(this);
}
};
obj.sayThis(); // obj
const globalSay = obj.sayThis;
globalSay(); // window 浏览器中的 global 对象
谁调用了某个方法,那么这个方法中的
this
指向谁
这个“谁”我们可以通过 .
操作符来判断。比如 obj.sayThis()
,这里就是 obj
调用了 sayThis
方法,所以 this
指向 obj
。而后一个 globalSay()
是直接调用的,在 JavaScript 中会把这个方法绑在全局(window)上,所以本质就是 window.globalSay()
,自然这里的 this
就指向了 window
。
JavaScript 给我们提供了 apply/call/bind
三种方法来改变 this
的指向。在 ES6 的语法中还提供了箭头函语法,让我们在代码书写时就能确定 this
的指向(编译时绑定)。唯一需要注意的就是要避开箭头函数带来的坑。
把上面的例子改为箭头函数,结果完全不同。
const obj = {
sayThis: () => {
console.log(this);
}
};
obj.sayThis(); // window 因为 JavaScript 没有块作用域,所以在定义 sayThis 的时候,里面的 this 就绑到 window 上去了
const globalSay = obj.sayThis;
globalSay(); // window 浏览器中的 global 对象
https://github.com/getify/You-Dont-Know-JS/blob/2nd-ed/objects-classes/ch2.md
判定 this
按照优先顺序来总结一下从函数调用的调用点来判定 this 的规则了。按照这个顺序来问问题,然后在第一个规则适用的地方停下。
1、函数是通过 new 被调用的吗(new 绑定)?如果是,this 就是新构建的对象。
var bar = new foo()
2、函数是通过 call 或 apply(或bind) 被调用?如果是,this 就是那个被明确指定的对象。
var bar = foo.call( obj2 )
3、函数是通过环境对象(也称为拥有者或容器对象)被调用的吗(隐含绑定)?如果是,this 就是那个环境对象。
var bar = obj1.foo()
4、否则,使用默认的 this(默认绑定):如果在 strict mode 下,就是 undefined,否则是 global 对象。
var bar = foo()
如果你传递 null 或 undefined 作为 call、apply 或 bind 的 this 绑定参数,那么这些值会被忽略掉,使用默认的this。“更安全”的做法是:为了 this 而传递一个特殊创建好的对象,创建完全为空的对象的最简单方法就是 Object.create(null)(见第五章)。Object.create(null) 和 {} 很相似,但是没有指向 Object.prototype 的委托,所以它比 {} “空得更彻底”。
与这四种绑定规则不同,ES6 的箭头方法使用词法作用域来决定 this 绑定,这意味着它们采用封闭他们的函数调用作为 this 绑定。它们实质上是 ES6 之前的 self = this 代码的语法替代品。
在普通函数中,this指向window
在对象中this指向这个对象,但是有特殊情况
特殊例子
在其被调用时才确定this指向
定时器,延时器里的this指向是window
构造函数中的this指向是new实例化之后的实例
apply,call,bind会改变this指向
apply改变this指向,第二个参数是数组
call改变this指向,第二参数是一个一个参数
bind改变this指向,但是不会立即执行,返回一个新的函数
箭头函数
函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象
函数中的this
永远指向函数的调用者(在代码执行时才能确定),谁最终调用这个函数,this
就指向谁
this
指向window
(如果开启严格模式,this
将指向undefined
)this
就指向new
出来的对象this
就指向这个对象(但要小心this
绑定丢失,尤其要注意将函数作为值向函数中传入赋值时发生隐式丢失)call
、apply
、bind
进行绑定,那么this
就指向被绑定的对象箭头函数中的this
例外,它指向定义时所在的位置。或者可以说箭头函数的this
继承自外层作用域,只要确定了外层作用域的this
,就知道箭头函数中的this
指向谁了。
如果函数是独立调用的话this就指向的是全局对象,如果是对象调用那么就指向的是这个对象,并且通过call/apply可以改变this指向,对比箭头函数中的this指向的是父级作用于中的this,并且通过call/apply无法改变this指向。
基本上可以归为四类,
全局this 是window
函数this 是调用者
构造函数的this 是new 之后的新对象
call 和 apply bind的this第一个参数
this的指向不是在编写的时候确定的,是在执行的时候确定的。分为四类:
- 默认绑定:非严格模式下this指向全局对象,严格模式下指向undefined
- 隐式绑定:如果函数被一个对象调用,this指向该对象
- 显示绑定:使用 call apply bind 改变this指向,指向第一个参数
- new绑定:构造函数的this 指向new之后的新对象
箭头函数中没有this绑定,必须通过查找作用域链来决定其值,如果箭头函数被非箭头函数所包含,this指向最近一层非箭头函数的this,否则是undefined
Most helpful comment
基本上可以归为四类,
全局this 是window
函数this 是调用者
构造函数的this 是new 之后的新对象
call 和 apply bind的this第一个参数