zoukankan      html  css  js  c++  java
  • 怎样才算精通JavaScript

        我没有参加过一线IT公司的笔试,但是我听说大公司都喜欢笔试,于是从网上搜了一些面试方面的题来试手。无论是笔试还是被笔试,有备无惧嘛。我个人倾向于javascript编程方向的开发,所以面试题自然也是选择这方面相关的。网上各类面试题的质量参差不齐,其中有一篇叫《如何面试前端工程师》的博文里边给的题型是我认为最好的。题量很少,但是考察的方向都基本上覆盖了。作者说他是个行业派(言下之意他也是写代码的),注重前端编程的能力。下面我就拿它的题来分析一下:

    第一部分:Object Prototypes (对象原型)

    题目是:定义一个方法,传入一个string类型的参数,然后将string的每个字符间加个空格返回,例如:

    spacify('hello world') // => 'h e l l o  w o r l d'    

    这题我认为是比较简单的,但是考虑到好的考题都是容易的在前,稍难的在中间,最难的在后面。这样布局是很合乎常理的。让面试者有一个热身过程,做到良好发挥。

    因为做为面试官,不是要把面试者都难倒,证明你个人有多牛X,而是要选出合适的人才,为企业创造价值。好像说的有点远了,继续分析试题。

    我一下子就想出好几个方法,但是保险起见,我用了这个:

    function spacify(str) {
          return str.split('').join(' ');
        }

    接下来,难度加大,如何把这个方法放入String对象上面,例如:

    'hello world'.spacify();

    显然这个问题是要考察侯选人是否对function prototypes(方法原型)有一个基本的理解。我把上面的方法简单改了一下就出来了:

    String.prototype.spacify = function(){
        //能否理解这里的this 是本题的关键
          return this.split('').join(' ');
        };

    我认为这题即考察了原型对象,又考察了this的经典用法,如果不是有多年的编程经验,是很难领会这一行代码的。当然,如果说你是背出来的,另当别论了。

    第二部分:apply和call的用法

    定义一个log函数,它可以代理console.log的方法。这个简单,再基础不过了:

    function log(msg) {
          console.log(msg);
        }

    我发现,越是简单的题,我越小心,总要多看几遍,身怕里边有什么坑在等着我跳,仔细看了三遍,确实是没有地雷. 

    接着题目难度加大,要求可以打印多个参数,参数个数不确定,温馨提示可以使用apply。

    既然提示都这么明显了,用apply是没错的:

    function log(){
          console.log.apply(console, arguments);
        };

    这里要注意的是,用apply的时候,第一个参数是log执行环境的this指向。第二个参数必须是数组,或者是arguments这样的类数组对象。对于这个题目来说,如果第一个参数用

    this的话,指的是在window中去找log这个方法, 那不成了自己找自己啊。

    接下来要给每一个log消息添加一个"(app)"的前辍,比如:

     log('hello world'); //'(app) hello world'

    现在可能有点麻烦了。好的侯选人知道arugments是一个伪数组,然后会将他转化成为标准数组。通常方法是使用Array.prototype.slice,像这样:

    复制代码
    function log(){
          var args = Array.prototype.slice.call(arguments);
          args.unshift('(app)');
    
      console.log.apply(console, args);
    };</span></pre>
    
    复制代码

    当然你也可以不用call或apply之类的方法实现,但是按照出题者的意思走,可以收到事半功倍的效果. 如果是技术官,他会认为你懂他!如果不是,他会认为你的答案很贴合“标准答案”。

    第三部分:上下文

    关于this的身份,一直以来都是各大面试题喜欢八卦的命题。这次也不例外,题目是一段代码:

    复制代码
    var User = {
      count: 1,
    

    getCount: function() {
    return this.count;
    }
    };

    复制代码

    下面几行,log输出的会是什么?

     console.log(User.getCount());
    
    </span><span style="color: rgba(0, 0, 255, 1)">var</span> func =<span style="color: rgba(0, 0, 0, 1)"> User.getCount;
    console.log(func());</span></pre>
    

    对于一个javascript 编程的老手来说,这种情况下,正确的答案是1和undefined,一点都不会意外的,但是对于以前一直是从事套站写网页模版的候选人来说,就未必了。

    当然,我在此并没有要黑这些资深网页制作者的意思。

    那么问题来了,怎么让这个答案输出都是1呢?

    正确的答案是使用Function.prototype.bind,例如:

    var func = User.getCount.bind(User);
        console.log(func());

    用bind?这些从ie5时代走过来的前辈们会觉得难以接受。这个方法对老版本的浏览器不起作用,好吧,那就顺道问下,这个怎么兼容这个bind好了。

    聊到兼容,理论派或者说思路派就要开始滔滔不绝了,balabala...讲一大堆。他们甚至不屑于写这样的代码,认为太低估了他们的实力。

    为了这篇文章的严谨和真实性,我特意拿到我所在的前端开发群里考察了一翻,注意,这个群里边有许多“XX神之类的人物”哦,当然也有我自己。

    一起来看看它们的答案吧:

    杭州的朋友给的答案:

    这个暂且不说他写的对与错,这逻辑就有点让人眼晕,直接放弃。事后我采访他,原来是从网上copy的,这种不经过自己吸收的拿来主义,吃再多也是长不胖滴。

    再看一位广州的朋友给的答案:

    复制代码
     Function.prototype.binds=function(obj){
            var fc=this;
            return function(){
                fc.call(obj);
            }
        }
    复制代码

    再看一位烟台的朋友:

    ..... 其它的就不一一例举了。我来说说这两个答案。 
    这两位同学的思路都是正确的,但是细节上都有错误。这也是我要求贴源码,不要写思路的原因。
    有些人说起思路来涛涛不绝,但是写出来的代码却是漏洞百出。看起来很简单,却不一定做的很容易。同意的点个赞!
    在宣布参考答案之前,我来做为一回面试官,过一把干瘾也好嘛。从答题结果来看,我会选广州的朋友。

    下面说下我的理由:
    虽然广州的朋友修改过多次仍然没有全对,但是都是粗心或是考虑不全面,假以时间,还是可以胜任工作。
    而烟台的那位虽然写的很简洁,也非常注意命名规范,思路也明确,但是Function.apply这一行,暴露了他基础的不足,需要学习的时间成本明显要多一些。当然如果悟性很好的话,另当别论。
    以上纯属娱乐,请看到的朋友不要当真。

    下面看下我的参考答案:

    复制代码
    Function.prototype.bind = function(context){
        var self = this;
        var arg  = [].slice.call(arguments,1);
    
    </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)">(){
        </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> self.apply(context,arg)
    }
    

    }

    复制代码

    检测一下:

    复制代码
    setTimeout(function(msg){
        console.log(this.name,this.age,msg)
    }.bind({
        name : 'frog',
        age  : 18
    },'blogs'))
    

    var User = {

    count: 1,

    getCount: function() {
    return this.count;
    }

    };

    var func = User.getCount.bind(User);

    console.log(func());

    复制代码

    上面的结果和使用bind的结是一致的,但是既然是做兼容,这样写,相当于忽略了那些本来就支持bind方法的浏览器了。得修改一下:

    复制代码
    Function.prototype.bind = Function.prototype.bind || function(context){
        var self = this;
        var arg  = [].slice.call(arguments,1);
        return function(){
            return self.apply(context, arg);
        };
    }
    复制代码

    这个地方,可以顺便提问关于闭包的问题。

    函数式声明提升及没有块级作用域

    这个是我自己加的,因为我在工作中,经常会遇到这样或那样的问题,多少都与这些东东粘点边。下面是一个例子:

    复制代码
    function say(){
        function name(){
            console.log('frog')
        }
        return name();
        function name(){
            console.log('hello')
        }
    }
    

    var name = say();

    复制代码

    输出的是hello;我承认,不会有人傻X到写这样无聊的代码。这个题的原型我不记得了,反正大致就是说明这样一个问题。当然关于这个例子你肯定还有更好的代码。例如:

    复制代码
    function say(){
        ok(); //这里会报错
        var ok = function(){
            console.log('ok')
        }
    }
    

    //----------------
    function say(){
    ok();
    //这样就正常了
    function ok(){
    console.log(
    'ok')
    }
    }

    复制代码

    变量声明则不会被提升,函数式声明则会提升。再看一个例子:

    var name = 'frog'
    function hello(){
        alert(name); // undefined
        var name = 'bbc';
    }

    很多初学者都在这里翻船的。原因在于他们只记得没有块级作用域,但是没理解什么叫块级作用域。

    在javascript中,函数是可以形成一个独立作用域的,变量的查找,首先是就近原则,先看自己有没有,自己没有,就会自动跑到外层去找,这一点和其它语言可能不一样,它会自动跑外边去找。在整个hello作用域内,只要定义了name这个变量,就不会去window中找,不过呢,在hello自己的作用域内,还有一个规则,申明之前调用,都是undefined,申明且赋值之后调用才会有值。alert(name)发生在申明之前,所以会弹出undefined就是这么个道理。

    下面我再来说说这个没有块级作用域:

    for(var i=0;i<10;i++){
        //...
    }
    alert(i)//10

    这个块,指的就是两个大括号之间的区域, 在javascript中,原本是不存在这个问题的,出现这个疑问,是由那些搞过c语言之类的人转来搞javascript带来的。他们以前的知识

    中,循环之后,i自动销毁了,但是javascript中不是这样的。只要记得javascript中,函数才是划分作用域的就可以了。

    类似的情况还有判断:

    function fn(){if(true){
        var i = 10
      }
      return i;
    }

    其他方面

    当然这些问题只能覆盖前端一点点的知识的,还有很多其他的方面你有可能会问到,像性能,HTML5 API, AMD和CommonJS模块模型,构造函数(constructors),类型和盒子模型(box model)。以及热门的移动前端开发框架angula.js,spa应用,栅格布局, 异步编程问题,甚至可以问问算法,数据库什么的。只要给的起价,怎么折腾都行。

    欢迎大家来我的 [Gitee仓库](https://gitee.com/jiffyzhang)参观。 同时欢迎关注我的同名公众号:就这样写(keepStarve),未来很大可能会活跃在此地。
  • 相关阅读:
    CentOS Linux更改MySQL数据库目录位置
    CodeCombat第一关:KITHGARD地牢之KITHGARD精通
    aspose将word转pdf时乱码,或者出现小方框问题
    go ---switch语句
    go ---作用域及判断变量类型的方式。
    golang 学习笔记 --基本类型
    892. 三维形体的表面积
    70. 爬楼梯
    centOS安装java
    CentOS7安装MySQL
  • 原文地址:https://www.cnblogs.com/zhang-bobo/p/13797790.html
Copyright © 2011-2022 走看看