跟着coderwhy学习JavaScript高级(四)

699 阅读4分钟

this的作用

  • 从某些角度来说,开发中我们没有this,也是有解决方案的,但是如果真的没有this,会让我们编写代码十分不方便。
     // 如果没有this,我们更改了obj的命名 
     // 那么下面的obj.name 都需要进行修改
    var obj = {
        var name = "malong"
        eating: function() {
             console.log(obj.name + "吃东西")
        }
        studying: function() {
             console.log(obj.name + "学习")
        }
    }

this的指向

  • 在多数情况下,this出现在函数中,正常开发,通常都是在函数中使用this
    • 所有的函数在被调用时,都会创建一个执行上下文FEC
    • 这个上下文中记录着函数的调用栈、AO对象等;
    • this也是其中的一条记录;
  • 函数在调用时,JavaScript会默认给this绑定一个值
  • this的绑定和定义的位置(编写的位置)没有关系
  • this的绑定和调用方式以及调用的位置有关系
  • this是在运行时被绑定的

this绑定方式1:默认绑定

独立的函数调用我们可以理解成函数没有被绑定到某个对象上进行调用,这样this就是默认绑定

// 案例1 默认绑定
function foo() {
    console.log(this)
}

// 案例2 默认绑定
function test1() {
    console.log(this)
    test2()
}

function test2() {
    console.log(this)
    test3()
}

function test3() {
    console.log(this)
} 
test1()

// 案例3 默认绑定
function bar() {
    func()
}

var obj = {
    name: "malong",
    foo: function() {
        console.log(this)
    }
}

bar(obj.foo)

this绑定方式2:隐式绑定

  • 必须在调用的对象内部有一个对函数的引用(比如一个属性)
  • 如果没有这样的引用,在进行调用时,会报找不到该函数的错误
  • 正是通过这个引用,间接的将this绑定到了这个对象上
  • 调用方式是通过某个对象进行调用的,隐式绑定
// 案例1 隐式绑定
function foo() {
    console.log(this)
}

var obj = {
    name: "malong",
    foo: foo
}

obj.foo()

// 案例2 隐式绑定
function bar() {
    console.log(this)
}

var obj1 = {
    name: "malong1",
    bar: bar
}

var obj2 = {
    name: "malong2",
    obj1: obj1
}

obj2.obj1.bar()

// 案例3 隐式绑定
function baz() {
    console.log(this)
}

var obj3 = {
    name: "malong",
    foo: baz
}

var fn = obj3.foo;
fn()

this绑定方式3:显示绑定

  • bind
    • 形参第一个参数是对象,第二个参数是列表,返回值是一个函数。
  • call
    • 形参第一个参数是对象,第二个参数是列表
  • apply
    • 形参第一个参数是对象,第二个参数是数组 通过call绑定this
    function foo() {
        console.log(this)
    }
    
    foo.call(window)
    foo.call({name: "malong"})
    foo.call(123)

如果我们希望一个函数总是显示绑定到一个对象上,而不用向上面那样写多次

    function foo() {
        console.log(this)
    }
    
    var obj = {
        name: "123"
    }
    
    var baz = foo.bind(obj);
    baz()

this绑定方式4:new绑定

  • JavaScript中的函数可以当做一个类的构造函数来使用,也就是使用new关键字。
function Person(name) {
    console.log(this)
    this.name = name
}

var person = new Person("malong")

this的绑定过程:

  • 创建一个新的对象
  • 将构造函数的作用域赋值给新对象(构造函数.prototype === 对象.__ proto__)
  • 执行构造函数中的代码(this绑定在这个步骤完成)
  • 返回这个对象(内部有一个隐式的 return this)

内置函数绑定思考

有些时候,我们会调用一些JavaScript的内置函数,或者一些第三方库中的内置函数。这些内置函数会要求我们传入另外一个函数。我们自己并不会显示的调用这些函数,而且JavaScript内部或者第三方库内部会帮助我们执行。这些函数中的this又是如何绑定的呢?

// setTimeout
setTimeout( function() {
    console.log(this)
},100)

// DOM
var box = document.querySelector('.box')
box.onclick = function() {
    console.log(box === this)
}

// 高阶函数
var nums = [1,2,3,4]
var obj = {name: "malong"}
nums.forEach(item => {
    console.log(this)
},obj)

this绑定优先级

new > 显示绑定(apply,call,bind) > 隐式绑定 > 默认

this规则之外 忽略显示绑定

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

var obj = {
    name: "malong"
}

foo.call(obj)
foo.call(null)
foo.call(undefined)

var baz = foo.bind(null)
baz()

this规则之外 间接函数引用

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

var obj1 = {
    name: "obj1",
    foo: foo
};

var obj2 = {
    name: "obj2"
}

obj1.foo();
(obj2.foo = obj.foo)();


this规则之外 箭头函数

  • 箭头函数没有this,它的this由运行时外层作用域的this决定
  • 箭头函数没有arguments
  • 箭头函数不能new

    var name = "window";
    var obj = {
      name: "obj",
      getData: function() {
       // 会默认绑定,所以这里的this是 obj
        setTimeout(() => {
          console.log(this)
        }, 100)
      }
    }
    // 如果getData也是一个箭头函数,那么this会是window
    obj.getData()
    

面试题

    var name = "window"
    
    var person = {
        name: "person",
        sayName: function() {
            console.log(this.name);
        }
    };
    
    function sayName() {
        var sss = person.sayName;
        sss();
        person.sayName();
        (person.sayName)();
        (b = person.sayName)();
    }
    sayName()
    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.foo1.call(person2);

    person1.foo2();
    person1.foo2.call(person2)

    person1.foo3()()
    person1.foo3.call(person2)()
    person1.foo3().call(person2)

    person1.foo4()()
    person1.foo4.call(person2)()
    person1.foo4().call(person2)

    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.foo1.call(person2)

    person1.foo2()
    person1.foo2.call(person2)

    person1.foo3()()
    person1.foo3.call(person2)()
    person1.foo3().call(person2)

    person1.foo4()()
    person1.foo4.call(person2)()
    person1.foo4().call(person2)

 var name = 'window'
    function Person(name) {
      this.name = name;
      this.obj = {
        name: 'obj',
        foo1: function () {
          return function () {
            console.log(this.name)
          }
        },
        foo2: function () {
          return () => {
            console.log(this.name)
          }
        }
      }
    }

    var person1 = new Person('person1')
    var person2 = new Person('person2')

    person1.obj.foo1()()
    person1.obj.foo1.call(person2)()
    person1.obj.foo1().call(person2)

    person1.obj.foo2()()
    person1.obj.foo2.call(person2)()
    person1.obj.foo2().call(person2)