This 指向问题
- 函数调用时,
javascript默认给this绑定一个值 this的绑定与 定义的位置(编写位置)无关this的绑定与 调用方式以及调用位置 有关this是在运行时被绑定的
绑定规则
默认绑定
function fn() {
console.log(this)
// node: Object [global]
// browser: Object [window]
// 严格模式下:
// node: {}
// browser: Object [window]
}
fn()
const fn = () => {
console.log(this)
// node: {}
// browser: Object [window]
// 严格模式下:
// node: {}
// browser: Object [window]
}
fn()
var obj = {
name: 'John',
hello: function () {
console.log(this)
},
}
obj.hello()
// node: Object [obj]
// browser: Object [obj]
// 严格模式下:
// node: Object [obj]
// browser: Object [obj]
var hello = obj.hello
hello() // 独立调用,在严格模式下指向undefined
// node: Object [global]
// browser: Object [window]
// 严格模式下
// node: undefined
// browser: undefined
// 下述情况同属上列独立调用
const test = (fn) => {
fn()
}
function fn() {
console.log(this)
}
test(fn)
隐式绑定
前提条件:
- 必须在调用的对象内部有一个对函数的引用
- 如果没有这样的引用,在进行调用时,会找不到该函数而报错
- 正式通过这个引用,间接的将
this绑定到了这个对象之上
function foo() {
console.log(this)
}
var obj = {
fn: foo,
}
foo() // Object [global]
obj.fn() // Object [obj]
显示绑定
如果我们不希望在对象内部包含这个函数的引用,同时又希望在这个对象上强制调用某函数
可以使用 call和 apply方法
- 第一个参数是相同的,要求传入一个对象
- 这个对象是给
this准备的 - 在调用这个函数时,会将
this绑定到这个传入的对象上
- 这个对象是给
- 后面的参数,
apply为数组,call为参数列表
func.apply(thisArg, [argsArray])
func.call(thisArg, arg1, arg2, arg3, ...)
通过 call和 apply绑定 this对象
- 显示绑定过后,
this就会明确的指向绑定的对象
function foo() {
console.log(this)
}
var efo = foo.call(window) // 这就是怪异函数对象
foo.call({name: 'apries'})
foo.call(123) // [Number: 123]
- 使用
bind方法,bind()创建一个新的绑定函数(bound function, BF) - 绑定函数是一个怪异函数对象(
exotic function object) 术语
意料之外
new绑定
JavaScript中的函数可以当做一个类的构造函数来使用,也就是 new关键字
使用 new关键字来调用函数时,会执行如下的操作:
- 创建一个新对象
- 对这个新对象执行
prototype连接原型链 - 这个新对象被绑定到函数调用的
this上 - 如果函数没有返回其他对象,则返回这个隐式的
this
function Person(name, age) {
this.name = name
this.age = age
console.log(this)
}
var p1 = new Person('John', 25) // Person { name: 'John', age: 25}
null和 undefined
如果在显示绑定中,传入一个 null或者 undefined,那么这个显示绑定会被忽略,使用默认绑定(非严格模式)
function foo() {
console.log(this)
}
foo.apply(null) // S -> null Object [global]
foo.apply(undefined) // S -> undefined Object [global]
foo.apply('123') // S -> 123 [String: '123']
间接引用
var obj1 = {
name: 'obj1',
foo: function () {
console.log(this)
},
}
var obj2 = {
name: 'obj2',
}
obj1.foo() // Object [obj1]
;(obj2.foo = obj1.foo)() // Object [global] 默认绑定
obj2.foo() // Object [obj2]
箭头函数
- 箭头函数不会绑定
this,arguments属性 - 剪头函数不能作为构造函数来使用
面试题🌰
- 🌰1
var name = 'window'
var person = {
name: 'person',
sayName: function () {
console.log(this.name)
},
}
function sayName() {
var sss = person.sayName
sss() // 直接调用-> window
person.sayName() // 隐式绑定->person
;(person.sayName)() // 与上述一样,隐式绑定->person
;(b = person.sayName)() // 间接引用,返回b,调用->window
}
sayName()
- 🌰2
var name = 'window'
var person1 = {
name: 'person1',
foo1: function () {
console.log(this.name)
},
foo2: () => console.log(this.name),
foo3: function () {
return function () {
console.log(this.name)
}
},
foo4: function () {
return () => console.log(this.name)
},
}
var person2 = { name: 'person2' }
person1.foo1() // 隐式绑定->person1
person1.foo1.call(person2) // 显示绑定->person2
person1.foo2() // 上层作用域->window
person1.foo2.call(person2) // 上层作用域->window
person1.foo3()() // 隐式绑定->window
person1.foo3.call(person2)() // window 前面拿到的还是那个函数 默认绑定(后面那个执行的)
person1.foo3().call(person2) // 显示绑定->person2
person1.foo4()() // person1
person1.foo4.call(person2)() // person2
person1.foo4().call(person2) // person1
- 🌰3️
var name = 'window'
function Person(name) {
this.name = name
this.foo1 = function () {
console.log(this.name)
}
this.foo2 = () => console.log(this.name)
this.foo3 = function () {
return function () {
console.log(this.name)
}
}
this.foo4 = function () {
return () => console.log(this.name)
}
}
var person1 = new Person('person1')
var person2 = new Person('person2')
person1.foo1() // 隐式绑定->person1
person1.foo1.call(person2) // 显示绑定->person2
person1.foo2() // new 绑定 上层作用域 -> person1
person1.foo2.call(person2) // 无关 new 绑定上层作用域-> person1
person1.foo3()() // 隐式绑定->window
person1.foo3.call(person2)() // window 前面拿到的还是那个函数 默认绑定(后面那个执行的)
person1.foo3().call(person2) // 显示绑定->person2
person1.foo4()() // person1
person1.foo4.call(person2)() // person2
person1.foo4().call(person2) // person1