一道题充分理解变量提升、this 指向、运算符优先级

267 阅读3分钟

面试中经常碰到这种题目,主要考察面试者对变量定义提升、this 指针指向、运算符优先级、原型、继承、全局变量污染、对象属性及原型属性优先级等知识的理解

  // q1
  function Foo() {
    getName = function() {
      alert(1);
    };
    return this;
  }
  // q2
  Foo.getName = function() {
    alert(2);
  };
  // q3
  Foo.prototype.getName = function() {
    alert(3);
  };
  // q4
  var getName = function() {
    alert(4);
  };
  // q5
  function getName() {
    alert(5);
  }

  // 请写出输出结果
  Foo.getName(); 
  getName(); 
  Foo().getName(); 
  getName(); 
  new Foo.getName(); 
  new Foo().getName(); 
  new new Foo().getName(); 

分析代码: q1: 定义一个叫 Foo 函数 q2: 为 Foo 创建一个叫 getName 静态属性,存储一个匿名函数 q3: 为 Foo 原型对象上创建一个叫 getName 的匿名函数 q4: 函数表达式创建一个 getName 函数 q5: 声明一个 getName 函数

第一题Foo.getName()

直接访问 Foo 上的静态属性,直接调用 q2,输出 2

第二题getName();

考察函数提升,函数生明在运行时会提升到作用域最前面,函数表达式则是在运行时赋值 所以在运行时 q5 函数会提升,而运行到函数表达式 q4 时,会给生明提前的 q5 的 getName 赋值为 q4,输出 4

第三题Foo().getName();

考察变量作用域和 this 指向问题 先执行了 Foo 函数,然后调用 Foo 函数的返回值对象的 getName 属性函数。 Foo 函数的第一句 getName = function () { alert (1); };是一句函数赋值语句,注意它没有 var 声明,所以先向当前 Foo 函数作用域内寻找 getName 变量,没有。再向当前函数作用域上层,即外层作用域内寻找是否含有 getName 变量,找到了,也就是第二问中的 alert(4)函数,将此变量的值赋值为 function(){alert(1)}。 this的指向在函数定义的时候是确定不了的,只有函数执行的时候才能确定 this 到底指向谁,实际上 this 的最终指向的是那个调用它的对象,可以理解为window.Foo().getName();->window.getName(),所以最终会输出 1

第四题getName();

直接调用 getName 函数,相当于 window.getName(),因为这个变量已经被 Foo 函数执行时修改了,遂结果与第三问相同,为 1,也就是说 Foo 执行后把全局的 getName 函数给重写了一次,所以结果就是 Foo()执行重写的那个 getName 函数

第五题new Foo.getName();

考察的是 JS 的运算符优先级问题,点的优先级比 new 无参数列表优先级高,当点运算完后又因为有个括号(),此时就是变成 new 有参数列表,所以直接执行 new getName 函数作为了构造函数来执行,输出 2 Foo()执行重写的那个 getName 函数

第六题new Foo().getName();

首先 new 有参数列表跟点的优先级是同级,同级的话按照从左向右的执行顺序,所以先执行 new 有参数列表再执行点的优先级,最后再函数调用,相当于(new Foo()).getName() 由于new Foo()返回的是 this,而 this 在构造函数中本来就代表当前实例化对象,最终 Foo 函数返回实例化对象。之后调用实例化对象的 getName 函数,因为在 Foo 构造函数中没有为实例化对象添加任何属性,当前对象的原型对象(prototype)中寻找 getName 函数。输出 3

第七题new new Foo().getName();

同样是运算符优先级问题,这是已经要崩溃了,new 有参数列表遇到 new 有参数列表,从左向右执行,相当于 new ((new Foo()).getName)(),先初始化 Foo 的实例化对象,然后将其原型上的 getName 函数作为构造函数再次 new,最终输出 3

参考:github.com/Wscats/arti…