this解析
-
当函数被正常调用时,在严格模式下,this 值是 undefined,非严格模式下 this 指向的是全局对象 window;
-
通过一个对象来调用其内部的一个方法,该方法的执行上下文中的 this 指向对象本身
-
ES6 中的箭头函数并不会创建其自身的执行上下文,所以箭头函数中的 this 取决于它的外部函数
-
new 关键字构建好了一个新对象,并且构造函数中的 this 其实就是新对象本身
- 当执行 new CreateObj() 的时候,JavaScript 引擎做了如下四件事:
- 首先创建了一个空对象 tempObj;
- 接着调用 CreateObj.call 方法,并将 tempObj 作为 call 方法的参数,这样当 CreateObj 的执行上下文创建时,它的 this 就指向了 tempObj 对象;
- 然后执行 CreateObj 函数,此时的 CreateObj 函数执行上下文中的 this 指向了 tempObj 对象;
- 最后返回 tempObj 对象。
- 当执行 new CreateObj() 的时候,JavaScript 引擎做了如下四件事:
-
嵌套函数中的 this 不会继承外层函数的 this 值。
1. 全局环境下的this
1. 在非严格模式下,this指向window,在通过use strict指明严格模式的情况下指向undefined。
2. 给元素的某个事件绑定方法,当事件触发方法执行的时候,方法中的this是当前操作元素本身。
3. 当方法执行的时候,我们看方法前面是否有点,没有点this是window或undefined(严格模式下),有点,前面的this就是谁。
var name = 'zzf';
function fn() {
console.log(this.name)
}
var obj = {
name : 'zdf',
fn:fn
}
obj.fn(); // this:obj
fn() //this: window
var fullName = 'language';
var obj = {
fullname: 'javascript',
prop: {
getFullName: function() {
return this.fullName;
}
}
}
console.log(obj.prop.getFullName());
// obj.prop.fullName => undefined
var test = obj.prop.getFullName;
console.log(test());
// window.fullName = 'language'
再来看一道高阶题目
var name = 'window';
var Tom = {
name: 'Tom',
show: function (){
console.log(this.name);
}
wait: function(){
// this:Tom Tom.show()
var fun = this.show;
fun(); // this:window => this.name = 'window'
}
}
Tom.wait();
Tom调用wait()方法,this指向Tom, 但是在wait()方法内,谁调用了fun()?虽然fun赋值给this.show,但是调用fun()的是window,结果输出window.name也就是’window’。
const o1 = {
text: 'o1',
fn: function() {
return this.text
}
}
const o2 = {
text: 'o2',
fn: function() {
return o1.fn()
}
}
const o3 = {
text: 'o3',
fn: function() {
var fn = o1.fn
return fn()
}
}
console.log(o1.fn()) // 'o1'
console.log(o2.fn()) // 'o1'
console.log(o3.fn()) // undefined
和上面的类似,o3.fn() 最终盗用fn()的是window,window没有text,结果为undefined。
2. call/bind/apply
这里是面试的高频点,甚至会手写这3个方法。
call/bind/apply都是用来改变相关函数this指向的,但是call和apply直接进行函数调用,bind不会执行相关函数,而是返回新的函数,新的函数已经绑定了新的this指向,开发者可以手动调用它。call和apply的区别在于参数上的设定。
const target = {}
fn.call(target, 'arg1','arg2')
fn.apply(target, ['arg1', 'arg2']
fn.bind(target, 'arg1', 'arg2')()
window.val = 1;
var json = {
val: 10;
dbl: function() {
this.val *= 2;
}
}
json.dbl();// json.val = 20
dbl(); // window.val = 2;
json.dbl.call(window); // window.val = 4;
3. 构造函数
在构造函数模式执行中,函数体中的this是当前类的实例。
这引出一个很重要的面试题:new操作符调用构造函数时具体做了什么?(手写new操作符)
- 创建一个新的对象
- this指向新的对象
- 为对象添加属性和方法等
- 返回新的对象
注意:在构造函数中,我们可以显式的return一个值,如果return的是一个基本类型,对返回的实例没有影响,如果手动return的是一个引用类型的值,会把默认返回的实例给替换掉(所以在构造函数模式执行下,我们一般都不要手动写return,防止把返回的实例给替换掉)
4. 箭头函数
箭头函数没有的this,this的指向是由外层作用域来决定的。
const foo = {
fn: function() {
setTimeout(function() {
console.log(this) // window
})
}
}
const foo = {
fn: function() {
setTimeout(() => {
console.log(this)
})
}
}
console.log(foo.fn()) // {fn:f}
5. this的优先级
显式绑定:call / apply/ bind/ new等对this进行绑定
隐式绑定:根据调用关系确定
call、bind的显示绑定一般优先级更高,不然也无法更改this指向不是。
new绑定的优先级比显式bind的绑定的更高。
箭头函数的绑定无法修改。
var a = 1
const foo = () => a => {
console.log(this.a)
}
const obj1 = {
a: 2
}
const obj2 = {
a: 3
}
var bar = foo.call(obj1)
console.log(bar.call(obj2)) // 1
let a = 1
const foo = () => a => {
console.log(this.a)
}
const obj1 = {
a: 2
}
const obj2 = {
a: 3
}
var bar = foo.call(obj1)
console.log(bar.call(obj2)) // undefined
// 想一想这里为什么是undefined?
6. 实现一个bind
这部分会写一个手写专题,尽请期待。