Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

2019前端面试题 —— js基础 #15

Open
fengmiaosen opened this issue Jul 8, 2019 · 0 comments
Open

2019前端面试题 —— js基础 #15

fengmiaosen opened this issue Jul 8, 2019 · 0 comments

Comments

@fengmiaosen
Copy link
Owner

fengmiaosen commented Jul 8, 2019

  1. js事件循环(eventloop)

    https://juejin.im/post/5c394da4518825253661bd4d

    Tasks, microtasks, queues and schedules 强烈推荐,图文并茂可查看每一步执行动画

    引用贺老师知乎上的一个例子

    async function f() {
      await p
      console.log('ok')
    }

简化理解为:

    function f() {
        return RESOLVE(p).then(() => {
            console.log('ok')
        })
    }
  1. ES5继承和ES6继承的区别

    • ES5的继承实质上是先创建子类的实例对象,然后再将父类的方法添加到this上(Parent.call(this)).

    • ES6的继承有所不同,实质上是先创建父类的实例对象this,然后再用子类的构造函数修改this。因为子类没有自己的this对象,所以必须先调用父类的super()方法,否则新建实例报错。

  2. new对象的具体实现过程

    https://blog.csdn.net/Neokekeke/article/details/79394197

    new的本质:

    • 创建一个全新的对象
    • 新对象会被执行prototype操作(prototype之后会写文章专门进行介绍,感兴趣的童鞋可以先关注下)
    • 新对象会被绑定到函数调用的this
    • 如果函数没有返回新对象,new表达式中的函数调用会自动返回这个新对象(对于一个构造函数,即使它内部没有return,也会默认返回return this)
function myNew(fun) {
    return function () {
        // 创建一个新对象且将其隐式原型指向构造函数原型
        let obj = {
            __proto__: fun.prototype
        }
        // 执行构造函数
        fun.call(obj, ...arguments)
        // 返回该对象
        return obj
    }
}

function person(name, age) {
    this.name = name
    this.age = age
}
let obj = myNew(person)('chen', 18) // {name: "chen", age: 18}
  1. 模块引入 es6的import 和 commonjs的 require的主要区别是什么( CommonJS 和 ES Module 的区别)

    加载时机:CommonJS 是运行时加载(动态加载),ES Module 是编译时加载(静态加载)
    加载模块:CommonJS 模块就是对象,加载的是该对象,ES Module 模块不是对象,加载的不是对象,是接口
    加载结果:CommonJS 加载的是整个模块,即将所有的接口全部加载进来,ES Module 可以单独加载其中的某个接口(方法)
    输出:CommonJS 输出值的拷贝,ES Module 输出值的引用
    this: CommonJS 指向当前模块,ES Module 指向 undefined

  2. 跨域请求cookie是否会传送到服务端

    设置 withCredentials 为 true

  3. defer 和 async 的区别

    defer要等到整个页面在内存中正常渲染结束(DOM 结构完全生成,以及其他脚本执行完成),才会执行;async一旦下载完,渲染引擎就会中断渲染,执行这个脚本以后,再继续渲染。一句话,defer是“渲染完再执行”,async是“下载完就执行”。另外,如果有多个defer脚本,会按照它们在页面出现的顺序加载,而多个async脚本是不能保证加载顺序的。

  4. 自己实现promise.all

    https://segmentfault.com/a/1190000010765655

    function promiseAll(promises) {
    return new Promise(function(resolve, reject) {
      if (!isArray(promises)) {
        return reject(new TypeError('arguments must be an array'));
      }
      var resolvedCounter = 0;
      var promiseNum = promises.length;
      var resolvedValues = new Array(promiseNum);
      for (var i = 0; i < promiseNum; i++) {
        (function(i) {
          Promise.resolve(promises[i]).then(function(value) {
            resolvedCounter++
            resolvedValues[i] = value
            if (resolvedCounter == promiseNum) {
              return resolve(resolvedValues)
            }
          }, function(reason) {
            return reject(reason)
          })
        })(i)
      }
    })
  }
  1. promise的then返回值

    • then方法返回的是一个新的Promise实例
    • catch方法返回的还是一个 Promise 对象,因此后面还可以接着调用then方法
  2. promise的三种状态

    Promise.resolve('foo')
    // 等价于
    new Promise(resolve => resolve('foo'))
    

    你真的完全掌握了promise么
    从一道Promise执行顺序的题目看Promise实现

  3. 原生js实现排行榜列表(dom)结构的首位调转顺序

  4. Js严格模式与非严格模式

    https://larryzhuo.github.io/2015/11/03/20151103/
    https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Strict_mode

  5. 防抖和节流

    函数防抖和函数节流都是防止某一时间频繁触发,但是这两兄弟之间的原理却不一样。
    函数防抖是某一段时间内只执行一次,而函数节流是间隔时间执行。

结合应用场景

debounce 防抖

  • search搜索联想,用户在不断输入值时,用防抖来节约请求资源。
  • window触发resize的时候,不断的调整浏览器窗口大小会不断的触发这个事件,用防抖来让其只触发一次

throttle 节流

  • 鼠标不断点击触发,mousedown(单位时间内只触发一次)
  • 监听滚动事件,比如是否滑到底部自动加载更多,用throttle来判断
// 这个是用来获取当前时间戳的
function now() {
  return +new Date()
}
/**
 * 防抖函数,返回函数连续调用时,空闲时间必须大于或等于 wait,func 才会执行
 *
 * @param  {function} func        回调函数
 * @param  {number}   wait        表示时间窗口的间隔
 * @param  {boolean}  immediate   设置为ture时,是否立即调用函数
 * @return {function}             返回客户调用函数
 */
function debounce (func, wait = 50, immediate = true) {
  let timer, context, args

  // 延迟执行函数
  const later = () => setTimeout(() => {
    // 延迟函数执行完毕,清空缓存的定时器序号
    timer = null
    // 延迟执行的情况下,函数会在延迟函数中执行
    // 使用到之前缓存的参数和上下文
    if (!immediate) {
      func.apply(context, args)
      context = args = null
    }
  }, wait)

  // 这里返回的函数是每次实际调用的函数
  return function(...params) {
    // 如果没有创建延迟执行函数(later),就创建一个
    if (!timer) {
      timer = later()
      // 如果是立即执行,调用函数
      // 否则缓存参数和调用上下文
      if (immediate) {
        func.apply(this, params)
      } else {
        context = this
        args = params
      }
    // 如果已有延迟执行函数(later),调用的时候清除原来的并重新设定一个
    // 这样做延迟函数会重新计时
    } else {
      clearTimeout(timer)
      timer = later()
    }
  }
}
https://juejin.im/post/5b8de829f265da43623c4261
https://yuchengkai.cn/docs/frontend/#%E9%98%B2%E6%8A%96
  1. 实现一个 Promise
    https://www.cxymsg.com/guide/jsWritten.html#%E5%AE%9E%E7%8E%B0promise

  2. NodeJS的Event Loop
    https://www.cxymsg.com/guide/eventLoop.html#nodejs%E7%9A%84event-loop

  3. Reflect
    https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Reflect

  4. Function.prototype.call.apply作用详解

    参照 Reflect.apply

    var num=Function.prototype.apply.call(function (b) { return this.a + b }, {a:2},[1.75])
    console.log(num);
    

    相当于

    var num=(function (b) { return this.a + b }).apply( {a:2},[1.75])
    console.log(num);
    

    http://www.softwhy.com/article-7058-1.html
    https://www.jianshu.com/p/87eafacf51cf

  5. 利用apply调用系统max方法获取最大值

    /* min/max number in an array */
    var numbers = [5, 6, 2, 3, 7];
    
    /* using Math.min/Math.max apply */
    var max = Math.max.apply(null, numbers); 
    /* This about equal to Math.max(numbers[0], ...)  or Math.max(5, 6, ...) */
    var min = Math.min.apply(null, numbers);
    
  6. 类数组(Array-like)对象/集合转换成一个新数组

function list() {
  return Array.prototype.slice.call(arguments);
}

var list1 = list(1, 2, 3); // [1, 2, 3]
console.log(list1);
  1. 重新认识string.replace

    https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/String/replace#%E8%AF%AD%E6%B3%95

  2. 查找字符串中出现最多的字符和个数

let str = "abcabcabcbbccccc";
let num = 0;
let char = '';

 // 使其按照一定的次序排列
str = str.split('').sort().join('');
// "aaabbbbbcccccccc"

// 定义正则表达式
let re = /(\w)\1+/g;
str.replace(re,($0,$1) => {
    if(num < $0.length){
        num = $0.length;
        char = $1;        
    }
});
console.log(`字符最多的是${char},出现了${num}次`);
  1. 实现千位分隔符
// $& 最后匹配的字符
// (?=exp) 匹配exp前面的位置
// x(?=y)
//Meaning:Matches x only if x is followed by y.
//解释:当x后面跟着y(即y的正则匹配成功)的时候,匹配成功
function parseToMoney(num) {
  num = parseFloat(num.toFixed(3));
  let [integer, decimal] = String.prototype.split.call(num, '.');
  integer = integer.replace(/\d(?=(\d{3})+$)/g, '$&,');
  return integer + '.' + (decimal ? decimal : '');
}
parseToMoney(1234.56);
  1. Canvas API

    https://juejin.im/post/5ac437b5f265da238f12c1c6

  2. 再谈前端虚拟列表的实现

    https://zhuanlan.zhihu.com/p/34585166

  3. Reflect对象
    http://es6.ruanyifeng.com/#docs/reflect

  4. 一个例子看懂循环和闭包之间的关系

    https://www.nodejs.red/#/javascript/func?id=%e4%b8%80%e4%b8%aa%e4%be%8b%e5%ad%90%e7%9c%8b%e6%87%82%e5%be%aa%e7%8e%af%e5%92%8c%e9%97%ad%e5%8c%85%e4%b9%8b%e9%97%b4%e7%9a%84%e5%85%b3%e7%b3%bb

  5. 区分event对象中的[clientX,offsetX,screenX,pageX]

    https://www.jianshu.com/p/a52077e8369d

  6. cookie samesite

    https://www.chromestatus.com/feature/5088147346030592
    https://juejin.im/post/5e1437c6e51d45415b4760cb
    https://zhuanlan.zhihu.com/p/73261967

  7. postMessage跨页面传递数据

    MDN postMessage

  8. localhost:3000 与 localhost:5000 的 cookie 信息是否共享

    根据同源策略,cookie是区分端口的,但是浏览器实现来说,“cookie区分域,而不区分端口,也就是说,同一个ip下的多个端口下的cookie是共享的

  9. 帮你彻底搞懂JS中的prototype、__proto__与constructor(图解)

①__proto__和constructor属性是对象所独有的;
② prototype属性是函数所独有的,因为函数也是一种对象,所以函数也拥有__proto__和constructor属性。

__proto__属性的作用就是当访问一个对象的属性时,如果该对象内部不存在这个属性,那么就会去它的__proto__属性所指向的那个对象(父对象)里找,一直找,直到__proto__属性的终点null,然后返回undefined,再往上找就相当于在null上取值,会报错。
通过__proto__属性将对象连接起来的这条链路即我们所谓的原型链。
prototype属性的作用就是让该函数所实例化的对象们都可以找到公用的属性和方法,即f1.proto === Foo.prototype。
constructor属性的含义就是指向该对象的构造函数,所有函数(此时看成对象了)最终的构造函数都指向Function。 另外 proto 属性是浏览器对es5的实现,而不是es标准

https://blog.csdn.net/cc18868876837/article/details/81211729
  1. js存储“栈”和“堆”的特点

在JS中,基本数据类型变量大小固定,并且操作简单容易,所以把它们放入栈中存储。
引用类型变量大小不固定,所以把它们分配给堆中,让他们申请空间的时候自己确定大小,这样把它们分开存储能够使得程序运行起来占用的内存最小。

闭包中的变量并不保存中栈内存中,而是保存在堆内存中。 这也就解释了函数调用之后之后为什么闭包还能引用到函数内的变量

  • 栈内存由于它的特点,所以它的系统效率较高。
  • 堆内存需要分配空间和地址,还要把地址存到栈中,所以效率低于栈。

栈:

存储基础数据类型
按值访问
存储的值大小固定
由系统自动分配内存空间
空间小,运行效率高
先进后出,后进先出
栈中的DOM,ajax,setTimeout会依次进入到队列中,当栈中代码执行完毕后,再将队列中的事件放到执行栈中依次执行。
微任务和宏任务

堆:

存储引用数据类型
按引用访问
存储的值大小不定,可动态调整
主要用来存放对象
空间大,但是运行效率相对较低
无序存储,可根据引用直接获取

  1. 前端跨页面通信
    对于同源页面,常见的方式包括:

广播模式:Broadcast Channe / Service Worker / LocalStorage + StorageEvent
共享存储模式:Shared Worker / IndexedDB / cookie
口口相传模式:window.open + window.opener
基于服务端:Websocket / Comet / SSE 等

而对于非同源页面,则可以通过嵌入同源 iframe 作为“桥”,将非同源页面通信转换为同源页面通信。

  1. 用 promise 实现一个请求超时功能
function promiseWithTimeout(url, timeout = 3000) {
  return new Promise((resolve, reject) => {
    fetch(url).then(data => data.json()).then(data => resolve(data)); // fetch 先得到结果就 resolve
    setTimeout(() => reject(Error('time is out!')), timeout); // 时间到了还没 fetch 到就 reject
  });
}

promiseWithTimeout('http://localhost:8080/data.json')
  .then(data => console.log(data))
  .catch(err => console.error(err));
  1. async/await 里的并行和串行
(async ()=>{
    let namePromise = getName()
    let idPromise = getId()              // 先生成所有 promise 实例

    let name = await namePromise
    let id = await idPromise

})()
  1. CORS(跨域资源共享) 什么情况下会发送预检请求
> “需预检的请求”要求必须首先使用 [OPTIONS](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Methods/OPTIONS)   方法发起一个预检请求到服务器,以获知服务器是否允许该实际请求。"预检请求“的使用,可以避免跨域请求对服务器的用户数据产生未预期的影响。

https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS
  1. js实现readonly

    • Object.defineProperty
    • Proxy
  2. 位运算的妙用

    https://juejin.im/post/5a98ea2f6fb9a028bb186f34
    https://juejin.im/entry/57317b2679df540060d5d6c2

  3. js 类数组对象

    https://juejin.im/post/5be561a6f265da613f2efb73

    类数组对象,就是指可以通过索引属性访问元素并且拥有 length 属性的对象

    var arrLike = {
      0: 'name',
      1: 'age',
      2: 'job',
      length: 3
    }
    
    • 类数组对象与数组的区别

      • 类数组对象不能直接使用数组的方法
    • 如果类数组对象能够和数组一样使用数组的方法,应该怎么做

    // 使用 call
    Array.prototype.push.call(arrLike, 'hobby');
    console.log(arrLike); // { '0': 'name', '1': 'age', '2': 'job', '3': 'hobby', length: 4 }
    
    var arrLikeStr = Array.prototype.join.call(arrLike, '&')
    console.log(arrLikeStr); // name&age&job&hobby
    
    // 使用 apply
    Array.prototype.push.apply(arrLike, ['hobby']);
    console.log(arrLike); // { '0': 'name', '1': 'age', '2': 'job', '3': 'hobby', length: 4 }
    
    var arrLikeStr = Array.prototype.join.apply(arrLike, ['&'])
    console.log(arrLikeStr); // name&age&job&hobby
    • 把类数组对象转换成真正的数组
    // 使用 call
    console.log(Array.prototype.slice.call(arrLike,0));
    console.log(Array.prototype.splice.call(arrLike,0));  // 会改变原先的类数组对象
    
    
  4. 前端图片&二进制操作

    前端如何进行图片处理,然后穿插介绍二进制、Blob、Blob URL、Base64、Data URL、ArrayBuffer、TypedArray、DataView 和图片压缩相关的知识点

参考资料

@fengmiaosen fengmiaosen changed the title 2019前端面试题(一)—— js基础原理 2019前端面试题 —— js基础原理 Jul 8, 2019
@fengmiaosen fengmiaosen changed the title 2019前端面试题 —— js基础原理 2019前端面试题 —— js基础 Sep 3, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant