js中this的四种用法
在JavaScript中每一个函数都是一个对象,所以在这个函数中, var temp = this,指的是当前对象
this是一个关键字,它代表函数运行时,自动生成的一个内部对象,只能在函数内部使用
在 ES6 的箭头函数下, call 和 apply 将失效,
this在函数执行时,this 总是指向调用该函数的对象。要判断 this 的指向,其实就是判断 this 所在的函数属于谁。在《javaScript语言精粹》这本书中,把 this 出现的场景分为四类,简单的说就是:
- 有对象就指向调用者
- 没对象指向全局对象
- new 出来的 指向新对象
- call/bind/apply来改变this的指向
1. 有对象就指向调用者 (隐式绑定)
函数有所属对象的时候,通过 . 表达式调用,这时 this自然指向所属对象,
var obj = {
value: 100,
getValue: function() {
console.log(this.value) // 这里this 就是obj对象
return this.value
}
}
var fn = obj.getValue
console.log(obj.getValue()) ===> 100
console.log(fn()) ===> undefined
<!-- getValue() 属于对象 myObject,并由 myOjbect 进行 . 调用,因此 this 指向对象 myObject。 -->
2. 函数没有所属对象:指向全局对象 (默认绑定)
var value = 'global'
var obj = {
value: 100
}
obj.getValue = function() {
var foo = function() {
console.log(this.value) // 'global' ,var 声明的默认为全局变量,所以这里访问的是全局的, 严格模式下为undefined
console.log(this) // 全局对象global window
}
foo()
return this.value;
}
<!--
在上述代码块中,foo 函数虽然定义在 getValue 的函数体内,
但实际上它既不属于 getValue 也不属于 myObject。
foo 并没有被绑定在任何对象上,所以当调用时,它的 this 指针指向了全局对象 global。
据说这是个设计错误。
-->
3. 构造器中的this 指向新对象 (new 绑定)
js 中,我们通过 new 关键词来调用构造函数,此时 this 会绑定在该新对象上。
let Dog = function() {
this.name = '旺财'
}
let dog1 = new Dog()
console.log(dog1.name)
===> 旺财
<!--
顺便说一句,在 js 中,构造函数、普通函数、对象方法、闭包,这四者没有明确界线。界线都在人的心中。
-->
4. apply 和 call 调用以及 bind 绑定:指向绑定的对象 (显示绑定)
- apply方法接受两个参数, 第一个是函数运行的作用域, 第二个是函数的参数数组(arguments)
- call方法第一个参数的意义与 apply() 方法相同,只是其他的参数需要一个个列举出来。
- bind
简单来说,call 的方式更接近我们平时调用函数,而 apply 需要我们传递 Array 形式的数组给它。它们是可以互相转换的。
var myObject = {value: 100};
var foo = function(){
console.log(this.value);
};
foo(); // ===> undefined
foo.apply(myObject); ===> 100 // { value: 100 }
foo(); // ===> undefined
foo.call(myObject); ===> 100 // { value: 100 }
foo(); // ===> undefined
var newFoo = foo.bind(myObject);
newFoo(); // { value: 100 }
优先级
new绑定 > 显示绑定 > 隐式绑定 > 默认绑定
关于call/apply/bind的区别与用法
- call
call 方法第一个参数是要绑定给this的值,后面传入的是一个参数列表。当第一个参数为null、undefined的时候,默认指向window。
var obj = {
message: 'My name is: '
}
function getName(firstName, lastName) {
console.log(this.message + firstName + ' ' + lastName)
}
getName.call(obj, 'Dot', 'Dolby')
====> My name is: Dot Dolby
- apply
apply接受两个参数,第一个参数是要绑定给this的值,第二个参数是一个参数数组。当第一个参数为null、undefined的时候,默认指向window。
var obj = {
message: 'My name is: '
}
function getName(firstName, lastName) {
console.log(this.message + firstName + ' ' + lastName)
}
getName.apply(obj, ['Dot', 'Dolby'])// My name is: Dot Dolby
====> My name is: Dot Dolby
- bind
和call很相似,第一个参数是this的指向,从第二个参数开始是接收的参数列表。区别在于bind方法返回值是函数以及bind接收的参数列表的使用。
var obj = {
name: 'Dot'
}
function printName() {
console.log(this.name)
}
var dot = printName.bind(obj)
console.log(dot) // function () { … }
dot() // Dot
ES5 实现继承 通过call或apply修改this指向
var Person1 = function () {
this.name = 'Dot';
}
var Person2 = function () {
this.getname = function () {
console.log(this.name);
}
Person1.call(this);
}
var person = new Person2();
person.getname(); // Dot
Person2 实例化出来的对象 person 通过 getname 方法拿到了 Person1 中的 name。因为在 Person2 中,Person1.call(this) 的作用就是使用 Person1 对象代替 this 对象,那么 Person2 就有了 Person1 中的所有属性和方法了,相当于 Person2 继承了 Person1 的属性和方法。
参数的使用
call 是把第二个及以后的参数作为 fn 方法的实参传进去,而 fn1 方法的实参实则是在 bind 中参数的基础上再往后排
function fn(a, b, c) {
console.log(a, b, c);
}
var fn1 = fn.bind(null, 'Dot');
fn('A', 'B', 'C'); // A B C
fn1('A', 'B', 'C'); // Dot A B
fn1('B', 'C'); // Dot B C
fn.call(null, 'Dot'); // Dot undefined undefined