前端中的 this 指向

70 阅读6分钟

js变量提升基础

知识点1if (false) {
        var a = 1;
    }
    
    console.log(a); // undefiend
    
    // 其实 var a 是执行了的, 只是if (false) a = 1; 这个赋值操作没执行。

知识点2function test() {
        console.log(1);
    }
    
    var test;
    console.log(test);  // function test() {...}
    
    // 是不是惊呆啦! 为什么 var test; 却没有覆盖掉函数,
    // 只var不赋值不是为 undefined么! 
    // 其实直接 var test 和 var undefined不太一样
    // 前者为无意义的声明,如果该变量有值, js 是不会处理这样的声明的。
    // 举个例子
    
    console.log(test);  // 函数体
    var test = 1;
        
    function test() {
        console.log(1);
    }
    
    
    // 因为函数提升优先级最高, 所以提升后的代码为
    function test() {
        console.log(1);
    }
    
    var test;
    
    console.log(test);
    
    test = 1; // 很显然答案是函数体啦

变量提升以及函数声明提升的优先级

    alert(a);
    a();
    var a = 3;
    
    function a() {
    	alert(10);
    }
    
    alert(a);
    a = 6;
    a();
答案
  1. 弹出函数体 function a() { alert(10); }
  2. 10
  3. 3
  4. a is not a function

注解: ==函数声明提升的时候连着函数体一起提升, 且优先级大于变量声明,也就是a函数声明会提到最前面, 具体的指向步骤是这样的
function a() { alert(10); }
var a;
a = 3;
alert(a);==


函数声明的作用域问题

   (function test () {
      console.log('i am test'); 
   })();
   
   console.log(test);
答案

test is no defiend
注解: 函数声明只能在当前作用域生效。特别的一点是es6花括号作用域, 只能使let, const失效。比如:

    {
        let a = 1
        function A () {
            console.log('AAA');
        } 
    }
    console.log(a); // undefined
    A(); // AAA

函数名与变量引用的冲突

   const fn = function test () {
      console.log('i am test'); 
   }
   
   console.log(test);
答案

test is no defiend
注解: 把函数声明到一个变量上时, 对函数命名是没有作用的;


变量表达式声明的函数的函数名在函数内部不能被重写

   (function test2 () {
     test2 = 1;
     console.log(test2)
   })();
   
   test2();
   
   const fn = function test () {
      test = 1;
      console.log(test);
   }
   
   fn();
答案
  1. function test () { test = 1; console.log(test); }
  2. test2 is not defined
  3. function test () { test = 1; console.log(test); }

    注解: ==函数表达式定义的时候==, ==函数的名字==外部不能访问,内部也不能修改。 什么是函数表达式, 以上两种都是, 和函数声明的区别就是函数表达式不以function开头定义的。 对比下面的写法:
    // 函数声明
    function zhijia() {
    	zhijia = "老师";
    	console.log(zhijia)
    }
    zhijia(); // 老师

来点花样

   (function test (w) {
	
    })(window);
    
    console.log(test);
       
    (function test2 (w) {
        this.a = 2;
	    w.b = 1;
    })(window);
    
    console.log(a);
    console.log(b);
答案
  1. test2 is not defined
  2. 2
  3. 1

    注解: window传进自执行函数 但是test2这个函数并没有绑定在window上。自执行函数的this本身就指向window。

自执行函数的this指向

   obj = {
    	fn: (function() {
    		console.log(this)
    	})()
    }
    
    console.log(obj.fn)
答案
  1. windows对象
  2. undefined
    注解: 非严格模式下, 自执行函数的this永远指向window

自执行函数的this指向

   obj = {
    	fn: (function() {
    	    // "use strict";
    		console.log(this);
    	})()
    }
    
    console.log(obj.fn)
答案
  1. windows对象
  2. undefined
    注解: 非严格模式下, 自执行函数的this永远指向window。打开注释试一下~

[你不知道的js] 书中一处错误记录

   function foo() {
	   var a = 2;
	   
	   this.bar();
   }
    
    function bar() {
    	console.log(this.a);
    }
    
    foo();
答案
  1. undefined

注解: 是不是很诧异为什么输出的是undefinede而不是a is not defined; 因为此时输出的是this.a, this -> window, 也就是访问的window.a。对象下面的属性读不到会输出undefined, 尝试下改成console.log(a)~


this特点 谁调用指向谁 没人调用指向window

   this.a = 20;
   var test = {
       a: 40,
       init: () => {
           console.log(this.a);
           
           function go() {
               // this.a = 60;
               
               console.log(this.a);
           }
           
           go.prototype.a = 50;
           return go;
       }
   }
   
   // var p = test.init();
   // p();
   
   new (test.init())();
   
   // 请写出答案 放出注释后 再写出下一版答案
   
答案
  1. 20
  2. 50

放开注释后 答案为

  1. 20
  2. 60

注解: 非严格模式下,箭头函数的this永远指向他调用地方的上下文this, (es6对象方法声明函数和箭头函数不能被new调用), test.init中的go方法, 因为没有调用者, this指向window。


补充-> 箭头函数this指向

    const obj = {
        a: 2
    }
    
    const fn = function () {
    	const k = () => {
    		console.log(this);
    	}
    
    	k();
    	
    	setTimeout(() => {
    	    console.log(this);
    	}, 1000)
    }
    
    fn.bind(obj)();
    // fn.apply(obj);
    // fn.call(obj);
答案
  1. {a: 2}
  2. {a: 2}

注解: 非严格模式下,箭头函数的this永远指向他调用地方的上下文this


new遇到es6函数!!!

    var o = {
        foo: function () {
            console.log('jc');
        },
        bar() {
            console.log('yd');
        }
    }
    
    var f = o.foo.bind({});
    new f();
    var b = o.bar.bind({});
    new b();
答案
  1. jc
  2. b is not a constructor

注解: es6对象方法声明方法和箭头函数不能被new调用


bind 能改变箭头函数的this指向么

   var obj1 = {
        a: 2
    }
    
    var fn1 = function () {
    	var k = () => {
    		console.log(this);
    	}
    
    	k.bind({ b: 1 })();
    	// k.bind(null)(); 
    	
    }
    
    fn1.bind(obj)();
答案
  1. { a: 2 }

注解: 箭头函数没有this。


bind遇到new

    this.a = 20;
    
    function test() {
        console.log(this.a);
    }
    
    var obj = {
        a: 30
    }
    
    var result = test.bind(obj);
    new result();
答案
  1. undefined

注解: new使bind失效。 new 绑定 > 显式绑定(bind, apply, call) > 隐式绑定(函数更改this指向) > 默认绑定


开胃菜

    var num = 1;
    
    function fn1() {
        "use strict";
        
        console.log(this.num++);
    }
    
    function fn2() {
        console.log(++this.num);
    }
    
    (function() {
        "use strict";
        console.log(this);
        fn2();
    })();
    
    fn();
答案
  1. undefined
  2. 2
  3. undefined

注解: 自执行函数的this非严格模式下永远指向window, 严格模式下为undefined, 而没有东西的调用fn2, 因为它没有调用者, fn2内部this指向window, 所以第一题是2, 第二题严格模式下没有window, 所以为undefined; 注意严格模式的作用范围, 只是对当前作用域有效。自执行函数的 "use strict"并不会作用到fn2函数内部。

    function fn() {
    	console.log(this);
    	(function() {
    		console.log(this);
    	})();
    }
    
    fn.bind({ a: 3 }); 
    
    // { a: 3 }
    // window

原型链权重问题

    function c1 (name) {
        if (name) this.name = name;
    }
    
    function c2 (name) {
        this.name = name;
    }
    
    function c3 (name) {
        this.name = name || 'fe';
    }
    
    c1.prototype.name = 'shunshun';
    c2.prototype.name = 'ys';
    c3.prototype.name = 'nice';
    
    console.log(new c1().name); 
    console.log(new c2().name); 
    console.log(new c3().name); 
答案
  1. 'shunshun'
  2. undefined
  3. 'fe'

注解: 最有意思的是第二个打印的结果, 注意传入的是undefined。


杂耍

    1. {} + [];
    
    2. [] + {}; 
    
    3. 1 + [];
    
    4. 1 + {};
    
    5. {} + [1, 2, 3];
    
    6. [1, 2, 3] + {};
    
    7. [] == 0;
    
    8. [] === 0;
    
    9. [[['a']]] == 'a';
    
    10. [[['a']]] === 'a';
    
    11. [[['a']]] === "a";
答案
  1. 0
  2. "[object Object]"
  3. "1"
  4. "1[object Object]"
  5. "NAN"
  6. "1,2,3[object Object]"
  7. true
  8. false
  9. true
  10. false
  11. true

注解:

  1. { } 是空代码块, 什么都没干, 数组隐式调用自身的toString方法, 所以打印1相当于 + ( [].toString() ), 相当于+ "", + 使"" 转化成整数0, 故为0;
  2. 如上, 这句相当于一个[].toString() + {}.toString(); 相当于"" + "[object Object]"; 故答案为 [object Object]; 这里需要注意的是中括号里的第一个object没有任何意义。仅仅是js为了区分其他类型给的一个标识(typeof显示的)。
  3. 需要注意的是, +"1" = 1; +"" =0; 但是+"1,2" = NAN;

  4. ...

杂耍

    function test(m) {
        m = { v: 5 }
    }
    
    var m = { k: 30 };
    
    test(m);
    
    alert(m.v);
答案
  1. undefined

注解: 把test形参m改成n就明白了; 必须滴~ 不解释。


函数声明提升的范围

    function test() {
        console.log(1);
    }
    (function() {
        if (false) {
            function test() {
                console.log(2);
            }
        }
        console.log(test); // 提升了 值为undefined
        test();
    })();
答案
  1. test is not a function

注解: 自执行函数在自己内部相当于声明了test, 但是因为逻辑(if (false)), 导致函数体提升不出来。而在自执行函数里面访问test, 因为找到一个没有赋值的声明, 所以不往上去查找window里的test, 如果改成如下就可以了。

    function test() {
        console.log(1);
    }
    
    (function() {
        if (false) {
            function test() {
                console.log(2);
            }
        }
        
        window.test(); // 1
    })();
    
    // 或者
     function test() {
        console.log(1);
    }
    
    (function() {
        // if (false) {
        //    function test() {
        //        console.log(2);
        //    }
        //  }
        console.log(test); // 内部找不到 往父层找 func test
        test(); // 1
    })();