this的指向
全局上下文
在全局执行环境中(在任何函数体外部)this都指向全局对象
// 在浏览器中,window 对象同时也是全局对象:
console.log(this === window); // true
函数上下文
this是在调用时进行绑定的。不能再执行期间被赋值,并且每次函数被调用时this的值也可能会不同。
绑定规则
默认绑定
函数调用:独立函数调用,this指向全局对象
let obj = {
a: function() {
console.log(this); // obj
function b() {
console.log(this) // window
}
b();
(function (){
console.log(this) // window
})()
}
}
obj.a()
隐式绑定
考虑调用位置是都有上下文对象,或者说是否被某个对象拥有或者包含
对象属性链中只有上一层或者说最后一层再调用位置中起作用
function foo () {
console.log(this.a) // 1
}
let obj2 = {
a: 1,
foo: foo
}
let obj1 = {
a: 2,
obj2: obj2
}
obj1.obj2.foo()
隐式丢失
function foo () {
console.log(this)
}
let obj = {
foo: foo
}
let bar = obj.foo // 此时的bar是一个函数
bar() // window,this是再调用的时候绑定,所以此时的全局上下文
参数的传递实际上也是一种隐式赋值
// 参数传递
function foo () {
console.log(this.a) // 3
}
function doFoo(fn) {
fn()
}
let obj = {
a: 1,
foo: foo
}
var a = 3
doFoo(obj.foo)
显式绑定
Function.prototype.apply()、Function.prototype.call()、Function.prototype.bind()
call、apply、bind作用是改变函数执行时的上下文,简而言之就是改变函数运行时的this指向
三者区别
apply:apply接受两个参数,第一个参数是this的指向,第二个参数是函数接受的参数,以数组的形式传入
改变this指向后原函数会立即执行,且此方法只是临时改变this指向一次
function fn(...args){
console.log(this.a) // 3
console.log(args) // [1, 2]
}
let obj = {
a: 3
}
var a = 2
fn.apply(obj,[1,2]); // this会变成传入的obj,传入的参数必须是一个数组
fn(1,2) // this指向window
call:第一个参数也是this的指向,后面传入的是一个参数列表
改变this指向后原函数会立即执行,且此方法只是临时改变this指向一次
function fn(...args){
console.log(this.a) // 3
console.log(args) // [1, 2]
}
let obj = {
a: 3
}
var a = 2
fn.call(obj,1,2) // this会变成传入的obj,传入的参数必须是一个数组;
fn(1,2) // 2 this指向window
bind:第一参数是this的指向,后面传入的是一个参数列表(但是这个参数列表可以分多次传入)
改变this指向后不会立即执行,而是返回一个永久改this指向的函数
function fn(...args){
console.log(this.a)
console.log(args)
}
let obj = {
a: 3
}
var a = 2
const bindFn = fn.bind(obj) // this 也会变成传入的obj ,bind不是立即执行需要执行一次
bindFn(1,2) // this指向obj
fn(1,2) // this指向window
new绑定
new 一个构造函数的时候, 会发生下列操作:
- 在内存中创建一个新对象
- 在这个对象内部的[[Prototype]]特性被赋值为构造函数的Prototype属性
- 构造函数内部的this被赋值为这个新对象(即this指向新对象)
- 如果构造函数返回非空对象,则返回该对象;否则,返回刚创建的新对象
function myNew(Fn, ...args) {
const newObj = {} //新建一个空对象
if (Fn.prototype !== null ) {
newObj.__proto__ = Fn.prototype // 新建对象的原型指向构造函数的显示原型
}
// 构造函数的this指向这个新对象
const res = Fn.apply(newObj, args)
// 如果构造函数返回非空对象,则返回该对象
if(typeof res === 'function' || (typeof res === 'object' && res !== null)) {
return res
}
// 否则返回该对象
return newObj
}
// 利用Object
function myNew2 (Fn, ...args) {
// 创建一个空的对象,将实例化对象的原型指向构造函数的原型对象
const newObj = Object.create(Fn.prototype)
const res = Fn.apply(newObj, args)
return typeof res === 'object' ? res : newObj
}
function Person(name) {
this.name = name
}
Person.prototype.sayName = function () {
console.log(`My name is ${this.name}`)
}
const person1 = myNew(Person, 'Jack')
const person2 = myNew2(Person, 'Jack')
console.log(person1)
console.log(person2)
判断this:
创建箭头函数时,就已经确定了它的 this 指向。箭头函数内的 this 指向外层的 this
函数是否在new中调用(new绑定)?如果是的话this绑定的是新创建的对象。
var bar = new foo()函数是否通过call、apply(显式绑定)或者硬绑定调用?如果是的话,this绑定的是指定的对象。
var bar = foo.call(obj2)函数是否在某个上下文对象中调用(隐式绑定)?如果是的话,this绑定的是那个上下文对象。
var bar = obj1.foo()如果都不是的话,使用默认绑定。如果在严格模式下,就绑定到undefined,否则绑定到全局对象。
var bar = foo()