js—this指向问题

一、this的指向

  1. 默认绑定:指向window,独立调用也指向window

1.1. 默认指向window

     <script>
             console.log(this === window); //true
     </script>

1.2. 作为普通函数调用,this指向window

       //  函数的独立调用 this默认指向window
        function test() {
            console.log(this === window); //true
        }
        test();在这里插入代码片
  1. 隐式绑定:作为对象方法被调用时,谁调用就指向谁
        function foo() {
            console.log(this.a);
        }
        var obj1 = {
            a: 2,
            foo: foo
        }
        var obj2 = {
            a: 3,
            foo: foo
        }
            // 隐式丢失:
        var bar = obj1.foo; //foo函数并没有执行 他是先赋值再执行的,所以是独立调用的,指向window
        bar(); //window
        foo(); //window
        
        // 隐式绑定(对象调用)
        obj1.foo(); //2
        obj2.foo(); //3
        // 显示绑定
        obj1.foo.call(obj2); //3
        obj2.foo.call(obj1); //2

        // ------------显示绑定 > 隐式绑定
  1. 显示绑定:利用call、apply、bind更改this指向
        var a = 0;

        function foo(a, b, c, d, e) {
            console.log(a, b, c, d, e);
            console.log(this);
        }
        var obj = {
            a: 2,
            foo: foo
        }
        obj.foo(); //undefined undefined undefined undefined    指向obj
        var bar = obj.foo
        bar(); //undefined undefined undefined undefined     指向window
        obj.foo(1, 2, 3, 4, 5); //1 2 3 4 5                  指向obj
        bar.call(obj, 1, 2, 3, 4, 5); //1 2 3 4 5            指向obj
        bar.call(1, 2, 3, 4, 5); //2 3 4 5 undefined         指向Number  
        bar.call(false, 2, 3, 4, 5); //2 3 4 5 undefined     指向Boolean
        bar.call(undefined, 2, 3, 4, 5); //2 3 4 5 undefined 指向window
        bar.apply(null, [1, 2, 3, 4, 5]); //1 2 3 4 5        指向window
        bar.bind(obj)(1, 2, 3, 4, 5) //1 2 3 4 5             指向obj
  1. new绑定:当作为构造函数被调用时,new foo()指向实例之后的对象

new在构造函数中做了哪些事?
1、在内存中创建一个新的对象
2、让this指向这个新对象
3、在构造函数里面的代码,给这个新对象添加属性和方法
4、返回这个新对象,所以构造函数里面一般不需要return

构造函数里面的this
指向new创建的实例化对象(没有return的情况下);
如果构造函数内出现了return 并且是一个object对象,那么最终的运算结果返回这个对象;
只要构造函数不返回数据或者返回基本数据类型,this仍然指向实例。

        // //new 注重返回值!
        function Person() {
            var a11 = {};
            a11.a = 1;
            return a11;// {a:1}
                // return 1;//Person{}
                // return 的值为引用值
                // return {}; //{}

        }
        var person = new Person();
        console.log(person); 
        // new是用来实例化构造函数的
        // 构造函数与函数的区别 构造函数需要new
        // bar和foo是构造函数 baz是new出来的实例对象
        // new一个函数,生成一个实例化对象,这种函数叫构造函数
        function foo(b) {
            this.a = b;
            console.log(this);
        }

        var obj1 = {};
        var bar = foo.bind(obj1); //bind 改变this指向,使得this指向obj1  相当于obj1.a
        bar(2); //this -> {a:2}

        console.log(obj1.a); //2
        // baz就是实例对象 
        var baz = new bar(3); // new一个对象,使得this指向baz;此时是baz.a  this -> foo{a:3}
        console.log(obj1.a); //2

        console.log(baz.a); //3

        // ------------new绑定 > 显示绑定

二、 补充

  1. 箭头函数
    箭头函数内部并没有this指向,箭头函数的this指向是由外层函数的作用域来决定的
        var a = 0;

        function foo() {
            var that = this;
            console.log(this); //this指向obj {a:1 , foo:f}
            //箭头函数
            var test = () => {
                console.log(this); //this指向obj {a:1 , foo:f}
            }
            return test;
        }

        var obj1 = {
            a: 1,
            foo: foo
        }

        var obj2 = {
            a: 2,
            foo: foo
        }

        // obj1.foo() //返回test 是个函数 指向obj1
        // 箭头函数当中没有arguments对象
        obj1.foo()(); //默认绑定规则(独立调用)对箭头函数无效 //上面两个this都指向obj


        var bar = foo().call(obj2) //上面两个this都指向window 显示绑定 无效
  1. 定时器函数、立即执行函数等
        var a = 0;

        function foo() {
            console.log(this); //window
        }

        function bar(fn) {
            console.log(this); //window
            fn(obj); //window
            new fn() //foo{}
            fn.call(obj) //obj   {a: 2, foo: ƒ}
        }
        // api 接口中指明的
        // 回调函数; 父函数; 子函数;
        // 回调函数:当函数是参数时,那么我们认为当前的这个参数为回调函数
        var arr = [1, 2, 3]
        arr.forEach(function(item, idx, arr) {
            console.log(this); //window
            // 这个参数是可选的
        })

        arr.sort(function(a, b) {
            return a - b;
        })

        setInterval(function() {
            console.log(this); //window
        })
        var obj = {
                a: 2,
                foo: foo,
            }
            // 预编译的过程中实参被赋值为形参 ——> (值的拷贝过程,浅拷贝)
        bar(obj.foo)

        // 父函数是有能力决定子函数的this指向的

三、大厂面试题


        var name = 'window'

        var obj1 = {
            name: '1',
            fn1: function() {
                console.log(this.name)
            },
            fn2: () => console.log(this.name),
            fn3: function() {
                return function() {
                    console.log(this.name)
                }
            },
            fn4: function() {
                return () => {
                    console.log(this.name)
                }
            }
        }

        var obj2 = {
            name: '2'
        }

        obj1.fn1(); // 1 (隐式绑定)
        obj1.fn1.call(obj2); // 2 (显示绑定优先级大于隐式绑定)

        obj1.fn2(); // window (箭头函数不绑定作用域,上层作用域是全局)
        obj1.fn2.call(obj2); // window

        obj1.fn3()(); // window (独立函数调用)
        obj1.fn3.call(obj2)(); // window(独立函数调用)
        obj1.fn3().call(obj2); // 2 (最终调用返回函数式, 使用的是显示绑定)

        obj1.fn4()(); // 1  找父作用域为fn4的作用域是obj1 (箭头函数不绑定this, 上层作用域this是obj1)
        obj1.fn4.call(obj2)(); // 2(上层作用域被显示的绑定了一个obj1)
        obj1.fn4().call(obj2); // 1(上层找到obj1)
        function Foo() {
            getName = function() {
                console.log(1);
            }
            return this;
            console.log(this);
        }
        Foo.getName = function() {
            console.log(2);
        }
        Foo.prototype.getName = function() {
            console.log(3);
        }

        // 表达式
        var getName = function() {
            console.log(4);
        }

        // 函数声明 他会提升,预编译的时候会在前面,所以在执行时会替换成表达式的getName
        function getName() {
            console.log(5);
        }

        // 
        Foo.getName(); //2
        getName(); //4
        Foo().getName(); //1  (Foo执行完时候,getName全局变量,它进行重写)
        getName(); //1 因为foo执行完成之后,getName重写了

        new Foo.getName(); // 2    new的是Foo对象的一个属性
        new Foo().getName(); // 3  new Foo()实例之后的对象
        new new Foo().getName(); //3
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值