2020前端一些大厂面经系列———JS

12 篇文章 1 订阅

接上篇
总结了一下大厂面试中出现的JS题目,都是2019年12月的亲身经历
问题都附有解答,阅读非常方便

JS

0. bind apply call的相同与不同(腾讯)

  • 相同点:都可以改变函数内部的this指向
  • 不同点:
  1. call和apply会调用函数,并且改变函数内部的this指向
  2. call和apply传递的参数不一样,apply必须以数组形式
  3. bind不会调用函数,但是会改变函数内部的this指向
  • 主要应用场景:
  1. call主要用作继承
  2. apply经常跟数组有关,比如借助数学对象实现数组中的最大最小值
  3. bind可用于改变定时器内部的this指向

1. 用apply或者call实现bind(字节跳动)

考察的其实是对bind和apply/call的区别,即新建了一个函数

    Function.prototype.bind = function( context ){ 
    var self = this; // 保存原函数
    return function(){ // 返回一个新的函数
        return self.apply( context, arguments );//执行新的函数的时候,会把之前传入的context当作新的函数体的this
    } 
};

2. 节流和防抖分别是什么?手写一波(字节跳动,网易雷火)
防抖是动作绑定事件,动作发生后一定时间后触发事件,在这段时间内,如果该动作又发生,则重新等待一定时间再触发事件。
节流是 动作绑定事件,动作发生后一段时间后触发事件,在这段时间内,如果动作又发生,则无视该动作,直到事件执行完后,才能重新触发。

// 防抖
function debounce (fn, delay) {
	let timber = null;
	return function(this, ...args) {
		if(timber) {
			clearTimeout(timber);
			timber = null;
		}
		timber = setTimeout(fn.bind(this, ...args), delay);
	}
}

// 节流
function throttle (fn, delay) {
	let prev = Date.now();
	return function(this, ...args) {
		let now = Date.now();
		if (now - prev > delay) {
			fn.apply(this, ...args);
			prev = now;
		}
	}
}

3. JS基本数据类型和引用类型有哪些? 二者的区别?(滴滴,网易雷火)
基本类型:

  • number 用于任何类型的数字,整数或浮点数。
  • string 用于字符串:一个字符串可以包含一个或多个字符,所以没有单独的单字符类型。
  • boolean 用于 true 和 false。
  • null 用于未知的值 —— 只有一个 null 值的独立类型。
  • undefined 用于未定义的值 —— 只有一个 undefined 值的独立类型。
  • symbol 用于创建对象的唯一的标识符。是ES6里新出现的类型

引用类型 :

  • object 用于更复杂的数据结构。是一种引用数据类型 包含 RegExp正则对象、Date对象
  • Array
  • Function

区别: 基本数据类型存在栈内存中,是按值访问(即从一个变量复制基本类型的值到另一个变量后这两个变量的值是完全独立的,即使一个变量改变了也不会影响到第二个变量),可以操作保存在变量中实际的值;而引用类型的值是存在堆内存中的对象,当复制某个保存种对象的变量时,操作的是对象的引用(就像c语言的指针一样),但是操作对象的属性时,操作的是实际的对象。
4. 深拷贝是什么?有哪些深拷贝的方法?(滴滴,网易雷火)

根据上一个问题,就很容易引出深拷贝浅拷贝的问题

因为浅拷贝只在根属性上在堆内存中创建了一个新的的对象,复制了基本类型的值,但是复杂数据类型也就是对象则是拷贝相同的地址,而深拷贝则是对于复杂数据类型在堆内存中开辟了一块内存地址用于存放复制的对象并且把原有的对象复制过来,这两个对象是相互独立的,也就是两个不同的地址
深拷贝的方法:

// 1. 
 JSON.parse(JSON.stringify(...))   (这里提一嘴 strinfy不是百分百安全,它不一定会返回string,在大数和循环调用时很容易抛出错误 有这么一个库比较安全:safe-json-stringify
// 2.递归拷贝
 function deep(obj) {
     let copy = obj instanceof Array ? [] : {};
     for (let i in obj) {
     // i是obj所有还是继承得到
     // 判断对象是否包含特定的自身(非继承)属性
           if (obj.hasOwnProperty(i)) {
             copy[i] = obj[i] instanceof Object ? deep(obj[i]) : obj[i];
            }
     }
     return copy;
}

5. JS中判断数组的方法有哪些?(滴滴)

const an = ['Hello','An'];
// 1. es5的新方法
Array.isArray(an)
// 2.任何类型都可以予以判断
Object.prototype.toString.call(an)  // "[object Array]"
// 3. instanceof这个方法不准确,因为对象也会返回true;
//[]  instanceof Object; // true
// []  instanceof Array; // true
// 4. 
Array.prototype.isPrototypeOf(an)

5.1. 类数组转数组的方法有哪些?(滴滴)

先说一下什么是类数组:就是有指向对象元素的数字索引下标和 length 属性,但是没有比如 push 、shift、 forEach 以及 indexOf等数组对象具有的方法。
例如: arguments 对象,DOM API 返回的 NodeList 对象

var arr = [].slice.call(arguments)// 相当于
var arr = Array.prototype.slice.call(arguments);
ES6:
let arr = Array.from(arguments);
let arr = [...arguments];

6. 谈一谈闭包(滴滴,腾讯)
概念:闭包就是能读取其他函数内部变量的函数。
优点:

  • 避免全局变量的污染
  • 将一个变量长期存储在内存中

缺点:

  • 内存泄露
  • 常驻内存,增加内存使用量

6.1 了解作用域链吗?(腾讯)
当访问一个变量时,解释器会首先在当前作用域查找标示符,如果没有找到,就去父作用域找,直到找到该变量的标示符或者不在父作用域中,这就是作用域链。作用域链的顶端是全局对象。
它与原型链的区别是:如果原型链上找不到就返回 undefined 但是作用域链会抛出ReferenceError

7. 执行顺序,闭包的题目(字节跳动)
下面代码打印结果是什么?

for (var i = 0; i < 4; i++) {
  setTimeout(function() {
    console.log(i);
  }, 300);
}
// 4 4 4 4

如何修改使其正常打印0 1 2 3?

//方法一:
for (var i = 0; i < 4; i++) {
  setTimeout(
    (function(i) {
      return function() {
        console.log(i);
      };
    })(i),
    300
  );
}
// 或者
for (var i = 0; i < 4; i++) {
  setTimeout(
    (function() {
      var temp = i;
      return function() {
        console.log(temp);
      };
    })(),
    300
  );
}
//这个是通过自执行函数返回一个函数,然后在调用返回的函数
//去获取自执行函数内部的变量,此为闭包

//方法二:
for (var i = 0; i < 4; i++) {
  (function(i) {
    setTimeout(function() {
      console.log(i);
    }, 300);
  })(i);
}
// 方法二是通过创建一个自执行函数,使变量存在这个自执行函数的作用域里
//方法三:(
// i虽然在全局作用域声明,但是在for循环体局部作用域中使用的时候,变量会被固定,不受外界干扰。
for (let i = 0; i < 4; i++) { 
  setTimeout(function() {
    console.log(i);    //  i 是循环体内局部作用域,不受外界影响。
  }, 0);
}
// 输出结果:
0  1  2  3  4  5  6  7  8 9

8. 谈一谈你了解的Ajax(滴滴)

  • Ajax的核心使用 XMLHttpRequest (XHR)对象,通过XMLHttpRequest 构造函数来创建对象var xhr = new XMLRequestHttp()
  • XMLHttpRequest 对象有三个常用的属性: onreadystatechange 属性,readyState 属性,responseText 属性;
  • 有两个重要方法:open方法 不真正发送数据,只是准备发送数据 xhr.open(method, url, async);
  • send方法通过请求主体查传送数据时(post方法)xhr.send(data);
    不需要请求主体时(get请求)xhr.send(null);
    具体了解可以戳这里
    9. Ajax怎么传文件?(腾讯)
    使用formData对象和post方法进行传输
<input type="file" name="some" id="data"/>
<script src="https://cdn.bootcss.com/jquery/3.4.1/core.js">


// 使用原生ajax 可提交表单或者是上传文件
var data = document.getElementById('data')
let formData = new formData()
formData.append('name', data.file[0])
let xhr = new XMLHttpRequest()
xhr.open('post', url, true)
xhr.send(formData)

// 使用jq
// var data = document.getElementById('data')
// let formData = new formData()
// formData.append('name', data.file[0])
$.ajax({
	url: '',
	type: 'post',
	data: formData,
	processData: false, //让jq不要去处理发送的数据
	contentType: false, // 让jq不要设置content-type请求头
	success: function (data) {
		console.log(data)
	}
})

10. 讲一讲你了解的继承 原型链是什么?(腾讯)

每个对象都可以有一个原型_proto_,这个原型还可以有它自己的原型,以此类推,形成一个原型链。查找特定属性的时候,我们先去这个对象里去找,如果没有的话就去它的原型对象里面去,以此类推… 这个操作被委托在整个原型链上,这个就是我们说的原型链了。

  • proto,prototype,constructor
function Person( ){

}

var person = new Person();
// _proto_实例指向原型对象
// prototype 构造函数指向原型对象
// constructor指向构造函数
console.log(person._proto_ === Person.prototype); //true

console.log(Person === Person.prototype.constructor); //true

11. 介绍一下js的执行顺序(JS事件循环机制):(字节跳动,滴滴)

  • 口头阐述EventLoop
    • JS分为同步任务和异步任务,同步任务都在主线程上执行,形成一个执行栈;主线程之外,事件触发线程管理着一个任务队列,只要异步任务有了运行结果,就在任务队列之中放置一个事件。
    • 一旦执行栈中的所有同步任务执行完毕(此时JS引擎空闲),系统就会读取任务队列,将可运行的异步任务添加到可执行栈中,开始执行。
    • 异步任务的运行机制是:执行一个宏任务(栈中没有就从事件队列中获取)—> 执行过程中如果遇到微任务,就将它添加到微任务的任务队列中
      宏任务执行完毕后,立即执行当前微任务队列中的所有微任务(依次执行)—> 当前宏任务执行完毕,开始检查渲染,然后GUI线程接管渲染 —> 渲染完毕后,JS线程继续接管,开始下一个宏任务(从事件队列中获取)

注:

  • macro-task(宏任务):包括整体代码script,setTimeout,setInterval
  • micro-task(微任务):Promise,process.nextTick

不清楚可以戳这里

12. 下面代码的打印顺序以及原因(js的执行顺序)(字节跳动)

  • 代码题
console.log(1)
    setTimeout(function(){
        console.log(2);
        let promise = new Promise(function(resolve, reject) {
              console.log(7);
              resolve()
        }).then(function(){
            console.log(8)
        });
    },1000); // 注意定时器等待事件
    setTimeout(function(){
        console.log(10);
        let promise = new Promise(function(resolve, reject) {
              console.log(11);
              resolve()
        }).then(function(){
            console.log(12)
        });
    },0); // 注意定时器等待事件
    let promise = new Promise(function(resolve, reject) {
          console.log(3);
          resolve()
    }).then(function(){
        console.log(4)
    }).then(function(){
        console.log(9)
    });
    console.log(5)
   // 1  3  5  4  9  10  11  12  2  7  8

13. 了解事件冒泡和捕获吗?使用过事件委托吗?(网易雷火)
事件冒泡: 是会从当前触发的事件目标一级一级往上传递,依次触发,直到document为止。
事件捕获: 是会从document开始触发,一级一级往下传递,依次触发,直到真正事件目标为止。
事件委托: 是指事件不直接绑定到某元素上,而是绑定到该元素的父元素上,进行触发事件操作时(例如’click’),再通过条件判断,执行事件触发后的语句。可以使代码更简洁也能节省内存开销。

// 事件委托的例子
// 现在有一个ul,ul里又有100个li,我想给这100个li都绑定一个点击事件

function clickLi() {
    alert('你点击了li');}
	document.getElementById('isUl')
	.addEventListener('click', function(event) {
    // 每一个函数内都有一个event事件对象,它有一个target属性,指向事件源
    var src = event.target;
    // 我们判断如果target事件源的节点名字是li,那就执行这个函数
    // target里面的属性是非常多的,id名、class名、节点名等等都可以取到
    if(src.nodeName.toLowerCase() == 'li') {
       clickLi()}});

14. new是怎么实现的?(滴滴)
使用new命令时,它后面的函数依次执行下面的步骤:

  • 创建一个空对象,作为将要返回的对象实例。
  • 将这个空对象的原型,指向构造函数的prototype属性。
  • 将这个空对象赋值给函数内部的this关键字。
  • 开始执行构造函数内部的代码,返回新对象
function myNew (Con, ...args) {
	let obj = {};
	// 把这个空对象链接到构造函数的原型上
	obj._proto_ = Con.prototype;
	// 修改this指向
	let res = Con.apply(obj, args);
	// 如果返回的result是一个对象则返回,如果不是,则new方法失效,返回obj
	return res instanceof Object ? res : obj; 
}
function Person (name) {
	this.name = name
	
};
let person = myNew(Person, 'bob');
console.log(person.name);

15. 判断类型有哪些方法?(字节跳动)

  • typeof:可以判断出’string’,‘number’,‘boolean’,‘undefined’,‘symbol’
    判断 typeof(null) 时值为 ‘object’; 判断数组和对象时值均为 ‘object’
  • instanceof:原理是 构造函数的 prototype 属性是否出现在对象的原型链中的任何位置
  • Object.prototype.toString.call():常用于判断浏览器内置对象,对于所有基本的数据类型都能进行判断
  • Array.isArray():只能用于判断是否为数组

16. 数组去重的方法有哪些?(字节跳动)

let arr = [1,1,2,3,4,5,5,6];
// 1. indexOf
function newArr(array){ 
    //一个新的数组 
    var ar = []; 
    //遍历当前数组 
    for(var i = 0; i < array.length; i++){ 
        //如果临时数组里没有当前数组的当前值,则把当前值push到新数组里面 
        if (ar.indexOf(array[i]) == -1){ 
            ar.push(array[i])
        }; 
    } 
    return ar; 
}
let arr2 = newArr(arr);
// 2. reduce
let arr2 = arr.reduce(function(ar,cur) {
  if(!ar.includes(cur)) {
    ar.push(cur)
  }

  return ar
},[])
// 3. filter
let arr2 = arr.filter(function(item,index) {
  // indexOf() 方法可返回某个指定的 字符串值 在字符串中首次出现的位置
  return arr.indexOf(item) === index
})
// 4. Set
let arr2 = [...new Set(arr)]

下一篇在这里: 2020前端一些大厂面经系列———ES6

面经系列:
2020前端一些大厂面经系列———HTML,CSS,算法
2020前端一些大厂面经系列———JS
2020前端一些大厂面经系列———ES6
2020前端一些大厂面经系列———网络(上)
2020前端一些大厂面经系列———网络(下)
2020前端一些大厂面经系列———vue,node
2020前端一些大厂面经系列———综合实战篇

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值