03-this

6/22/2022

# 1. this是什么?

我们先来看一个让人困惑的问题,定义一个函数,我们采用三种不同的方式对它进行调用,它产生了三种不同的结果,如下:

<script>
    function fn() {
        console.log(this);
    }
    // 直接调用,独立调用
    fn()

    // 创建一个对象,让对象去借用fn函数
    var obj = {
        name: "wc"
    };
    fn.call(obj)

    var obj2 = {
        name: "xq",
        func: fn
    }
    obj2.func()
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

这个的案例可以给我们什么样的启示呢?

  1. 函数在调用时,JavaScript会默认给this绑定一个值;
  2. this的绑定和定义的位置(编写的位置)没有关系;
  3. this的绑定和调用方式以及调用的位置有关系;
  4. this是在运行时被绑定的;
<script>
    // 谁也说不清,现在的this是谁
    // 只有代码运行起来后,JS底层才会给this赋值
    // this最终值和你书写的位置没有关系
    // 和你如何调用fn函数是有关系,不同的调用方式,会给this赋不同的值
    function fn() {
        console.log(this);
    }
</script>
1
2
3
4
5
6
7
8
9

那么this到底是怎么样的绑定规则呢,绑定规则如下:

  • 绑定一:默认绑定;
  • 绑定二:隐式绑定;
  • 绑定三:显示绑定;
  • 绑定四:new绑定;

# 2,默认绑定

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

  • 代码如下:
<script>
    // --------------- 独立的函数调用
    function fn() {
        console.log(this);
    }
    // 独立的函数调用
    // 函数内部的this,在浏览器中表示window
    fn();
</script>
1
2
3
4
5
6
7
8
9
<script>
    // --------------- 独立的函数调用
    function fn() {
        console.log(this);
    }

    function gn() {
        console.log(this);
        fn() // 独立的函数调用
    }

    function kn() {
        console.log(this);
        gn(); // 独立的函数调用
    }
    kn(); // 独立的函数调用 
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<script>
    // --------------- 独立的函数调用
    var obj = {
        name: "wc",
        fn: function() {
            console.log(this);
        }
    }
    var gn = obj.fn;
    gn(); // 独立的函数调用 
</script>
1
2
3
4
5
6
7
8
9
10
11
<script>
    // --------------- 独立的函数调用
    function fn() {
        console.log(this);
    }
    var obj = {
        name: "wc",
        fn: fn
    }
    var gn = obj.fn;
    gn(); // 独立的函数调用 
</script>
1
2
3
4
5
6
7
8
9
10
11
12
<script>
    // --------------- 独立的函数调用
    function fn() {
        function gn() {
            console.log(this);
        }
        return gn;
    }
    var kn = fn();
    kn(); // 独立的函数调用 
</script>
1
2
3
4
5
6
7
8
9
10
11

# 3. 隐式绑定

另外一种比较常见的调用方式是通过某个对象进行调用的,也就是它的调用位置中,是通过某个对象发起的函数调用

  • 代码如下:
<script>
    // --------------- 隐式绑定
    function fn() {
        console.log(this);
    }

    var obj = {
        name: "wc",
        fn: fn
    }
    // fn中的this表示obj
    // fn中this是谁,看.前面是谁
    obj.fn(); // 隐式绑定 会把obj对象绑定到this上
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<meta charset="utf-8">
<script>
    // --------------- 隐式绑定
    var obj = {
        name: "wc",
        running: function() {
            console.log(this.name + "在跑步...");
        },
        coding: function() {
            console.log(this.name + "在打代码...");
        }
    }
    obj.running();
    obj.coding();
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<script>
    // --------------- 隐式绑定
    var obj = {
        name: "wc",
        fn: function() {
            console.log(this);
        }
    }
    var obj2 = {
        name: "xq",
        gn: obj.fn
    }
    // fn中this是谁,看.前面是谁
    obj2.gn(); // obj2
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 4. 显示绑定

隐式绑定有一个前提条件:

  • 必须在调用的对象内部有一个对函数的引用(比如一个属性);
  • 如果没有这样的引用,在进行调用时,会报找不到该函数的错误;
  • 正是通过这个引用,间接的将this绑定到了这个对象上;

如果我们不希望在 对象内部 包含这个函数的引用,同时又希望在这个对象上进行强制调用,该怎么做呢?

  • JavaScript所有的函数都可以使用call和apply方法(这个和Prototype有关)。
    • 它们两个的区别这里不再展开;
    • 其实非常简单,第一个参数是相同的,后面的参数,apply为数组,call为参数列表;
  • 这两个函数的第一个参数都要求是一个对象,这个对象的作用是什么呢?就是给this准备的。
  • 在调用这个函数时,会将this绑定到这个传入的对象上

因为上面的过程,我们明确的绑定了this指向的对象,所以称之为 显示绑定。

  • 代码如下:
<script>
    // fn是一个函数
    function fn() {
        console.log("fn...");
    }
    // 在JS中一切都是对象,函数也是对象
    // 可以给这个对象上添加属性
    fn.uname = "wc";
    fn.age = 18;
    fn.score = 100;
    console.log(fn.uname);
    console.log(fn.age);
    console.log(fn.score);
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
  • 通过call,apply,bind可以改变this指向。
  • 函数这个对象上,默认有几个方法,现在学习几个:call,apply,bind。
  • 代码如下:
<script>
    function fn() {
        console.log("fn...");
        console.log(this);
    }
    let obj = {
        name: "wc"
    }
    // fn本身也是对象
    // call的作用:1)显示绑定this  2)让函数执行
    // 通过call可以显示绑定this为obj
    fn.call(obj);
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
  • apply的作用和call是一模一样的,如下:
<script>
    function fn() {
        console.log("fn...");
        console.log(this);
    }
    let obj = {
        name: "wc"
    }
    // apply的作用:1)显示绑定this  2)让函数执行
    // 通过call可以显示绑定this为obj
    fn.apply(obj);
</script>
1
2
3
4
5
6
7
8
9
10
11
12
  • bind使用如下:
<script>
    function fn() {
        console.log("fn...");
        console.log(this);
    }
    let obj = {
        name: "wc"
    }
    //bind的作用:1)显示绑定this  2)不会让函数执行   3)返回一个绑定this之后新函数
    // 通过call可以显示绑定this为obj
    let newFn = fn.bind(obj);
    newFn();
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
  • 如果call,apply,bind后面跟的是基本数据类型,如下:
<script>
    function fn() {
        console.log("fn...");
        console.log(this);
    }
    fn.call("hello"); // 会把"hello"包装成一个新的对象
    fn.call(undefined); // 参数是und this指定window
    fn.call(null); // 参数是null this指定window
</script>
1
2
3
4
5
6
7
8
9

# 5. new绑定

** JavaScript中的函数可以当做一个类的构造函数来使用,也就是使用new关键字。使用new关键字来调用函数是,会执行如下的操作:

  1. 创建一个全新的对象;
  2. 这个新对象会被执行prototype连接;
  3. 这个新对象会绑定到函数调用的this上(this的绑定在这个步骤完成);
  4. 如果函数没有返回其他对象,表达式会返回这个新对象;
<script>
    // --------------- new运算符
    // 函数的几种角色:
    //    1)普通函数
    //    2)对象的方法
    //    3)普通的对象
    //    4)类  new一个类,就得到一个对象
    // 如果你要把函数当成一个类,建议把函数名首字母大写
    function Fn() {
        // new做了什么
        //   1)在类中创建一个空的对象
        //   2)把这个对象绑定到类中的this上面
        //   3)返回这个空对象 
        this.name = "wc";
        this.age = 18;
    }
    let obj = new Fn(); // new一个类,得到一个对象
    console.log(obj); // {name: 'wc', age: 18}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<script>
    // 类  构造器
    //   JS内置了一些构造器  String  Number  Boolean
    function Person(name, age) {
        this.name = name;
        this.age = age;
    }
    let p1 = new Person("wc", 18);
    console.log(p1);
    let p2 = new Person("xq", 20);
    console.log(p2);
</script>
1
2
3
4
5
6
7
8
9
10
11
12

# 6. 内置函数的绑定

有些时候,我们会调用一些JavaScript的内置函数,或者一些第三方库中的内置函数

  • 这些内置函数会要求我们传入另外一个函数;
  • 我们自己并不会显示的调用这些函数,而且JavaScript内部或者第三方库内部会帮助我们执行;
  • 这些函数中的this又是如何绑定的呢?
<script>
    // 一次性定时器
    // 3000表示3000ms  3s
    // 3s之后,会自动调用里面的函数
    // setTimeout(function(){
    //     console.log("hello 定时器~");
    // },3000)

    // 循环定时器
    // 每隔2s会执行里面的函数
    setInterval(function() {
        console.log("hello 定时器~");
    }, 2000)
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
  • 定时器中的this表示什么?如下:
<script>
    // 定时器中的this表示window。
    setTimeout(function() {
        console.log(this);
    }, 3000)

    setInterval(function() {
        console.log(this);
    }, 2000);
</script>
1
2
3
4
5
6
7
8
9
10
  • 监听器中的this,表示什么,如下:
<div id="box">click me</div>
<script>
    // 得到id为box的元素  dom元素是一个对象
    // 对象是引用数据类型
    let box = document.getElementById("box");
    // box表示事件源  事件发生的场所
    // click表示事件类型    JS中有非常多不同类型的事件
    // function(){}   表示当点击事件发生了做什么 监听器
    box.onclick = function() {
        console.log("hello DOM~");
        console.log(this); // 监听器中的this表示事件源
    }
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
  • 数组中有非常多的方法,这个方法中的this又表示什么,如下:
<script>
    let names = ["wc", "xq", "z3"];
    names.forEach(function(item) {
        console.log(item, this);
    }, {
        name: "ok"
    })
</script>
1
2
3
4
5
6
7
8

# 7. 规则优先级

默认规则的优先级最低

  • 毫无疑问,默认规则的优先级是最低的,因为存在其他规则时,就会通过其他规则的方式来绑定this
  • 显示绑定优先级高于隐式绑定
<script>
    // 隐式绑定
    let obj = {
        name: "wc",
        fn: function() {
            console.log(this);
        }
    }
    obj.fn();
</script>
1
2
3
4
5
6
7
8
9
10
<script>
    function fn() {
        console.log(this);
    }
    let obj = {
        name: "wc",
        // bind 作用:1)绑定this 2)返回一个绑定this指向后的新函数
        fn: fn.bind({
            name: "xq"
        }) // fn.bind({name:"xq"})是显示绑定
    }
    obj.fn(); // obj.fn() 隐式绑定    
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
  • new绑定优先级高于隐式绑定
<script>
    var obj = {
        name: "wc",
        fn: function() {
            console.log(this);
        }
    }
    // obj.fn(); // 隐式绑定  

    // 在JS中,只要是函数,都可以new
    let res = new obj.fn();
</script>
1
2
3
4
5
6
7
8
9
10
11
12

new绑定优先级高于bind

  • new绑定和call、apply是不允许同时使用的,所以不存在谁的优先级更高
  • new绑定可以和bind一起使用,new绑定优先级更高
<script>
    function fn() {
        console.log(this);
    }
    // bind返回改变this指向后的新函数
    var gn = fn.bind({
        name: "wc"
    });
    // gn();

    // this 是 {}  说明:new绑定高于显示绑定
    var kn = new gn();
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
<script>
    function fn() {
        console.log(this);
    }
    // new 不能和call和apply一起使用
    // call 可以让fn执行  gn不是函数
    var gn = fn.call({
        name: "wc"
    });
    console.log(gn);
    var kn = new gn();
</script>
1
2
3
4
5
6
7
8
9
10
11
12

# 8. 忽略显示绑定

我们讲到的规则已经足以应付平时的开发,但是总有一些语法,超出了我们的规则之外。

  • 如果在显示绑定中,我们传入一个null或者undefined,那么这个显示绑定会被忽略,使用默认规则
<script>
    function fn() {
        console.log(this);
    }

    fn.apply({
        name: "wc"
    })

    // 忽略显示绑定
    fn.apply(null) // window
    fn.apply(undefined) // window
    var gn = fn.bind(null);
    gn();
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 9. 间接函数引用

另外一种情况,创建一个函数的 间接引用,这种情况使用默认绑定规则

  • 赋值(obj2.foo = obj1.foo)的结果是foo函数;
  • foo函数被直接调用,那么是默认绑定;
<script>
    var obj = {
        name: "wc",
        fn: function() {
            console.log(this);
        }
    }

    var obj2 = {
        name: "xq"
    };

    // obj2.gn = obj.fn;
    // obj2.gn(); // 隐式绑定
    // IIFE    var a = 1;
    // IIFE this表示window
    ;
    (obj2.gn = obj.fn)();

    ;
    (function() {
        console.log(this);
    }())
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# 10. ES6箭头函数

<script>
    // var fn = function(num1,num2){
    //     return num1 + num2;
    // }

    // 像上面的函数可以简写
    var fn = (num1, num2) => {
        return num1 + num2;
    }
    console.log(fn(1, 2));
</script>
1
2
3
4
5
6
7
8
9
10
11
<script>
    // 如果形参只有一个,()可以不写
    var fn = num => {
        return num * 10;
    }
    console.log(fn(10));
</script>
1
2
3
4
5
6
7
<script>
    // 如果函数体中只有一行语句,{}可以不写
    var fn = num => console.log(num + 100);
    fn(100)
</script>
1
2
3
4
5
<script>
    // 如果函数体中只有一行代码,并且这行代码是return语句
    // {}可以不写  return也可以不写
    var fn = num => num * 10;
    console.log(fn(10));
</script>
1
2
3
4
5
6
<script>
    // 如果函数体中只有一行代码,并且是return语句
    // return了一个对象
    // var fn = function(){
    //     return { name:"wc" }
    // }

    // 把上面的代码转成箭头函数
    // 此时它会把对象的{} 当成函数的{}
    // 需要使用()把对象包起来
    var fn = () => ({
        name: "wc"
    })
    console.log(fn());
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 11. ES6箭头函数this

为什么在setTimeout的回调函数中可以直接使用this呢?

  • 因为箭头函数并不绑定this对象,那么this引用就会从上层作用于中找到对应的this。

记:箭头函数中的this,需要向上找一级。

<script>
    console.log(this); // 最外层的this是window

    var fn = () => {
        // 箭头函数中的this都需要向外找一级
        console.log(this);
    }
    fn(); // window

    var obj = {
        name: "wc"
    }
    fn.call(obj); // window
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<script>
    var fn = () => {
        console.log(this);
    }
    fn.call("hello")
    fn.call({})
    fn.call(null)
    fn.call(undefined)
</script>
1
2
3
4
5
6
7
8
9
<script>
    var obj = {
        name: "wc",
        fn: () => {
            console.log(this);
        }
    }
    obj.fn();
</script>
1
2
3
4
5
6
7
8
9
<script>
    // setTimeout(function(){
    //     console.log(this);
    // },300)

    setTimeout(() => {
        console.log(this);
    }, 300);
</script>
1
2
3
4
5
6
7
8
9

# 12. 练习题

<script>
    function fn() {
        console.log(this)
    }
    fn() // window
    window.fn() // window
</script>
1
2
3
4
5
6
7
<button onclick="fn">登录</button>
<script>
    let btn = document.getElementsByTagName("button")[0]
    btn.onclick = function() {
        // 监听器中的this表示事件源
        console.log(this)
    }
</script>
1
2
3
4
5
6
7
8
<script>
    let obj = {
        name: "wangcai",
        age: 100,
        run: function() {
            console.log(this)
            console.log("run....")
        }
    }
    obj.run()
</script>
1
2
3
4
5
6
7
8
9
10
11
<script>
    ;
    (function f() {
        console.log(this);
    }()); // IIFE中的this表示window
</script>
1
2
3
4
5
6
<button id="box">点击</button>
<script>
    let btn = document.getElementById("box");

    function f() {
        console.log(this);
    }
    // f()是值   und
    btn.onclick = f();
</script>
1
2
3
4
5
6
7
8
9
10
<button id="box">点击</button>
<script>
    let btn = document.getElementById("box");

    function f() {
        return function() {
            console.log(this)
        }
    }
    btn.onclick = f();
</script>
1
2
3
4
5
6
7
8
9
10
11
<script>
    function qq() {
        console.log(this);
    }
    window.qq();
    qq();
</script>
1
2
3
4
5
6
7
<script>
    let wc = {
        name: "wangcai",
        age: 100,
        eat: function() {
            console.log("eat...")
            console.log(this);
        }
    }
    wc.eat();
</script>
1
2
3
4
5
6
7
8
9
10
11
<script>
    let wc = {
        name: "wangcai",
        age: 100,
        eat: function() {
            console.log("eat...")
            console.log(this);
        }
    }
    let mm = wc.eat;
    mm(); // 独立函数调用
</script>
1
2
3
4
5
6
7
8
9
10
11
12
<script>
    var num = 10;
    var obj = {
        num: 20
    }
    obj.fn = (function(num) {
        this.num = num * 3;
        num++;
        console.log(num);
        return function(n) {
            this.num += n;
            num++;
            console.log(num)
        }
    })(obj.num);

    var fn = obj.fn;
    fn(5)
    console.log(window.num);
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<script>
    var num = 10;
    var obj = {
        num: 20
    }
    obj.fn = (function(num) {
        this.num = num * 3;
        num++;
        console.log(num);
        return function(n) {
            this.num += n;
            num++;
            console.log(num)
        }
    })(obj.num);

    obj.fn(10);
    console.log(num)
    console.log(window.num)
    console.log(obj.num)
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<script>
    (function() {
        var a = 1;
        var obj = {
            a: 10,
            f: function() {
                a *= 2;
            }
        }
        obj.f()
        alert(obj.a + a);
    })()
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
<script>
    (function() {
        var a = 1;
        var obj = {
            a: 10,
            f: function() {
                this.a *= 2;
                console.log(this.a)
            }
        }
        obj.f()
    })()
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
<script>
    var name = "window";
    var Wangcai = {
        name: "Wangcai",
        show: function() {
            console.log(this.name);
        },
        f: function() {
            var fun = this.show;
            fun(); // 独立函数调用
        }
    }
    Wangcai.f();
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<script>
    var fullname = "language";
    var obj = {
        fullname: "javascript",
        props: {
            getFullName: function() {
                // this是obj.props   name是und
                return this.name;
            }
        }
    }
    console.log(obj.props.getFullName());
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
<script>
    var fullname = "language";
    var obj = {
        fullname: "javascript",
        props: {
            fullname: "hello",
            getFullName: function() {
                return this.fullname;
            }
        }
    }
    console.log(obj.props.getFullName());
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
<script>
    var fullname = "language";
    var obj = {
        fullname: "javascript",
        props: {
            fullname: "hello",
            getFullName: function() {
                // window 
                return this.fullname;
            }
        }
    }
    let qq = obj.props.getFullName;
    console.log(qq()); // 独立函数调用
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<script>
    let obj = {
        fn: (function() {
            console.log(this); // window
            return function() {
                console.log(this); //  obj
            }
        })()
    }
    obj.fn();
</script>
1
2
3
4
5
6
7
8
9
10
11
<script>
    let obj = {
        fn: (function() {
            console.log(this)
            return function() {
                console.log(this)
            }
        })()
    }
    let qq = obj.fn;
    qq(); // 独立函数调用 
</script>
1
2
3
4
5
6
7
8
9
10
11
12
Last Updated: 12/25/2022, 10:02:14 PM