什么是this
MDN 上是这样说的:在绝大多数情况下,函数的调用方式决定了 this 的值
ECMAScript 规范中这样写:this 关键字执行为当前执行环境的 ThisBinding。
按照自己的理解翻译一下:在 JavaScript 中,this 的指向是调用时决定的,而不是创建时决定的,这就会导致 this 的指向会让人迷惑,简单来说,this 具有运行期绑定的特性。
死记硬背以下的规则
1、在函数体中,非显示或隐式地简单调用函数时,在严格模式下,函数内的this会被绑定到undefined上,在非严格模式下则会绑定到全局对象window/global上
2、一般使用new方法调用构造函数时,钩爪函数内的this会被绑定到新创建的对象上
3、一般通过call/apply/bind方法显式调用函数时,函数体内的this会被绑定指定参数的对象上
4、一般通过上下文都一项调用函数时,函数体内的this会被绑定到该对象上
5、在箭头函数中,this的指向由外层(函数或全局)作用域来决定的
箭头函数中的this
- 所有的箭头函数都没有
this,都是指向外层的当前函数所在的上下文的this - 为什么箭头函数没有自己的
this,因为箭头函数中的this在编译的时候会被变量替代
// 编译前书写的箭头函数
let test = {
foo: "apple",
getFoo() {
return () => {
return this.foo;
};
},
};
// 编译后
let test = {
foo: "apple",
getFoo() {
let _this = this;
return function () {
return _this.foo;
};
},
};
对于普通函数来说,内部的this指向函数运行时所在的对象,但是这一点对箭头函数不成立。它没有自己的this对象,内部的this就是定义时上层作用域中的this。也就是说,箭头函数内部的this指向是固定的,相比之下,普通函数的this指向是可变的
function Timer() {
this.s1 = 0;
this.s2 = 0;
// 箭头函数
setInterval(() => this.s1++, 1000);
// 普通函数
setInterval(function () {
this.s2++;
}, 1000);
}
var timer = new Timer();
setTimeout(() => console.log('s1: ', timer.s1), 3100);
setTimeout(() => console.log('s2: ', timer.s2), 3100);
// s1: 3
// s2: 0
this的优先级
new > 显式绑定 > 隐式绑定
bind、call、apply、箭头函数的区别
- 使用apply或call方法,那么
this指向他们的第一个参数,apply的第二个参数是一个参数数组,call的第二个及其以后的参数都是数组里面的元素,就是说要全部列举出来 - 都是用来改变函数的
this对象的指向的; - 第一个参数都是
this要指向的对象; - 都可以利用后续参数传参;
- bind是返回对应函数,便于稍后调用,apply、call是立即调用;
- 箭头函数:没有自己的
this值,箭头函数内的this值继承自外围作用域,在箭头函数中调用this时,仅仅是简单的沿着作用域链向上寻找,找到最近的一个this拿来使用
var name = "佚名";
var age = 20;
//global.name
//global.age
var p1 = {
name: "张三",
age: 12,
func: function(){
console.log(`姓名:${this.name},年龄:${this.age}`)
}
}
var p2 = {
name: "李四",
age: 15
}
p1.func(); //姓名:张三,年龄:12
p1.func.call(); //姓名:佚名,年龄:20
p1.func.apply(p2); //姓名:李四,年龄:15
p1.func.bind(p2)(); //姓名:李四,年龄:15
this面试题(所有题目都源于网络)
第一题
var name = "222";
var a = {
name: "111",
say: function () {
console.log(this.name);
},
};
var fun = a.say;
fun();
a.say();
var b = {
name: "333",
say: function (fn) {
fn();
},
};
b.say(a.say);
b.say = a.say;
b.say();
答案
var name = "222";
var a = {
name: "111",
say: function () {
console.log(this.name);
},
};
var fun = a.say;
fun(); // 此时fun处于全局环境中,也就是全局环境调用这个方法,谁调用this指向谁,所以打印 222
// fun() === fun.call(window) fun相当于是语法糖
a.say(); // 此时函数处于a对象中,所以 this指向 a对象 所以打印 111
// a.say() === a.say.call(a)
var b = {
name: "333",
say: function (fn) {
fn(); // 此时fn执行没有其他的this 绑定,
// fn() === fn.call(window)
// a.say() === fn() === fn.call(window) === a.say.call(window)
},
};
b.say(a.say); // 打印 222
b.say = a.say;
// b.say = a.say 相当于
// var b = {
// name: '333',
// say:function(){
// console.log(this.name)
// }
// }
b.say(); // 打印 333
第二题
let obj = {
fn: (function () {
return function () {
console.log(this);
};
})(),
};
obj.fn();
let fn = obj.fn;
fn();
答案
obj.fn(); // this指向的是obj ,所以输出 {fn:function(){}}
let fn = obj.fn;
fn(); // 此时的fn 属于全局,相当于把函数赋值给全局变量,所以 this 是window
第三题
var fullName = "language";
var obj = {
fullName: "javascript",
prop: {
getFullName: function () {
return this.fullName;
},
},
};
console.log(obj.prop.getFullName());
var test = obj.prop.getFullName;
console.log(test());
答案
console.log(obj.prop.getFullName()); //=>this:obj.prop =>obj.prop.fullName =>undefined
var test = obj.prop.getFullName;
console.log(test()); //=>this:window =>window.fullName =>'language' */
第四题
var name = "window";
var Tom = {
name: "Tom",
show: function () {
console.log(this.name);
},
wait: function () {
var fun = this.show;
fun();
},
};
Tom.wait();
答案
var Tom = {
name: "Tom",
show: function () {
// this->window
console.log(this.name); //=>'window'
},
wait: function () {
// this->Tom
var fun = this.show;
fun();
// 此时fn执行没有其他的this 绑定,
// fn() === fn.call(window)
},
};
第五题
window.val = 1;
var json = {
val: 10,
dbl: function () {
this.val *= 2;
},
};
json.dbl();
var dbl = json.dbl;
dbl();
json.dbl.call(window);
alert(window.val + json.val); //24
答案
json.dbl();
//->this:json ->json.val=json.val*2 ->json.val=20
var dbl = json.dbl;
dbl();
//->this:window ->window.val=window.val*2 ->window.val=2
json.dbl.call(window);
//->this:window ->window.val=window.val*2 ->window.val=4
alert(window.val + json.val); //=>"24"
第六题
(function () {
var val = 1;
var json = {
val: 10,
dbl: function () {
val *= 2;
},
};
json.dbl();
alert(json.val + val);
})(); //12
答案
(function () {
var val = 1; //->2
var json = {
val: 10,
dbl: function () {
// this->json
val *= 2; //val=val*2 此处的val是变量 json.val是对象的属性
},
};
json.dbl();
alert(json.val + val); //=>"12"
})();