JavaScript中this的指向
- this是Javascript语言的一个关键字。 它代表函数运行时,自动生成的一个内部对象,只能在函数内部使用,随着函数使用场合的不同,this的值会发生变化,指向是不确定的,也就是说是可以动态改变的;但是有一个总的原则,那就是this指的是,调用函数的那个对象。
this在立即执行函数中的指向
- 立即执行函数中this指向window
var number = 1;
var obj = {
number: 2,
showNumber: function() {
(function() {
console.log(this.number);//1,this指向window
})();
}
};
obj.showNumber();
复制代码
this在普通函数中的指向
- 如果一个函数中有this,但函数没有调用者时,分严格模式和非严格模式
- 非严格模式
function f() {
console.log(this);//window
}
f();
复制代码
- 严格模式
function f() {
'use strict';
console.log(this);//undefined
}
f();
复制代码
this在内置函数中的指向
- 延时函数内部this指向window对象
function fun() {
var a = 1;
setTimeout(function() {
console.log(this);//window,实际上就是window.setTimeout()
}, 100)
}
fun();
复制代码
this在构造函数中的指向
- 当一个函数作为构造器使用时(通过new关键字), 它的 this 值绑定到新创建的那个对象。如果没使用new关键字, 那么他就只是一个普通的函数, this 将指向 window 对象
function Fun(name, age) {
this.name = name;
this.age = age;
}
var fun = new Fun('Tom', 21);//实例化,this指向fun实例化对象
console.log(fun.name);//Tom
复制代码
this在箭头函数中的指向
- 箭头函数的this指向定义时所在的对象。由于箭头函数不绑定this,它会捕获其所在(即定义的位置)上下文的this值, 作为自己的this值。所以箭头函数只有在函数中定义,才会继承该函数的this对象,即指向该函数所在的对象;否则就指向window
//没有函数运行生成this对象,所以this最终指向window
var a = 1;
var obj = {
a: 10,
child: {
a: 100,
f: () => {
console.log(this.a);//1,this指向window
},
fun: function() {
console.log(this.a);
}
}
};
obj.child.f();//1
obj.child.fun();//100
复制代码
//当fun函数运行时会生成this对象,此时this指向child;
//而箭头函数又是在fun函数中定义的,所以箭头函数中会捕获继承此this作为自己的this
var obj = {
a: 1,
child: {
a: 2,
fun: function() {
setTimeout(() => {
console.log(this.a);//2
})
}
}
};
obj.child.fun();
复制代码
this在对象中的指向
- 如果一个函数中有this,那么this指向的就是调用函数最近的对象
//一层对象
var obj = {
a: 10,
fun: function() {
console.log(this.a);//10
}
};
obj.fun();//obj就是函数fun的调用者,所以this指向obj
复制代码
//多层对象
var obj = {
a: 1,
child: {
a: 2,
fun: function() {
console.log(this.a);//2,等于child.a
}
}
};
obj.child.fun();//调用函数最近的对象为child,所以this指向child
复制代码
var a = 0;
var obj = {
a: 1,
child: {
a: 2,
fun: function() {
console.log(this.a);//0
}
}
};
var f = obj.child.fun;//函数别名,隐式丢失
f();//函数没有调用者,所以this指向window
复制代码
this遇上return
- 如果return返回的不是一个对象那么this还是指向函数的实例;虽说null指向空对象,但this还是指向函数的实例
function Fun() {
this.user = 'Tom';
return 1;
}
var f = new Fun();
console.log(f);//Fun { user: 'Tom' }
复制代码
function Fun() {
this.user = 'Tom';
return undefined;
}
var f = new Fun();
console.log(f);//Fun { user: 'Tom' }
复制代码
function Fun() {
this.user = 'Tom';
return null;
}
var f = new Fun();
console.log(f);//Fun { user: 'Tom' }
复制代码
- return返回的是Object。这种情况下,不再返回this对象,而是返回return语句的返回值
function Fun() {
this.user = 'Tom';
console.log(this);//Fun { user: 'Tom' }
return {n: 1};
}
var f = new Fun();
console.log(f);//{n: 1}
复制代码
this指向的改变
apply()、call()和bind()改变this的异同:
- 同:
- call,apply,bind都会改变this的指向
- call,apply,bind不传参数自动绑定在window
- 异:
- call,apply的第一个参数都是将要绑定的this的对象
- apply()中的第二个参数为需要传入的参数,必须为数组类型[arg1,arg2]
- call()中第二个参数为一个个需要传入的参数arg1,arg2…
- apply(),call()改变this时不会产生新函数,只是在改变this时会立即调用函数
- bind()把对象和函数绑定死后,产生新的函数,需要重新调用才能执行
apply()
- apply()中第一个参数为绑定this的对象,第二个参数为传入的参数(封装为数组)
var a = 1;
var obj = {
a: 10
};
function fun(num) {
console.log(this.a + '' + num);//101
}
fun.apply(obj, [1]);//将fun绑定到obj对象上,this指向obj
复制代码
call()
- call()中第一个参数为绑定this的对象,第二个参数为传入的一个个参数
var obj = {
a: 10
};
function fun(num) {
console.log(this.a + '' + num);//101
}
fun.call(obj, 1);//将fun函数绑定到obj对象上,this指向obj
复制代码
bind()
- bind()方法用于将函数体内的this绑定到某个对象,然后返回一个新函数(原函数的拷贝)
var obj = {
a: 10
};
var obj1 = {
a: 100
}
function fun() {
console.log(this.a);//10
}
var f = fun.bind(obj);//新函数
f();//10,重新调用
//bind绑定this后生成的新函数的this无法再改变,除了new绑定外
f.apply(obj1);//10
var f1 = f.bind(obj1);
f1();//10
//只能重新改变fun函数的this
fun.apply(obj1);//100
var f2 = fun.bind(obj1);
f2();//100
复制代码
this绑定判断
- 优先级从高到低
- 函数是否在new中调用(new绑定),如果是的话this绑定的就是新创建的对象实例
var bar = new foo()//new绑定,优先级最高,this绑定到bar对象上
复制代码
- 函数是否通过call,apply(显式绑定)或者bind(硬绑定)调用,如果是的话this绑定的就是那个上下文对象
foo.apply(obj);//显式绑定,this绑定到obj对象上,会自己调用函数
var bar = foo.bind(obj);//硬绑定,将foo绑定到obj对象上,然后再生成一个新函数返回
bar();//函数手动调用
复制代码
- 函数是否在某个上下文对象中调用(隐式绑定),如果是的话this绑定的就是那个上下文对象
obj.foo();//隐式绑定,this绑定到obj对象上
var bar = obj1.foo();//隐式丢失,变为默认绑定
bar();//默认绑定,独立函数调用,this绑定到window或undefined
复制代码
- 如果都不是的话,那就是默认绑定;严格模式下就绑定到undefined上,否则绑定到全局对象window上
foo();//默认绑定
复制代码
this绑定例外
- 如果把null或者undefined作为this的绑定对象传入call,apply或bind,这些值在调用时会被忽略,实际上应用的是默认绑定
function foo() {
console.log(this.a);
}
var a = 1;
foo.apply(null);//1
foo.call(undefined);//1
复制代码
- apply使用null会将一个数组展开;bind使用null会对参数进行柯里化(预先设置一些参数)
function foo(a, b) {
console.log(a + ', ' + b);
}
//展开一个数组
foo.apply(undefined, [1, 2]);//1, 2
//对参数进行柯里化(预先设置一些参数)
var bar = foo.bind(null, 3);//预先设置一些参数
bar(4);//3, 4
复制代码
- 使用null可能带来一些副作用,更安全的做法是使用Object.create(null)创建一个空对象,它比{}更空,不会创建Object.prototype这个属性
function foo(a, b) {
console.log(a + ', ' + b);
}
//创建一个空对象
var o = Object.create(null);
//展开一个数组
foo.apply(o, [1, 2]);//1, 2
//对参数进行柯里化(预先设置一些参数)
var bar = foo.bind(o, 3);//预先设置一些参数
bar(4);//3, 4
复制代码
this绑定的优先级
- new绑定 > 显式绑定 > 隐式绑定 > 默认绑定
- 毫无疑问,默认绑定优先级是最低的,所有我们不做验证
- 隐式绑定 VS 显式绑定:显式绑定优先级更高
function foo() {
console.log(this.a);
}
var obj1 = {
a: 1,
foo: foo
};
var obj2 = {
a: 2,
foo: foo
};
//隐式绑定
obj1.foo();//1
obj2.foo();//2
//隐式绑定后再显式绑定,显式绑定修改了this的指向
obj1.foo.call(obj2);//2
obj2.foo.call(obj1);//1
复制代码
- new绑定 VS 隐式绑定:new绑定优先级更高
function foo(n) {
this.n = n;
}
var obj1 = {
foo: foo
};
//隐式绑定
obj1.foo(1);
console.log(obj1.n);//1
//先隐式绑定后new绑定
var bar = new obj1.foo(2);
console.log(obj1.n);//1,没有修改obj.n的值
console.log(bar.n);//2,new改变了隐式绑定的this,使其指向bar
复制代码
- new绑定 VS 显式绑定:new绑定优先级更高
- new无法和call/apply一起使用,所以只能使用硬绑定bind和new绑定测试
function foo(n) {
this.n = n;
}
var obj = {};
//硬绑定bind
var bar = foo.bind(obj);
bar(1);
console.log(obj.n);//1
//在硬绑定的基础上new绑定
var baz = new bar(2);
console.log(obj.n);//1,没有修改obj.n的值
console.log(baz.n);//2,new改变了硬绑定的this,使其指向baz
复制代码
this常见面试题
- 重点:立即执行函数
var number = 1;
var obj = {
number: 2,
showNumber: function() {
//将obj中的number改变
this.number = 3;//由于showNumber的拥有者是obj,所以这里this就是obj
console.log(this.number);//3
(function() {//立即执行函数中this指向window
console.log(this.number);//1
})();
}
};
obj.showNumber();
复制代码
var name = "window";
var person = {
name: "person",
sayName: function () {
console.log(this.name);
}
};
function sayName() {
var sss = person.sayName;
//没有调用者,独立函数调用
sss();//window
//person调用
person.sayName();//person
(person.sayName)(); //person
//将函数引用赋值给b,实际是调用person中的sayName函数,而不是person.sayName()
(b = person.sayName)();//window
}
sayName();
复制代码
- 重点:arguments对象,对象中的‘.’和‘[]’和数组中的‘[]’
var length = 100;
function f1() {
console.log(this.length);
};
var obj = {
x: 10,
f2: function(f1) {
f1();//100,没有调用者,默认为window
arguments[0]();//2,调用者为arguments,所以this.length为arguments的长度
}
};
obj.f2(f1, 1);
复制代码
(function(){
this.length = 10;//全局变量length,this指向window
var fn = function(){
console.log(this.length);//2
},
arr = [fn, 'hello'];
fn.length = 100;//fn函数中添加了length属性
arr[0]();//调用者arr,this.length为arr的长度
})();
复制代码
- 重点:全局变量和局部变量
var a = 1
var obj = {
a: 10,
dbl: function () {
a *= 2//a前面没有this,所以a为全局变量
}
}
obj.dbl()
console.log(obj.a + a)//12
复制代码
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END