this
如果想用好这几个方法,需要先了解this随调用方式不同而导致指向的对象不同的各种情况,然后了解指定this的几个方法(apply,call,bind)
一个变量在全局下声明定义(如: 全局作用域下 var a = 3
, 和在全局下或者函数内不使用var声明时都会让变量成为window的一个属性)
作为函数调用
- 在非严格模式下,直接调用函数时this默认指向全局(window)
var a = 3
console.log(this) // window
console.log(this.a) //3 这里表明全局变量是window的一个属性
function exp(){
var a = 4
sayHello = "hello world"
console.log(this.a)
}
var b = exp()
// 输出3 函数遵循非严格模式下this为window
this.sayHello // hello world 没有使用var声明的函数变量也是全局变量
- 在严格模式下为undefined
function exp(){
"use strict"
return this
}
console.log(exp() === undefined)
// 输出 true
下面分析各个调用场合下this的值
作为方法被调用
作为方法被调用时,this指向方法调用方法所在的对象上
var exp = {
obj: function context() {
var text = "hello"
return this
}
}
console.log(exp.obj() === exp)
var a = exp.obj()
console.log(a === exp)
// 执行后赋值给变量a,此时this指向的是exp
var b = exp.obj
console.log(b() === window) //true,
// 直接将函数的地址赋值给变量
console.log(b === window.b) // true
// 而这时的b是一个window属性, 执行b() 相当于window.b(),所以this是window
//均输出 true
// 对象里的方法在任何地方定义均可,只要它作为对象的方法调用this就是这个对象
var person = {
var name: "KangKang",
sayName: function () {
console.log(this.name)
}
}
person.sayName()
person.sayName({name: "xiaoming"})
//person.sayName() 等价于 person.sayName.call(person)
var o = {
prop: 37,
f: function() {
return this.prop;
}
};
console.log(o.f()); // logs 37
// 在对象里面定义的方法
var o = {prop: 37};
function independent() {
return this.prop;
}
o.f = independent;
console.log(o.f()); // logs 37
// 在对象外面定义的方法,综合这两个例子可见: 只要作为对象的方法进行调用,那么this就是这个对象
作为构造函数被调用
我们知道构造函数创建一个对象的过程
- 创建新对象
- 新对象作为this指向的对象
- 为新对象添加方法、属性、并返回对象
function Obj() {
this.name = "Kangkang"
return this
}
var obj = Obj.call({})
// 创建新对象{} ,新对象 {} 作为this指向的对象
// 为新对象 {} 添加方法、属性(name = "Kangkang")、并返回对象(return this)
// 具有相同功能
function Obj() {
this.name = "KangKang"
}
// 还是成功创建新的对象
function Obj() {
this.name = "KangKang"
return 3
}
var obj2 = new Obj()
需要注意的地方:构造函数返回一个非对象类型时,不会影响创建的新对象。但是当构造函数显式返回一个对象时就会将这个对象赋值给变量,this的使用则无效。
function Fn (){
this.obj = function() {
return this
}
}
let a = new Fn()
console.log(a.obj() === Fn) // false
console.log(a.obj() === a) //true
let newObj = {
name: "小明"
}
function reObj (){
this.name = "康康"
return newObj
}
let b = new reObj()
console.log(b.name) //小明,返回的对象是newObj
在这么多变化中随时都可能出错,所以call()、apply()、bind()就提供了一个可以指定this的方式
方法的使用
-
call()
这个方法接受多个参数,第一个参数是指定的this值,剩下的都是调用的函数的参数列表
fn.call(this, arg1, arg2, ...);
如果第一个参数需要是对象,如果传入了数字、字符串、布尔值的话this会指向该原始值的自动包装对象function f(){ console.log(this) console.log(arguments) } f.call() // window f.call({name:'小明'}) // {name: '小明'}, [] f.call({name:'小红'},1) // {name: '小红'}, [1] f.call({name:'康康'},1,2) // {name: '康康'}, [1,2]
-
apply()
apply() 与call()区别在于第二个参数接受的是一个包含多个参数的数组,对于一些方法需要传入的参数不能是数组
可以使用apply()调用函数使其可以使用数组作为参数.
```
var a = [1,2,3,4,5,6,7,8,9]
sum.apply(null,a)
//将参数a全都传入,它会把参数作为数组传入
//求数组的最大元素
Math.max.apply(null,[1,2,6]) // 6
```
-
bind()
bind()方法创建一个新的函数, 当被调用时,将其this关键字设置为提供的值.this.name = "大牛" let obj = { name: "康康", age: 18, city:"上海" } let newObj = { name: "小明", sayName: function() { console.log(this.name) } } newObj.sayName()// 小明 let a = newObj.sayName.bind(obj) a() //康康 let b = newObj.sayName b() //大牛
-
箭头函数
这里说一下箭头函数,因为箭头函数没有this,所以会根据作用域链进行寻找this,这也衍生了很多用法,比如在setTimeout里经常出现的上下文(作用域)问题,如果不使用箭头函数,在函数运行时作用域就变成了全局,使用箭头函数会使函数里用到的this绑定在setTimeout的作用域上var timer = { fn1() { setTimeout(function(){ console.log(this) }, 10) }, fn2() { setTimeout(()=>{ console.log(this) },20) }, fn3: ()=> { setTimeout(()=>{ console.log(this) },30) } } timer.fn1() //window timer.fn2() // timer timer.fn3() //window // 第一个在执行时是在全局调用,相当于 fn1.call(undefined) // 第二个使用箭头函数自身没this,使this 指向了timer // 第三个自身没this的情况下,外层函数也是箭头函数所以指向了window