前端面试题之JavaScript【this指向】

JavaScript this指向

  • 全局环境下this 始终指向全局对象(window), 无论是否严格模式

    console.log(this.document === document); // true
    
    // 在浏览器中,全局对象为 window 对象:
    console.log(this === window); // true
    
    this.a = 37;
    console.log(window.a); // 37
    
  • 普通函数调用非严格模式下:指向全局对象 :window; 严格模式:this是undefined

    	function f1(){
      		return this;  // 非严格模式:普通函数的this指向window
    	}
    	f1() === window; // true
    
    	function f1(){
    		"use strict"; // 这里是严格模式
      		return this;  // 严格模式:普通函数的this是undefined
    	}
    	f1() === window; // false
    
    	// let 声明的变量
    	let username = 'username'
    	function fn(){
    	    alert(this.username); // undefined   let声明的变量不在window上
    	}
    	fn();
    
    	// var 声明的变量
    	var username = 'username'
    	function fn(){
    	   alert(this.username);  // username 
    	}
    	fu();
    
    	// 直接挂载在 window 上的变量
    	window.username = 'username'
    	function fn(){
    	    alert(this.username); // username
    	}
    	fn();
    	//可以理解为 window.fn();
    
  • 对象函数调用哪个对象函数调用,this就指向哪里

    window.b=2222
    let obj={
        a:111,
        fn:function(){
            alert(this.a);//111
            alert(this.b);//undefined
        }
    }
    obj.fn();  
    // 此时的 this 指向对象 obj; this.a 相当于 obj.a, this.b 相当于 obj.b ; 
    // this.a = 'username', obj没有 b 属性,所以 this.b = undifined
    
  • 构造函数调用this指向实例对象

    // 例1:
     function Person(age, name) {
         this.age = age;
         this.name = name
         console.log(this)  // 此处 this 分别指向 Person 的实例对象 p1 p2
         console.log(this.age)  
         console.log(this.name)
     }
    var p1 = new Person(18, 'zs')  // 输出:18, zs
    var p2 = new Person(18, 'ww')  // 输出:18, ww
    
    // 例2:
    let TestClass = function() {
        this.name = '111';
    }
    let subClass = new TestClass();
    subClass.name = 'cn';
    console.log( subClass.name ); // cn
    let subClass_1 = new TestClass();
    console.log( subClass_1.name ) // 111
    
    

    new() 方法做了什么

    (1) 创建一个新对象
    (2) 将构造函数的作用域赋给新对象(因此 this 就指向了这个新对象) ;
    (3) 执行构造函数中的代码(为这个新对象添加属性) ;
    (4) 返回新对象。
    new 操作符
    在有上面的基础概念的介绍之后,在加上new操作符,我们就能完成传统面向对象的class + new的方式创建对象,在JavaScript中,我们将这类方式成为Pseudoclassical。
    基于上面的例子,我们执行如下代码

    	var obj = new Base();
    

    我们在Javascript引擎中看到的对象模型:
    前端面试题之JavaScript【this指向】_第1张图片

    new操作符具体干了什么呢? 其实很简单,就干了三件事情。

    	var obj  = {};   // 创建一个新对象
    	obj.__proto__ = Base.prototype;  // 新对象的 __proto__(隐式原型)指向Base函数对象的prototype(显式原型)原型对象
    	Base.call(obj); // 将Base的this指针利用 call()方法替换成obj,然后调用Base函数,给obj对象赋值了一个id成员变量,这个成员变量的值是”base”
    

    如果我们给Base.prototype的对象添加一些函数会有什么效果呢?
    例如代码如下:

    Base.prototype.toString = function() {
      return this.id;
    }
    

    那么当我们使用new创建一个新对象的时候,根据__proto__的特性,toString这个方法也可以做新对象的方法被访问到。于是我们看到了:
    构造函数中,我们来设置‘类’的成员变量(例如:例子中的id),构造子对象prototype中我们来设置‘类’的公共方法。于是通过函数对象和Javascript特有的__proto__与prototype成员及new操作符,模拟出类和类实例化的效果。

  • 定时器函数调用this 指向 window

     setInterval(function () {
       console.log(this); // window
     }, 1000);
    
  • 事件绑定调用this 指向 绑定事件的对象

    	<body>
        <button id="btn">hh</button>
    	<script>
    	    var oBtn = document.getElementById("btn");
    	    oBtn.onclick = function() {
    	        console.log(this); // btn
    	    }
    	</script>
    </body>
    
  • apply和call调用【修改this指向】

     var Person = {
        name:"lixue",
        age:21
    }
    function fn(x,y){
        console.log(x+","+y);  // hh20
        console.log(this);  // this 指向 Person 
        console.log(this.name);  // lixue
        console.log(this.age);  // 21
    }
    fn.call(Person,"hh",20);
    fn.apply(Person,["hh",20]);
    
  • 箭头函数调用自身没有this,箭头函数的this是父级所在的执行上下文中的this

    注意:简单对象(非函数)是没有执行上下文的!所以在定义对象的时候,定义对象属性,里面的this指向的一般是全局,或者这个对象所在的那个环境中的this。
    实际原因: 箭头函数根本没有自己的this,导致内部的this就是外层代码块的this。

    // 例1:
    function Person() {  
        this.age = 0;  
        setInterval(() => {
            this.age++;    // this的指向是 p 实例对象,执行上下文是Person()
        }, 3000);
    }
    
    var p = new Person();   // 输出 0 1 2 ..... 间隔三秒
    
    // 例2:
    var b = 'BBBB';
    var obj = {
        b : 'bbbb'
        say:()=>{
        	console.log(this.b)  // this 的指向是上级对象obj所在的执行上下文:window,所以输出的是:BBBB
        }
    }
    obj.say()   // 输出 BBBB
    
  • call、bind、apply区别

    • call:第一个参数是要绑定给this的值,后面传入的是一个参数列表 list 。当第一个参数为null、undefined的时候,默认指向window。
      特点:从第二个参数开始是一个参数列表 list

      var arr = [1, 2, 3, 89, 46]
      var max = Math.max.call(null, arr[0], arr[1], arr[2], arr[3], arr[4]) // 输出 89;因为call方法的第一个参数为null,则 this 指向 window,从window中找 arr 变量
      
    • apply:第一个参数是要绑定给this的值,第二个参数是一个参数数组。当第一个参数为null、undefined的时候,默认指向window。
      特点:从第二个参数是一个参数数组

      var arr = [1, 2, 3, 89, 46]
      var max = Math.max.call(null, arr) // 输出 89;因为call方法的第一个参数为null,则 this 指向 window,从window中找 arr 变量
      

      示例:

      var obj = {
          message: 'My name is: '
      }
      
      function getName(firstName, lastName) {	
      	 // 原本的this指向window,但是call/apply改变后,此时的this指向 obj,所以 this.message 相当于 obj.message = My name is: 
          console.log(this.message + firstName + ' ' + lastName)   
      }
      
      getName.apply(obj, ['Dot', 'Dolby']) // My name is: Dot Dolby
      getName.apply(obj, 'Dot', 'Dolby') // My name is: Dot Dolby
      
      var Person1  = function () {
          this.name = 'Dot';
      }
      var Person2 = function () {
          this.getname = function () {
              console.log(this.name);
          }
          Person1.call(this);   // 改变this指向,使得Person2的this指向 Person1;相当于Person2 拥有了Person1 的所有属性,所以this.name = 'Dot'
      }
      var person = new Person2();
      person.getname();       // Dot
      
    • bind:第一个参数是this的指向,第二个参数开始是接收的参数列表。
      特点:bind 方法不会立即执行,而是返回一个改变了上下文 this 后的函数以及bind接收的参数列表的使用。。

      返会的函数

      	var obj = {
          name: 'Dot'
      }
      
      function printName() {
          console.log(this.name)   // 函数本身的this指向没有被改变,依然指向 window
      }
      var dot = printName.bind(obj)   // this指向 obj
      console.log(dot)  // function () { … }
      dot()  // Dot
      printName();  // undefiined
      
      

      参数的使用

      function fn(a, b, c) {
          console.log(a, b, c);
      }
      var fn1 = fn.bind(null, 'Dot');   //  this 指向 window
      
      fn('A', 'B', 'C');            // A B C
      fn1('A', 'B', 'C');           // Dot A B
      fn1('B', 'C');                // Dot B C
      fn.call(null, 'Dot');      // Dot undefined undefined
      

你可能感兴趣的:(前端面试题,javascript,es6)