致敬各位10年阿里的前端开发

很巧合,我在认识了两位同是10年工作经验的阿里前端开发小伙伴,不但要向前辈学习,我有时候还会选择另一种方法逗逗他们,拿了网上一道经典面试题,可能我连去阿里面试的机会都没有,但是我感受到了一次面试10年阿里的前端开发,让我感觉一个字,比吃老坛酸菜还爽!虽然有答对也有答错,我们都应该对阿里的前端开发老前辈们来一次致敬,无论是在开源社区提供的技术,还是对一些书籍进行翻译,出版,给我们这些后生对技术提供了很大的帮助,给中国在整个世界it发展上也做出了卓越的贡献

虽然我暂时没有能力提供那么强大的技术栈,我从掘金开始写下第一篇从史上最祥细的vue2.0从基础到组件(基础1)的时候,只要有空就进行技术分享,无论对大家是有用还是没用,但是有些留言的小伙伴说像追剧一样,一直跟到现在,这也是这些同学给我的动力和支持,小编也默默留言让我参加技术征文,无论结果如何,就算失败也会不会后悔!

幻想写作

什么是幻想写作呢,借着这个主题,我就当两个阿里同学是一些小菜鸟,让我这为大神为他们讲讲课,两位小菜鸟听好请回答以下面这道面试题,不会的我可以给你们讲解,这样吧,我就当他们什么都不会。哈哈~!!!真接讲以下是网上的金典面试题

我个人认为整个体系对基础知识一个总结,但是我个人认为,整体是围绕着一个函数来开展的

1.函数对象和构造函数

function Demo(){
    this.a = 1
}
console.log(Demo)
console.log(new Demo)

console.log(Demo)

此时返回的就是一个包括代码段的函数体

console.log(new Demo)

此时返回的就是一个函数实例化后的对象,里面此时a就变成了属性,同样有proto属性

2.函数对象静态属性方法和构造函数实例的对象属性方法

function Demo(){
    this.a = 1
}
Demo.a = 2
console.log(Demo.a)  //2
console.log(new Demo().a)//1

console.log(Demo.a)

此时Demo就是一个函数对象,对象我们就可以挂方法和属性

console.log(new Demo().a)

此时Demo当作的是一个构造函数, new Demo()返回的是实例对象,函数体内的this.a指向的就是实例方的a属性

请看题

function Foo() {
    getName = function () { alert (1); };
    return this;
}
Foo.getName = function () { alert (2);};
Foo.prototype.getName = function () { alert (3);};
var getName = function () { alert (4);};
function getName() { alert (5);}

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

闭上你的眼睛好好考虑考虑一下这道题,可能有些同学已经看过了,也有着很详细的解释,那我就借花献佛再说一次,对我只答出五道。那你呢,先不要控制台打印出来,可能百分之50%的前端工程师,在第二道就被卡住了

看这道题我们先分析一下,整个题目结构是怎么样的

  • 首先声名了一个构造函数Foo
  • Foo虽然是函数,但也是一个对象,在对象上挂了一个静态getName方法
  • 在Foo构造函数的原型上也挂了一个getName方法
  • 写了个getName的函数表达式
  • 声明了一个getName函数

第一题 Foo.getName(); 答案 2

从此题我要引入的话题就是一切皆对象,但是我们抛开值类型那就不是对象了
那函数是不是对象?

回答:

是对象!

let a = function(){}
let b = {}
typeof(a) ==>Function
typeof(b)==>Object

这明显的看出来我们用typeof去验证的话,很明显一个Function类型,一个是Object类型,那两都到底都是不是对象?

回答:

是对象!

let a = function(){}
let b = {}
a instanceof Object
b instanceof Object
//都输出为true

我们再进证明真身,我们用instanceof去判断,如果判断一个变量到底是不是对象,那Instanceof Object,返回true,那我们就可以肯定是对象,既然是对象,我可以把function类形和object类形归并在一起麻?

回答

不可以!
我个人感觉吧对象与函数之间是一个很复杂的关系,是先有函数还是很有对象?函数又是一个对象,但是对象又是构造函数New 出来的

function A (){}
var a = new A()
typeof(a) //object

那var obj = {}
这obj是那里来的?这里只是一个语法堂
你可以这么理解万物对象都是构造函数New出来的

console.log(typeof (Object));  // function
console.log(typeof (Array));  // function

以上做了一系列的对象和函数的讲解

言归正转那既然函数也是一个对象,我们用Instanceof去证明了,那对象上面肯定可以挂属性和方法,就像jquery一样

$.trim()

trim就是挂jquery函数上的静态方法
Foo.getName = function () { alert (2);};也就是挂在Foo函数上的静态方法
所以结果是2

第二题 getName(); 答案是4

这题主要考验的是一个变量提升的概念

var getName = function () { alert (4);};
function getName() { alert (5);}

在我们正常思考的前题下,Js引擎从下执行到下的话应该第二个函数声明会覆盖掉函数表达试

如果你对变量提升不了解的话,这题肯定会回答错。

在函数的写法里,函数声明是一等公民

fun()   //=>变量提升了
function fun(){
   console.log(“变量提升了”)
}

有些人会问很奇怪,按照js引擎执行的顺序fun()没有定义之前,执行fun()应该会报错啊,js内置引擎里其实在执行之前有一个扫描器,先把代码扫一遍,有函数声明的,和全局变量,都会先提到最前面,所以在执行fun()时,已经执行了fun函数

既然全局变量提升 函数声明也提升为什么函数声会还会覆盖函数表达式

var getName = function () { alert (4);};
function getName() { alert (5);}

我们手动改造一个js引擎扫描后的结果

var getName
function getName() { alert (5);}
getName = function () { alert (4);};

此时就很明显的发现,对于函数表达式提升的只是函数表达式的函数名,而作为声明式函数是整个函数体向上的进行了提升,执行到后面函数表达式还是会覆盖函数声明

所以答案是4

第三题

在做第三题之前这个Foo函数现在有三个身份了

  • window下的方法
  • 构造函数
  • 同样先前说的是一个对象

在浏览器里window对象承载着万物,我们所声明的全局函数,就是window下的方法,全局变量,那就是window下的属性

那FOO函数体内的this指向那里

function Foo(){
   return this
}
Foo() 返回的是window对象

因为window对象上挂着foo函数,foo函数体内this同样也着向着window对象,而foo函数执行后把window对象返回给了foo的执行结果作为返回值

这题是这么执行的Foo().getName();

我们先不执行,我们接着第二个问题的结果继续衍生,第二个getName已经被函数表达式给覆盖了

我们拆分模拟Foo() 先执行

Foo函数调用的时候有会执行两种环境

  • 执行函数体的代码段
  • 返回一个值,如果这个值没有定义,则返回undefined

我们看执行的代码段

此时getName是一个全局变量,FOO函数代码段里的getName里没有通过任何声明,此时指向改变的是全局变量getName

我们再看看返回值

如果想调到全局方法getname,只有谁有调用的到?前面讲过只有window对象,因为getName是挂在window对象上的一个方法,恰巧,此时的返回值就是返回一个window对象前面this的指向我也讲过

所以答案是1

第四题

getName();

如果你前面几全回答出来了,如果这题没有答出来,那你肯定对执行环境的结果没有注重,这题题一般肯定会有三种答案 1,4,5

如第你第二题没有答对的话就题就回答5
如果你第三题没答案对肯定回答4

经过第三题的执行过后,此的全局的getName函数已经被alert(1)给覆盖,我们此时再次执行全局的getName函数的时候,此时答案也就是1

所以此时答案就是1

第五题

new Foo.getName();

对于第五题以后大部分考优先级
(.)的优先级高于new操作,遂相当于是:

new (Foo.getName)();

(Foo.getName)是FOO函数的静态方法,再进行简化则是 new getName()

此时上面new出来的结果一是执行alert(2)再返回一个对象

所以结果是2

第六题

new Foo().getName();

new Foo().getName() ,首先看运算符优先级括号高于new,实际执行为

(new Foo()).getName()

此时的FOO函数已经不是一般的对象了,现在是看作一个构造函数,一般来说构造函数我们不应该有返回值,因为会new出一个实例化对象,

那如要一定会有返回值会如何?

回答:

前面说过了,当FOO作为一个对象执行后返回this指向的是window对象
如果Foo当作是一个构造函数那执行后this指向的是new出来看实例,

如果我们没有返回值

  function Foo(){}
  console.log(new Foo) //此时返回的是实例

如果我们有返回值,只是值类型会如何

  function Foo(){
      return 2
  }
  console.log(new Foo) //此时返回的是实例化后的对象

如果有返回值,并且是引用类形会如何?

  function Foo(){
      return [1,2,3,4]
  }
  console.log(new Foo) //此时返回的是引用类形数组对象

如果是this呢?

  function Foo(){
      return this
  }
  console.log(new Foo) //此时返回的是实例化后的对象

此时的this 不同于Foo函数执行后return this作为一个对象来看代指向于window对象,
但是作为构造函数的话,返回的就是实例化后的对象,你可以查到proto属性

此时你会发现在Foo构造函数实例方法里你会发现没有实例getName方法,那怎么办?

通过原型链向上找

new Foo().getName === Foo.prototype.getName  //true

所以答案是3

说难不难,说简单不简单,此题让人感觉第一眼就是很迷茫~~
如果你那天面试到这种题请你静下心来面对这些题,考的就是基础!!
如果有机会我想把这一套原型体系给大家彻底来几个教程进行全面分析,欢迎订阅我的掘金专栏

原文地址:https://www.cnblogs.com/libin-1/p/6765225.html