• 面试题整理


    70个JavaScript面试题集锦,内含解答,自测 JS 掌握程度

    比上面的多5道

    面试题101道

     你不知道的 JSON.stringify() 的威力

     

    1. ['1', '2', '3'].map(parseInt)输出结果

    ['1', '2', '3'].map(parseInt);    // [1,NaN,NaN]

    这道题需要理解两个点:

    ①.parseInt(string, radix)

    接收两个参数,第一个表示被处理的值(字符串),第二个表示为解析时的基数。
    radix 可选。表示要解析的数字的基数。该值介于 2 ~ 36 之间。如果省略该参数或其值为 0,则数字将以 10 为基础来解析。如果它以 “0x” 或 “0X” 开头,将以 16 为基数。如果该参数小于 2 或者大于 36,则 parseInt() 将返回 NaN。

    ②.代码实际执行的以下代码

    ['1', '2', '3'].map((item, index) => {
        return parseInt(item, index)
    })
    parseInt('1', 0)   // 1
    parseInt('2', 1)   // NaN
    parseInt('3', 2)   // NaN, 3 不是二进制 string要符合基数的规范

    同样的经典题

    ['10','10','10','10','10'].map(parseInt)
    // =>
    parseInt('10', 0) // 10
    parseInt('10', 1) // NaN
    parseInt('10', 2) // 2, 
    parseInt('10', 3) // 3, 
    parseInt('10', 4) // 4, 
    
    // 所以['10','10','10','10','10'].map(parseInt)  => [10,NaN,2,3,4];

    2.实现一个函数:输入一个整数,求该整数的二进制表达中有多少个1?

    方法1:

    function fun1(num){
        return new Number(num).toString(2).split('').filter(item => item == 1).length 
    }
    fun1(9)   // 2

    方法2:

    function fun2(num){
        var len = 0;
        while(num > 0) {
            var a = num % 2;
            num = (num - a) / 2;
            if(a === 1) {
                len++;
            }
        }
        return len;
    }
    fun2(9)

    3.实现一个函数:给定一个字符串,找出出现次数最多的字符及次数

    function getMaxStr(str) {
        let json = {};
        for(let i =0;i<str.length;i++){
            if(json[str[i]]) {
                json[str[i]]++
            }else {
                json[str[i]] = 1;
            }
        }
        let maxNum = 0;
        let maxStr = '';
        for(let key in json){
            if(json[key] > maxNum){
                maxNum = json[key];
                maxStr = key;
            }
        }
        console.log(`出现次数最多的字符是${maxStr},出现次数是${maxNum}`)
    }
    getMaxStr('ddddssfes')    // 出现次数最多的字符是d,出现次数是4

    4.this指向理解

    var a = 1;
    var obj = {
        a: 2,
        func1: ()=>{console.log(this.a)},
        func2: function(){console.log(this.a)}
    }
    
    var obj2 = {a: 3};
    console.log(obj.func1())    // 1
    console.log(obj.func2())    // 2
    obj.func2.apply(obj2)       // 3
    var newFunc = obj.func2;
    newFunc()    //1

    tips: 函数直接被调用时this则指向window,函数作为某对象的方法调用时,this指向该对象,而箭头函数没有执行上下文,取决于他就近的外面的一层非箭头函数的函数。

    箭头函数的this看外层的是否有函数,如果有,外层函数的this就是内部箭头函数的this,如果没有,则this是window

    5.下面三段代码分别输出什么?并且什么时候输出什么?

    for (var i = 0; i < 5; i++) {
      setTimeout(function () {
        console.log(i)
      }, 1000 * i)
    } 
    // 在极短的一段时间内输出5,随后每隔一秒输出一个5
    // 5 5 5 5 5
    // for循环结束后才到宏观任务setTimeout,这时候i的值已经变为5了
    for (let i = 0; i < 5; i++) { setTimeout(function () { console.log(i) }, 1000 * i) } // 在极短的一段时间内输出0,随后每隔一秒结果加1 // 0 1 2 3 4
    // let i 是块作用域
    for (var i = 0; i < 5; i++) { (function (i) { setTimeout(function () { console.log(i) }, 1000 * i) })(i) } // 在极短的一段时间内输出0,随后每隔一秒结果加1 // 0 1 2 3 4
    // 立即执行函数,创建了属于自己的作用域,因此每一次执行都是不同的i

    事件运行机制

    6.自由变量理解 

    var x = 10
    function fn() {
      console.log(x)
    }
    function show(f) {
      var x = 20
      (function() {
        f() //10,而不是20
      })()
    }
    show(fn)

    什么是自由变量? --当前作用域没有定义的变量,这成为自由变量 

    var a = 100
    function fx() {
        var b = 200
        console.log(a) // 这里的a在这里就是一个自由变量
        console.log(b)
    }
    fx()

    在fn函数中,取自由变量x的值时,要到哪个作用域中取?--要到创建fn函数的那个作用域中取,无论fn函数将在哪里调用。

    要到创建这个函数的那个域。作用域中取值,这里强调的是“创建”,而不是“调用”,切记切记——其实这就是所谓的"静态作用域"。

    7. 闭包的理解

    function outer(){
      var num=0;//内部变量
      return function add(){//通过return返回add函数,就可以在outer函数外访问了
          num++;//内部函数有引用,作为add函数的一部分了
          console.log(num);
      };
    }
    var func1=outer();
    func1();
    //实际上是调用add函数, 输出1 func1();//输出2 因为outer函数内部的私有作用域会一直被占用 var func2=outer(); func2();// 输出1 每次重新引用函数的时候,闭包是全新的。 func2();// 输出2

    一般情况下,函数执行会形成一个新的私有的作用域,当私有作用域中的代码执行完成后,我们当前作用域都会主动的进行释放和销毁。但当遇到函数执行返回了一个引用数据类型的值,并且在函数的外面被一个其他的东西给接收了,这种情况下一般形成的私有作用域都不会销毁。

    所谓内存泄漏指任何对象在您不再拥有或需要它之后仍然存在。闭包不能滥用,否则会导致内存泄露,影响网页的性能。闭包使用完了后,要立即释放资源,将引用变量指向null。

    8.手写简化版防抖函数、节流函数

    // 防抖函数 防抖函数原理:在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时。 只执行最后提交的一次
    const debounce = (fn, delay = 500) => {
      let timer = null;
      return () => {
        clearTimeout(timer);
        timer = setTimeout(() => {
          fn();
        }, delay);
      };
    };
    
    // 节流函数 防抖函数原理:规定在一个单位时间内,只能触发一次函数。如果这个单位时间内触发多次函数,只有一次生效。
    const throttle = (fn, delay = 500) => {
      let flag = true;
      return () => {
        if (!flag) return;
        flag = false;
        setTimeout(() => {
          fn();
          flag = true;
        }, delay);
      };
    };

     9.说出下面运行的结果,解释原因 

    function test(person) {
        person.age = 26;
        person = {
            name: 'hzj',
            age: 18
        };
        return person;
    }
    const p1 = {
        name: 'fyq',
        age: 19
    };
    const p2 = test(p1);
    console.log(p1);   //{name: “fyq”,age: 26}
    console.log(p2);   //{name: “hzj”,age: 18}

    原因: 在函数传参的时候传递的是对象在堆中的内存地址值,test函数中的实参person是p1对象的内存地址,通过调用person.age = 26确实改变了p1的值,但随后person变成了另一块内存空间的地址,并且在最后将这另外一份内存空间的地址返回,赋给了p2。

     10.new 关键字有什么作用 

    new关键字与构造函数一起使用以创建对象在JavaScript中。

    下面看看例子:

    function Employee(name, position, yearHired) {
      // creates an empty object {}
      // then assigns the empty object to the "this" keyword
      // this = {};
      this.name = name;
      this.position = position;
      this.yearHired = yearHired;
      // inherits from Employee.prototype
      // returns the "this" value implicitly if no
      // explicit return statement is specified
    };
    
    const emp = new Employee("Marko Polo", "Software Developer", 2017);

    new关键字做了4件事:

    • 创建空对象 {}

    • 将空对象分配给 this 值

    • 将空对象的__proto__指向构造函数的prototype

    • 如果没有使用显式return语句,则返回this

    根据上面描述的,它将首先创建一个空对象{},然后它将this值赋给这个空对象this={},并向这个对象添加属性。因为我们没有显式的return语句,所以它会自动为我们返回this。

    11. http常见的响应码

     

     常见状态码:

    • 200 请求成功
    • 301 永久重定向,一般是地址发生变化
    • 302 临时重定向
    • 304 Not Modified(通常是在协商缓存中表示本次内容未修改)
    • 400 Bad Request - 前端请求错误
    • 401 Unauthorized - 未授权
    • 403 Forbidden - 请求被拒绝
    • 404 Not Found - 资源不存在
    • 500 Inter Server Error - 服务端异常
    • 503 Server Unavailable - 服务不可用

    12.前端鉴权

    鉴权主要分为四种:

    • HTTP Basic Authentication (HTTP基本认证)
    • session-cookie
    • Token 验证(包括JWT,SSO)
    • OAuth(开放授权)

    我们普通网站常用的认证就是session-cookie的方式,用户向服务端发生请求,服务端会创建session并保存相关身份信息,并向客户端下发一个sessionId,大家如果用心的话,会发现跟JAVA交互的时候,浏览器会有一个JSESSION_ID,跟PHP交互的时候,会有一个PHPSESSION_ID;后面的每次请求,客户端都会自动带上这个cookie跟服务端通信。

    实际上大家要明白每一种方式的作用;SSO主要用来做单点登录;OAuth主要用来做第三方网站授权;JWT就是一种便于扩展的跨域认证解决方案,通常会考察这个。

    给大家推荐阮一峰的JWT讲解 

     

    OAuth2.0原理

    讲原理主要是让你对这个过程做梳理,并不要求对源码过程做剖析,所以可以通过一个简单的流程来进行回答。我通过微信的授权登录来给大家做讲解:OAuth2.0是一个开源的授权认证方案。当我们登录一个网站时,如果想要通过微信做授权登录,从而获取微信的用户信息,正常情况肯定是不允许,通过微信开放的OAuth2.0我们可以做授权认证。我们点击自己网站的微信按钮,跳转一个链接,这个链接比如是:https://open.weixin.qq.com/connect/oauth2/authorize?会跳转到微信那边去让用户同意授权,用户同意以后,会重定向回来并携带一个code,此code是微信下发的临时凭证。开发者拿到此code以后,就可以获取access_token,根据下发的token,我们才能有权限获取其它接口信息。

    • 点击按钮,跳转第三方授权网站
    • 用户同意授权,重定向回来携带code
    • 开发者根据code获取微信access_token
    • 拿到token拉取用户资料

    回答的时候,能够把这个过程描述清楚就好,不要过多解析源码。更进一步了解OAuth2.0可参考阮一峰教程:http://www.ruanyifeng.com/blog/2019/04/oauth-grant-types.html

    OAuth2.0的四种方式

    • 授权码(最常用,适用于有后端的web)
    • (授权码)隐藏式 (适用于没有后端的web,少了拿授权码步骤,直接拿到令牌)
    • 密码式 (通过用户给出用户名及密码获取令牌,风险大,在其他授权方式无法获取到令牌且高度信任该应用时下适用)
    • 客户端凭证 (适用于没有前端的命令行应用,这种方式给出的令牌,是针对第三方应用的,而不是针对用户的,即有可能多个用户共享同一个令牌。)

    授权码图示

    (授权码)隐藏式图示

    A 网站拿到令牌以后,就可以向 B 网站的 API 请求数据了。

    此时,每个发到 API 的请求,都必须带有令牌。具体做法是在请求的头信息,加上一个Authorization字段,令牌就放在这个字段里面。

    更新令牌 

    令牌的有效期到了,如果让用户重新走一遍上面的流程,再申请一个新的令牌,很可能体验不好,而且也没有必要。OAuth 2.0 允许用户自动更新令牌。

    具体方法是,B 网站颁发令牌的时候,一次性颁发两个令牌,一个用于获取数据,另一个用于获取新的令牌(refresh token 字段)。令牌到期前,用户使用 refresh token 发一个请求,去更新令牌。

    未完,待续...

  • 相关阅读:
    Local File Manage in JavaScript Using FileSystemObject
    How to Register COM in VS
    Permission Error When Building C++ in VS2010
    Upload and Download File using Java
    JavaScript Interactive with ActiveX Control
    一个完整的Installshield安装程序实例
    一个.NET(C#)的双键字典类
    C#实现内存中字符串或byte[]的加解密
    判断请求是否来自 AJAX
    Reflector 7.3.018
  • 原文地址:https://www.cnblogs.com/kewenxin/p/11535611.html
Copyright © 2020-2023  润新知