““You know nothing Jon Snow”
this关键字
““你不知道的JavaScript学习笔记02”
概念
- 定义:当一个函数被调用时,会创建一个执行上下文。这个执行上下文会包含函数在哪里被调用(调用栈)、函数的调用方式、传入的参数等信息。this就是这个记录的一个属性,会在函数执行的过程中用到
- 所以可以明确一点:this既不指向函数自身也不指向函数的词法作用域,this指代的是当前函数被调用时的执行上下文
- this实际上是在函数被调用时发生的绑定,它指向什么完全取决于函数在哪里被调用
绑定规则
默认绑定
- 独立函数调用,this默认绑定全局对象(在严格模式下默认绑定不会指定上下文对象)
function foo() {
console.log(this.a)
}
var a = "fuck";
foo();//fuck
隐式绑定
- 当函数作为对象的属性被调用时,会触发隐式绑定
var obj1 = {
a:1,
foo:function() {
console.log(this.a);
}
}
var obj2 = {
b:2,
bar:function() {
consolelog(this.b);
}
}
obj1.foo();//1
obj2.bar();//2
- 当函数引用有上下文对象时,隐式绑定规则会把函数调用中的this绑定到这个上下文对象。
- 注意:对象属性引用链中只有上一层或者说最后一层在调用位置中起作用。
function foo() {
console.log(this.a);
}
var obj1 = {
a:1,
foo:foo
}
var obj2 = {
a:2,
obj1
}
var obj3 = {
a:3,
obj2
}
obj3.obj2.obj1.foo(); //1;
隐式丢失
var obj1 = {
a:1,
foo:function() {
console.log(this.a);
}
}
var a = "shit";
var bar = obj1.foo;
bar(); //shit
在js中,函数属于Object的之类,在这块代码块中的RHS实际上是传递函数的引用,而this是由调用宿主和执行上下文决定的,函数执行时执行的是默认绑定的规则
显式绑定
- 概念:直接指定函数执行时的上下文对象
- 内置函数(对象)Function上的三个方法:call()、apply()、bind()
var obj1 = {
a:1
}
function foo() {
console.log(this.a);
}
var a = 2;
foo.call(obj1); // 1
foo.apply(obj1); //1
var bar = foo.bind(obj1);
bar(); //1
new声明绑定
new关键字
avaScript中的“构造函数”有别于其他语言,在JavaScript中,在函数调用前用new关键字进行声明,该函数就成了“构造函数”,函数本身并无区别。 使用new来调用函数,或者说发生构造函数调用时,会自动执行下面的操作。1.创建(或者说构造)一个全新的对象。2.这个新对象会被执行[[Prototype]]连接。3.这个新对象会绑定到函数调用的this。4.如果函数没有返回其他对象,那么new表达式中的函数调用会自动返回这个新对象。
//eg
function myNew() {
// 第一个参数是构造函数
var Constructor = [].shift.call(arguments);
// 创建一个继承对象
var obj = Object.create(Constructor.prototype);
// 执行构造函数,this执行obj
var result = Constructor.apply(obj, arguments);
// 构造函数返回是对象,优先使用返回的对象,否则是obj
return result instanceof Object ? result : obj;
// 执行构造函数,this执行obj
var result = Constructor.apply(obj, arguments);
// 构造函数返回是对象,优先使用返回的对象,否则是obj
return result instanceof Object ? result : obj;
}
var a = 'fuck';
function foo(a) {
this.a = a;
this.saya = function() {
console.log(this.a)
}
}
var b = "shit";
var obj = myNew(foo,b); // new foo(b)
obj.saya; //shit;
使用new来调用foo(..)时,我们会构造一个新对象并把它绑定到foo(..)调用中的this上。new是最后一种可以影响函数调用时this绑定行为的方法,我们称之为new绑定。
优先级
“new绑定 > 显式绑定 >隐式绑定 >默认绑定
可以按照下面的顺序来进行判断:
1.函数是否在new中调用(new绑定)?
如果是的话this绑定的是新创建的对象。
var obj = new foo(); //绑定obj
2.函数是否通过call、apply(显式绑定)或者硬绑定调用?
如果是的话,this绑定的是指定的对象。
var bar = new foo.call(obj); //绑定obj
3.函数是否在某个上下文对象中调用(隐式绑定)?
如果是的话,this绑定的是那个上下文对象。
var bar = obj.foo(); //绑定obj
4.如果都不是的话,使用默认绑定。如果在严格模式下,就绑定到undefined,否则绑定到全局对象。

箭头函数
箭头函数并不是使用function关键字定义的,而是使用被称为“胖箭头”的操作符=>定义的。箭头函数不使用this的四种标准规则,而是根据外层(函数或者全局)作用域来决定this。
function foo() {
return (a) => {
console.log(this.a)
}
}
var obj1 = {
a:1
}
var obj2 = {
a:2
}
var bar = foo.call(obj1);
bar.call(obj2); //1,不是2
foo内部的箭头函数会捕获调用foo时的this;箭头函数的this查询规则遵从词法;外层函数绑定的this会间接传递给内部的箭头函数,箭头函数的绑定无法被修改。
function foo() {
var _this = this;
setTimeout(function(){
console.log(_this.a)
},100)
}
function bar() {
setTimeout(()=>{
console.log(this.a)
},100)
}
function baz() {
setTimeout(function(){
console.log(this.a)
},100)
}
var obj = {
a: 1
}
var a = 2;
foo.call(obj); //1
bar.call(obj); //1
baz.call(obj); //2, 天惹,回调函数的this执行了默认绑定
“参考文献: 《你不知道的JavaScript(上卷)》
本文使用 mdnice 排版