普通函数:谁调用指向谁
普通函数是指由构造函数Function创建或者使用关键字function声明定义的函数。通常情况下,普通函数的this并不天生指向其定义所在的对象,而是指向它的调用者。
let obj = {
a: 'obj',
func () {
console.log(this.a)
}
}
let obj2 = {
a: 'obj2',
func: obj.func
}
obj.func() // this指向调用者obj,输出:obj1
obj2.func() // this指向调用者obj2,输出:obj2
let func = obj.func
window.a = 'window'
func() // 未指定调用者,this指向全局对象,输出:window
"use strict"
// 省略函数定义
let func = obj.func
window.a = 'window'
func() // 报错:Uncaught TypeError: Cannot read property 'a' of undefined
箭头函数中的this
箭头函数的this指向,和箭头函数定义所在上下文的this相同。对于普通函数,this在函数调用时才确定;而对于箭头函数,this在箭头函数定义时就已经确定了,并且不能再被修改,指向此时外层函数的this,如果没有外层函数则指向全局对象。
let obj = {
A () {
return () => {
return this
}
},
B () {
return function () {
return this
}
}
}
let func = obj.A()
console.log(func() === obj) // true
func = obj.B()
console.log(func() === obj) // false
console.log(func() === window) // true
构造函数中的this
JS里的普通函数可以使用new操作符来创建一个对象,此时该函数就是一个构造函数,箭头函数不能作为构造函数。执行new操作符,其实JS内部完成了以下事情:
- 创建一个空的简单JavaScript对象(即{});
- 将构造函数的prototype绑定为新对象的原型对象 ;
- 将步骤1新创建的对象作为this的上下文并执行函数 ;
- 如果该函数没有返回对象,则返回this。
function A () {
this.a = 1
this.func = () => {
return this
}
}
let obj = new A()
console.log(obj.a) // 1
console.log(obj.func() === obj) // true
使用apply和call动态改变函数this的指向
使用apply可以在调用函数时改变this的指向,当然,箭头的this已经不可改变,所以不能通过该方法修改this指向。
function dir (x, y) {
this.x = x
this.y = y
}
let obj = {
a: 1,
b: 2
}
dir.apply(obj, [3, 4])
console.log(JSON.stringify(obj)) // {"a":1,"b":2,"x":3,"y":4}
call函数和apply的作用都是更改this的指向,只是使用上稍微不同,call函数接收多个参数。
对箭头函数使用call或者apply将不能指定this,因为箭头函数的this在定义时已经确定:
let dir = (x, y) => {
this.x = x
this.y = y
}
let obj = {
a: 1,
b: 2
}
dir.apply(obj, [3, 4])
console.log(JSON.stringify(obj)) // {"a":1,"b":2}
console.log(window.x, window.y) // 3, 4
使用bind创建新函数并绑定this
bind() 方法会创建一个新函数,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。bind方法有以下特点:
- 新生成一个函数,包装了原函数,这意味着原函数的功能不被影响,但是调用新函数会调用原函数
- 对箭头函数无效
- 新生成的函数绑定了固定的this,再次bind生成的函数不会重新绑定this,很类似箭头函数,不过二者存在很大不同。
- 非严格模式指定this为undefined,结果会指向全局对象
- bind函数的第二个以及之后的参数,会作为原函数的参数并占用参数位置,绑定函数接收的参数会拼接在后面一并传给原函数。
let test = function (b, c) {
console.log(this.a, b, c)
}
let obj = { a: 'obj1-a' }
let obj2 = { a: 'obj2-a' }
let bindFunc = test.bind(obj, 'b')
bindFunc('c') // obj1-a, b, c
bindFunc.call(obj2, 'c') // 指定this无效,输出:obj1-a, b, c
test.call(obj2, 'b', 'c') // 原函数不被影响,输出:obj2-a, b, c
let bindFunc2 = bindFunc.bind(obj2, 'c-rebind')
bindFunc2() // 不能再次重新绑定,输出:obj1-a, b, c-rebind
let bindFunc3 = test.bind(undefined, 'b')
window.a = 'window-a'
bindFunc3('c') // 非严格模式输出window-a, b, c
以上是bind函数的常规用法,bind函数生成的函数与箭头函数不同,它仍然可以用于new操作符,不过这种情况下,bind的this对象会被忽略,this还是指向新生成的对象:
function A (b) {
this.a = 'a'
this.b = 'b'
}
let obj = {
a: 'obj-a'
}
let B = A.bind(obj)
let newObj = new B()
console.log(newObj.a, obj.a) // a, obj-a
this指向规则总结
- 绝大多数情况下,非箭头函数内部的this由调用者确定,即谁调用指向谁
- 箭头函数的this取决于函数定义所在的上下文中this,即函数定义外部this是什么,箭头函数内部的this就是什么,相当于固化了当前执行环境中的this,注意不是函数定义所在的对象!
- 构造函数中的this指向新创建的对象
- apply/call方式调用的函数,其this可以在调用时指定
- bind函数可以将根据一个函数生成一个新的函数,并且该函数的this绑定到指定的对象上
本文由 前端技术精髓 创作,采用 知识共享署名4.0 国际许可协议进行许可
本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名
最后编辑时间为: Jan 16, 2020 at 01:14 am