具体见:https://github.com/febobo/web-interview
10.this指向
根据不同的使用场合,this
有不同的值,主要分为下面几种情况:
- 默认绑定
- 隐式绑定
- new绑定
- 显示绑定
①默认绑定
全局环境中定义person
函数,内部使用this
关键字
var name = 'Jenny';
function person() {
return this.name;
}
console.log(person()); //Jenny
上述代码输出Jenny
,原因是调用函数的对象在游览器中位window
,因此this
指向window
,所以输出Jenny
注意:
严格模式下,不能将全局对象用于默认绑定,this会绑定到undefined
,只有函数运行在非严格模式下,默认绑定才能绑定到全局对象
②隐式绑定
函数还可以作为某个对象的方法调用,这时this
就指向这个上级对象。
//this 永远指向最后调用它的对象
var o = {
a:10,
b:{
fn:function(){
console.log(this.a); //undefined
}
}
}
o.b.fn();
因为this
的上一级对象为b
,而b
内部并没有a
变量的定义,所以输出undefind
③new绑定
通过构建函数new
关键字生成一个实例对象,此时this
指向这个实例对象
function test() {
this.x = 1;
}
var obj = new test();
obj.x // 1
上述代码之所以能过输出1,是因为new
关键字改变了this
的指向。
而当new
的过程中遇到return
一个对象时,此时this
指向这个对象。
function fn()
{
this.user = 'xxx';
return {user = '123'};
}
var a = new fn();
console.log(a.user); //123
而当return
一个基本类型时,this
还是指向它的实例。
function fn()
{
this.user = 'xxx';
return 1;
}
var a = new fn;
console.log(a.user); //xxx
null
同理指向实例。
④显式修改
使用apply | call | bind
改变函数的调用对象,第一个参数就是改变后调用这个函数的对象。
var x = 0;
function test() {
console.log(this.x);
}
var obj = {};
obj.x = 1;
obj.m = test;
obj.m.apply(obj) // 1
而三者的区别:
- 三者都可以改变函数的
this
对象指向 - 三者第一个参数都是
this
要指向的对象,如果如果没有这个参数或参数为undefined
或null
,则默认指向全局window
- 三者都可以传参,但是
apply
是数组,而call
是参数列表,且apply
和call
是一次性传入参数,而bind
可以分为多次传入。 bind
是返回绑定this之后的函数,apply
、call
则是立即执行 。
⑤箭头函数
箭头函数的this
非常特殊,因为它在函数定义时就被确定了,而且不会被动态改变。
<body>
<div>
<button class="btn1">普通函数</button>
<button class="btn2">箭头函数</button>
</div>
</body>
const btn1 = document.querySelector('.btn1');
const btn2 = document.querySelector('.btn2');
btn1Click = function () {
console.log("btn1" + this); //button
}
btn2Click = () =>{
console.log("btn2" + this); // window
}
btn1.addEventListener("click", btn1Click, false);
btn2.addEventListener("click", btn2Click, false);
可以看到默认函数的是由btn2
调用的,所以指向btn2
;而箭头函数在编译时就被绑定了,即btn2Click
在创建时就定义了this
,所以this
指向window
对象。
此外,箭头函数可以看作是匿名函数,是一次性的,所以与默认函数区别很明显。
- 箭头函数没有构造函数,没有原型对象
- 箭头函数没有arguments对象
- 箭头函数的this在定义时就被确认了,不会动态更新
其他
隐式绑定 VS 显式绑定
function foo() {
console.log( this.a );
}
var obj1 = {
a: 2,
foo: foo
};
var obj2 = {
a: 3,
foo: foo
};
obj1.foo(); // 2
obj2.foo(); // 3
obj1.foo.call( obj2 ); // 3
obj2.foo.call( obj1 ); // 2
显然,显示绑定的优先级更高
new绑定 VS 隐式绑定
function foo(something) {
this.a = something;
}
var obj1 = {
foo: foo
};
var obj2 = {};
obj1.foo( 2 );
console.log( obj1.a ); // 2
obj1.foo.call( obj2, 3 );
console.log( obj2.a ); // 3
var bar = new obj1.foo( 4 );
console.log( obj1.a ); // 2
console.log( bar.a ); // 4
可以看到,new绑定的优先级>
隐式绑定
new
绑定 VS 显式绑定
因为new
和apply、call
无法一起使用,但硬绑定也是显式绑定的一种,可以替换测试
function foo(something) {
this.a = something;
}
var obj1 = {};
var bar = foo.bind( obj1 );
bar( 2 );
console.log( obj1.a ); // 2
var baz = new bar( 3 );
console.log( obj1.a ); // 2
console.log( baz.a ); // 3
bar
被绑定到obj1上,但是new bar(3)
并没有像我们预计的那样把obj1.a
修改为3。但是,new
修改了绑定调用bar()
中的this
我们可认为new
绑定优先级>
显式绑定
综上,new绑定优先级 > 显示绑定优先级 > 隐式绑定优先级 > 默认绑定优先级