一. 假设没有 this

  1. let person = {
  2. name: 'frank',
  3. sayHi(){
  4. console.log(你好,我叫 + person.name)
  5. }
  6. }

分析:可以用直接保存了对象地址的变量获取 ‘name’,把这种办法简称为引用

  • 问题一:

person 如果改名,sayHi 函数就挂了,sayHi 函数甚至有可能在另一个文件里面,所以我们不希望 sayHi 函数里出现 person 引用

  • 问题二:
    1. class Person{
    2. constructor(name){
    3. this.name = name // 这里的 this 是 new 强制指定的
    4. }
    5. sayHi(){
    6. console.log(???)
    7. }
    8. }
    分析:这里只有类,还没创建对象,因此不可能获取对象的引用

二. 解决思路

那么需要一种办法拿到对象,这样才能获取对象的 name 属性

1. 土办法,用参数

对象

  1. let person = {
  2. name: 'frank',
  3. sayHi(p){
  4. console.log('你好,我叫' + p.name)
  5. }
  6. }
  7. person.sayHi(person); //你好,我叫frank

  1. class Person{
  2. constructor(name){ this.name = name }
  3. sayHi(p){
  4. console.log(你好,我叫 + p.name)
  5. }
  6. }
  7. let person = new Person('frank');
  8. person.sayHi(person); //你好,我叫frank

Python 就用了这种方法

  1. class Person:
  2. def init(self, name): # 构造函数
  3. self.name = name
  4. def sayHi(self):
  5. print('Hi, I am ' + self.name)
  6. person = Person('frank')
  7. person.sayHi() //Hi, I am frank

特点:每个函数都接受一个额外的 self,这个 self 就是传进来的对象,只不过 Python 会默认传一个传对象
person.sayHi() 等价于 person.sayHi(person),person 就被传给 self 了。

JS 没有模仿 Python 的思路,JS 在每个函数里加了 this,用 this 获取那个对象

  1. let person = {
  2. name: 'frank',
  3. sayHi(this){
  4. console.log(你好,我叫 + this.name)
  5. }
  6. }
  7. person.sayHi() 等同于 person.sayHi(person)

person 被传给 this 了(person 是个地址)这样,每个函数都能用 this 获取一个未知对象的引用了
person.sayHi()会隐式地把 person 作为 this 传给 sayHi,sayHi 可以通过 this 引用 person

这就引出另一个问题,person.sayHi(),person.sayHi(person) 哪个对

  1. let person = {
  2. name: 'frank',
  3. sayHi(this){
  4. console.log(你好,我叫 + this.name)
  5. }
  6. }
  7. person.sayHi()
  8. person.sayHi(person)

省略形式反而对了,完整形式反而是错的?

2. JS 提供两种调用形式解决这种不和谐

小白调用法

  1. let person = {
  2. name: 'frank',
  3. sayHi(p){
  4. console.log('你好,我叫' + p.name)
  5. }
  6. }
  7. person.sayHi(); //会自动把 person 传到函数里,作为 this

大师调用法

  1. let person = {
  2. name: 'frank',
  3. sayHi(p){
  4. console.log('你好,我叫' + p.name)
  5. }
  6. }
  7. person.sayHi.call({name:1}) //1

调用方式

  1. Array.prototype.forEach2 = function(fn){
  2. for(let i=0;i<this.length;i++){
  3. fn(this[i], i, this)
  4. }
  5. }

上面的代码 this 是什么?
由于大家使用 forEach2 的时候总是会用 arr.forEach2,所以 arr 就被自动传给 forEach2
this 一定是数组吗 ?不一定,比如:Array.prototype.forEach2.call({0:’a’,1:’b’})

this 的两种使用方法

隐式传递

  1. fn(1,2) // 等价于 fn.call(undefined, 1, 2)
  2. obj.child.fn(1) // 等价于 obj.child.fn.call(obj.child, 1)

显示传递

  • call(this,param) this 会被自动转化成对象(JS 的糟粕)

    1. function fn(x,y){
    2. console.log(arguments)
    3. console.log(this)
    4. }
    5. fn.call(1); //Number {1}
    6. let obj = {};
    7. fn.call(obj,123,321) //[123,321] {}
  • apply(this,arr) 可以以数组的形式传多个值到arguments

    1. function fn(x,y){
    2. console.log(arguments)
    3. console.log(this)
    4. }
    5. let obj = {};
    6. fn.apply(obj,[1,2,3]) //[1,2,3] {}
  • bind 绑定 this

    • 一个参数

      1. function fn(x,y){
      2. console.log(this.x.y)
      3. }
      4. let f2 = fn.bind({name: 'frank'}); //f2 是 fn 绑定了 this后的新函数
      5. f2() //等价于 f1.call({name: 'frank'})
    • 多个参数

      1. function fn(x,y){
      2. console.log(this.x.y)
      3. }
      4. let f3 = fn.bind({name: 'frank'},10); //f2 是 fn 绑定了 this后的新函数
      5. f3() //等价于 f1.call({name: 'frank'},10)