this的指向

48 阅读5分钟

image.png

为什么要使用this

// 假设对象名称user改为info
const info = {
  id: 1,
  name: 'luckyCoder'address: '猿星'// 使用this
  eating: function() {
    console.log(`${this.name}${this.address}吃东西~`)
  },
  // 不使用this
  running: function() {
   // 此处也需要同步进行修改为info
   console.log(`${info.name}${info.address}跑步~`)
  }
}
info.eating() // luckyCoder在猿星吃东西~
info.running() // luckyCoder在猿星跑步~

如果在不使用this的情况下修改了对象的名称,那我们必须将其内部的方法所使用的名称同步更改,而使用this则不需要考虑这个问题

this的指向

this是个关键字,它的值是在函数调用的时候确定的(函数也相当于一个对象), 严格模式下指向undefined

默认绑定

function foo() {
  function bar() {
    console.log(this)
  }
  return bar
}

const fn = foo()

fn() // 独立的函数调用 this => window

无论函数定义时通过哪些复杂的方式,只要调用的时候是独立的,就符合this默认绑定的规则,指向window

var name = '林一一'
var obj = {
    name : '二二',
    callback: function(){
        console.log(this.name)
    }
}
setTimeout(obj.callback,1000)
/* 输出
*   林一一
*/

setTimeout函数的延迟执行函数中的this指向window,若延迟执行函数是箭头函数this指向其外层的上下文

隐式绑定

const obj1 = {
  name: 'obj1',
  foo: function() {
    console.log(this)
  }
}

const obj2 = {
  name: 'obj2',
  bar: obj1.foo
}

obj2.bar() // 通过对象点语法的方式调用函数 this => obj2对象

通过object.fn()的方式调用某个函数时,object对象会被JS引擎绑定到fn函数中的this,这个绑定的过程是内部自动绑定,无法看到绑定过程,称为隐式绑定

var name = '林二二'
var obj = {
    name: '林一一',
    fn: function () {
        var name = '小三'
        return this.name
    }
}
console.log(obj.fn())   // '林一一'
var fo = obj.fn
console.log(fo())       // '林二二'     fo() ==> window.fo()
console.log(obj.fn()()) // '林二二'

(1)obj.fn()中函数fn()作为对象方法调用,this指向obj对象

(2)当调用函数作为参数传递或者变量赋值给另一个变量,this指向全局

(3)obj.fn()执行完后有一个函数返回,最后相当于执行函数A(), this指向全局

显示绑定

不希望在对象内部包含这个函数的引用,同时又希望在这个对象上进行强制调用?在JS中所有的函数都可以使用call,apply和bind方法,通过这些方法实现显示绑定规则

function foo() {
  console.log(this)
}

const obj = {
  name: "obj"
  foo: foo // 通过foo.apply(obj)调用时 这段代码可以省略
}

// 通过对隐式绑定的学习 我们知道 如果希望foo函数被调用时this是指向obj的
// 那么我们需要给obj添加一个foo属性并且属性值指向foo函数 
// 再进行对象点语法的方式调用
// 从而达到隐式绑定规则 this则指向obj
obj.foo() // this => obj

// 那么我们也可以使用call/apply的方式来实现显示绑定规则
// 并且也无需在obj中创建foo属性
foo.apply(obj) // 显示绑定规则 this => obj

// call/apply是可以指定this的绑定对象
call,apply,bind修改this的指向

共同点:

(1)改变this的指向

(2)三者的第一个参数都是this要指向的对象,如果没有默认全局window

不同点:

(1)调用方式

call: 可以直接调用函数,并传递参数列表,立即执行

apply:可以直接调用函数,参数以数组或类数组的形式传递,立即执行

bind: bind可以分多次传入参数,是返回绑定this之后的函数,apply,call则是立即执行

构造函数的this

new的作用
function Test(name) {
  this.name = name
}
Test.prototype.sayName = function() {
  console.log(this.name)
}
const t = new Test('yck')
console.log(t.name)
t.sayName()

(1)new通过构造函数Test创建出来的实例可以访问构造函数中的属性 (2)new通过构造函数Test创建出来的实例可以访问到构造函数原型链中的属性 (3)new操作符,实例与构造函数通过原型链连接起来

function Test1(name) {
  this.name = name
  return 1
} 
const t1 = new Test1('yck')
console.log('1',t1.name)

(1) 构造函数如果返回原始值,返回值毫无意义

function Test2(name) {
  this.name = name 
  return { age: 26 }
}
const t2 = new Test2('yck')
console.log(t2) // { age: 26 }
console.log(t2.name) // undefined

(2) 构造函数如果返回值为对象,这个返回值会被正常返回

当使用new关键字调用函数时,如果构造函数返回这个对象,this指向这个对象,如果返回基本数据或者没有返回,this指向实例,尽量不要返回值。因为返回原始值不会生效

new操作符的执行过程

1.创建一个空对象,

2.将空对象的_proto_属性指向构造函数的原型对象

3.将构造函数的this绑定到新的对象上

4.根据构造函数返回的类型判断返回值

// 第一个参数是构造函数,第二个参数是构造函数的参数
function newFun() {
    let obj = {}
    // 将构造函数的作用域赋给新对象, 
    // 获取构造函数即传入的第一个参数,arguments 是类数组,我们不能直接使用 shift 方法,我们可以使用 call 来调用 Array 上的 shift 方法,获取 fn
    let fn = Array.prototype.shift.call(arguments)
    // 把obj的原型指向构造函数的原型对象上 Object.setPropertyOf(obj, fn.prototype)
    obj.__proto__ = fn.prototype
    // 为这个新对象添加属性,将obj绑定到构造函数上, 并且传入剩余的参数
    let result = fn.apply(obj,arguments)
    // 如果返回值是对象,则返回,否则默认返回新对象,这样就实现了忽略构造函数返回的原始值
    return result instanceof Object ? result: obj
}
优先级
function Person(name, age) {
  // 往创建的对象中添加属性
  this.name = name
  this.age = age
  
  // 因构造函数被调用了两次 产生了两个新对象 
  // 第一次this为所创建出的a对象
  // 第二次this为所创建出的b对象
  console.log(this)
}

// 调用构造函数并传入参数 返回新的对象
const a = new Person('a', 18)
const b = new Person('b', 20)

console.log(a) // => {name: 'a', age: 18} 
console.log(b) // => {name: 'b', age: 20}

this绑定规则的优先级:new绑定 > 显示绑定 > 隐式绑定 > 默认绑定

箭头函数

const foo = () => {
  // 等同于我们在函数中使用某个变量 
  // 在函数中找不到的话则沿着作用域链向上查找
  console.log(this) 
}

const obj = {
  foo: foo
}

const info = {}

foo() // this => window
obj.foo() // this => window
foo.call(info) // this => window
const f = new foo() // 箭头函数不可以使用new关键字进行调用 报错

参考文献

juejin.cn/post/735086… juejin.cn/post/684490…