总结面试题

一.说一说JS实现异步的方法?

有5种方法实现异步:回调函数、事件监听、发布/订阅、Promise、生成器Generators/yield。

1.回调函数

优点:回调函数的优点是简单、容易理解和实现。

缺点:缺点是不利于代码的阅读和维护,各个部分之间高度耦合,使得程序结构混乱、流程难以追踪(尤其是多个回调函数嵌套的情况),而且每个任务只能指定一个回调函数。

function f1(callback) {
  setTimeout(function () {
     callback();
  },1000);
}
f1(f2);

2.Promise

Promise包装了一个异步调用并生成一个Promise实例,当异步调用返回的时候根据调用的结果分别调用实例化时传入的resolve 和 reject方法,then接收到对应的数据,做出相应的处理。

优点:Promise不仅能够捕获错误,回调函数变成了链式写法,程序的流程可以看得很清楚,而且也很好地解决了回调地狱的问题。

缺点:缺点是无法取消 Promise,错误需要通过回调函数捕获。

	f1().then(f2);
 	function f1(){
 		//deferred对象就是jQuery的回调函数解决方案。
    var dfd = $.Deferred();
    setTimeout(function () {
      // f1的任务代码
		   //将dtd对象的执行状态从"未完成"改为"已完成",从而触发done()方法
      dfd.resolve();
    }, 500);
	   //返回promise对象 
	   // deferred.promise()方法。它的作用是,在原来的deferred对象上返回另一个deferred对象,
	   //后者只开放与改变执行状态无关的方法(比如done()方法和fail()方法),
	   //屏蔽与改变执行状态有关的方法(比如resolve()方法和reject()方法),
	   //从而使得执行状态不能被改变。
    return dfd.promise;
  }
 
	f1().then(f2).then(f3); //指定多个回调函数
	f1().then(f2).fail(f3); //指定发生错误时的回调函数

3.Generator 

Generator 函数是 ES6 提供的一种异步编程解决方案,Generator 函数是一个状态机,封装了多个内部状态,可暂停函数, yield可暂停,next方法可启动,每次返回的是yield后的表达式结果。

优点:优点是异步语义清晰。

缺点:缺点是手动迭代`Generator` 函数很麻烦,实现逻辑有点绕 。

function *generatorDemo() {
	 yield 'hello';
	 yield 1 + 2;
	 return 'ok';
	}
	 
	var demo = generatorDemo()
	 
	demo.next()  // { value: 'hello', done: false } 
	demo.next()  // { value: 3, done: false } 
	demo.next()  // { value: 'ok', done: ture } 
	demo.next()  // { value: undefined, done: ture }

4.事件监听

思路是采用事件驱动模式。任务的执行不取决于代码的顺序,而取决于某个事件是否发生。

优点:比较容易理解,可以绑定多个事件,每个事件可以指定多个回调函数,而且可以"去耦合"(Decoupling),有利于实现模块化

缺点:缺点是整个程序都要变成事件驱动型,运行流程会变得很不清晰。

f1.on('done', f2);
	function f1(){
    setTimeout(function () {
      // f1的任务代码
      f1.trigger('done');
    }, 1000);
  }

5.发布/订阅

发布/订阅模式,又称观察者模式。发布/订阅,性质与“事件监听类似”,但是明显优于后者,因为我们可以通过查看”消息中心“,了解存在多少信号,多少个订阅者,从而监听程序的运行。

//f2向信号中心Jquery订阅done信号
	jQuery.subscribe("done", f2);
	function f1(){
    setTimeout(function () {
      // f1的任务代码
		   //发布done信号
      jQuery.publish("done");
    }, 1000);
  }
  //f2执行完成后,取消订阅
  jQuery.unsubscribe("done", f2);

二.说一说数组去重都有哪些方法?

数组去重有这些方法:对象属性、new Set() 、indexOf、hasOwnProperty、reduce+includes、filter。

1.new Set()

利用Set类型数据无重复项:new 一个 Set,参数为需要去重的数组,Set 会自动删除重复的元素,再将 Set 转为数组返回。

优点:效率更高,代码简单,思路清晰。

缺点:可能会有兼容性问题。

var arr = [1, 2, 3,4 ,5,6, 4, 3, 8, 1]
    // 数组去重:
    // 方法4: set
    function newArrFn (arr) {
      // .new Set方法,返回是一个类数组,需要结合 ...运算符,转成真实数组
      return ([...new Set(arr)])
    }
    console.log(newArrFn(arr));

2.filter+indexof 去重

这个方法和第一种方法类似,利用 Array 自带的 filter 方法,返回 arr.indexOf(num) 等于 index 的num。原理就是 indexOf 会返回最先找到的数字的索引,假设数组是 [1, 1],在对第二个1使用 indexOf 方法时,返回的是第一个1的索引0。

优点:可以在去重的时候插入对元素的操作,可拓展性强。

var arr = [1, 2, 3,4 ,5,6, 4, 3, 8, 1]
    // 数组去重:
    // 方法6 :filter + findIndex
    function newArrFn (arr) {
      // 利用indexOf检测元素在数组中第一次出现的位置是否和元素现在的位置相等,
      // 如果相等,说明数组中没有重复的
      return Array.prototype.filter.call(arr, function (item, index) { 
        return arr.indexOf(item) === index
       })
    }
    console.log(newArrFn(arr));
var arr = [1, 2, 3,4 ,5,6, 4, 3, 8, 1]
    // 数组去重:
    // 方法6 :filter + findIndex
    function newArrFn (arr) {
      // 利用indexOf检测元素在数组中第一次出现的位置是否和元素现在的位置相等,
      // 如果相等,说明数组中没有重复的
      return arr.filter(function (item, index) { 
        return arr.indexOf(item) === index
       })
    }
    console.log(newArrFn(arr));

3.for + indexof

主要利用indexof的特性,查找元素找不到返回-1, 接下来就需要判断,如果是-1,说明没找到,就往新数组里面添加元素。

var arr = [1, 2, 3,4 ,5,6, 4, 3, 8, 1]
    // 数组去重:
    // 方法2: for + indexof
    function newArrFn (arr) {
      let newArr = []
      for(let i = 0;i<arr.length;i++){
        newArr.indexOf(arr[i]) === -1 ? newArr.push(arr[i]) : newArr
      };
      return newArr
    }
    console.log(newArrFn(arr));

4.reduce +includes

这个方法就是利用reduce遍历和传入一个空数组作为去重后的新数组,然后内部判断新数组中是否存在当前遍历的元素,不存在就插入到新数组中。

缺点:时间消耗多,内存空间也有额外占用。

var arr = [1, 2, 3,4 ,5,6, 4, 3, 8, 1]
    // 数组去重:
    // 方法12 :reduce
    function newArrFn (arr) {
      let newArr = []
      return  arr.reduce((prev, next,index, arr) => {
        // 如果包含,就返回原数据,不包含,就把新数据追加进去 
        return newArr.includes(next) ? newArr :  newArr.push(next)
      }, 0)
    }
    console.log(newArrFn(arr));
var arr = [1, 2, 3,4 ,5,6, 4, 3, 8, 1]
    // 数组去重:
    // 方法12 :reduce
    function newArrFn (arr) {
      return  arr.reduce((prev, next,index, arr) => {
        // 如果包含,就返回原数据,不包含,就把新数据追加进去 
        prev.includes(next) ? prev :  prev.push(next)
        return prev
      }, [])
    }
    console.log(newArrFn(arr));

5.sort 排序

首先利用 sort 方法进行排序。进行循环,如果原数组的第 i 项和新数组的 i - 1 项不一致,就push进去。

var arr = [1, 2, 3,4 ,5,6, 4, 3, 8, 1]
    // 数组去重:
    // 方法3: for + sort
    function newArrFn (arr) {
      arr = arr.sort()
      let newArr = []
      for(let i = 0;i<arr.length;i++){
        arr[i] === arr[i-1] ? newArr : newArr.push(arr[i])
      };
      return newArr
    }
    console.log(newArrFn(arr));

三.说一说es6中箭头函数?

箭头函数没有this、this是从外部获取、不能使用new、没有arguments、没有原型和super ,箭头函数相当于匿名函数,简化了函数定义。

由于没有this关键字所以箭头函数也不能作为构造函数,不能使用yield关键字,因此箭头函数不能用作 Generator 函数。

箭头函数函数适用场景: -简单的函数表达式,内部没有this引用,没有递归、事件绑定、解绑定,适用于map、filter等方法中。

四.说一说call apply bind的作用和区别?

相同点:call、apply、bind的作用都是改变函数运行时的this指向。

不同点:

1.bind在改变this指向的时候,返回一个改变执行上下文的函数,不会立即执行函数,但是call和apply在改变this指向的同时执行了该函数。

2.但是call和apply参数的格式不同,call是一个参数对应一个原函数的参数,但是apply第二个参数是数组,

call 的使用场景:

 1、对象的继承。如下面这个例子:

function Animal(name) {
  this.name = name;
  this.showName = function () {
    console.log(this.name);
  }
  }
function Cat(name) {
  Animal.call(this, name); 
  }
var cat = new Cat('Black Cat');
cat.showName(); 
var animal = new Animal('父元素')
animal.showName()

apply 的使用场景:

1、Math.max。用它来获取数组中最大最小的一项。

let max = Math.max.apply(null, array);
let min = Math.min.apply(null, array);

2、实现两个数组合并。在 ES6 的扩展运算符出现之前,我们可以用 Array.prototype.push来实现。

let arr1 = [1, 2, 3];
let arr2 = [4, 5, 6];
 
Array.prototype.push.apply(arr1, arr2);
console.log(arr1); // [1, 2, 3, 4, 5, 6]

五.js数组面试题

// 面试题一
// 已知有字符串foo = 'get-element-by-id',写一个函数将其转化为驼峰表示法"getElementById"

function HumpConversion() {
	let foo = "get-element-by-id"
	let arr = foo.split('-')
	for (let i = 1; i < arr.length; i++) {
		arr[i] = arr[i].charAt(0).toLocaleUpperCase() + arr[i].substring(1, arr[i].length)
	}
	arr = arr.join('')
	return arr
}
let newArr = HumpConversion()
// console.log(newArr)

// 面试题二
// 已知数组,arr = [5,2,6,8,4,1,3,7,0,9]
// 请自定义一个原生函数,使数组从小到大排序,比如ArrSort

function ArrSort(arr) {
	// let arr = [5,2,6,8,4,1,3,7,0,9]
	for (let i = 0; i < arr.length; i++) {
		for (let j = 0; j < arr.length - 1 - i; j++) {
			if (arr[j] > arr[j + 1]) {
				var temp = arr[j]
				arr[j] = arr[j + 1]
				arr[j + 1] = temp
			}
		}
	}
	return arr
	// console.log(arr);
}
ArrSort([5, 2, 6, 8, 4, 1, 3, 7, 0, 9])


// 如果数组是这样的呢?arr = ['-5,3,8,2','1,6,9,4','10,5,-3,7','11,-7,12,13']
// 请自定义一个原生函数,使数组从小到大排序,比如BubbleSort
let arr = ['-5,3,8,2', '1,6,9,4', '10,5,-3,7', '11,-7,12,13']

//把数组转化成数值型数组
function Concat(array) {
	let newArr = []
	for (let i = 0; i < array.length; i++) {
		newArr = newArr.concat(array[i].split(','))
	}
	for (let j = 0; j < newArr.length; j++) {
		newArr[j] = parseInt(newArr[j])
	}
	// console.log(newArr)
	return newArr
}

async function BubbleSort() {
	let sortArr = await Concat(arr)
	return ArrSort(sortArr)
}
BubbleSort().then(res => {
	// console.log(res);
})

// 面试题三
// 已知数组 arr = [1,2,3,4,5,6,7,8,9,10]
// 请自定义函数反转数组

function reverArr(arr) {
	for (let i = 0; i < arr.length / 2; i++) {
		var temp = arr[i]
		arr[i] = arr[arr.length - 1 - i]
		arr[arr.length - 1 - i] = temp
	}
	// console.log(arr)
}
reverArr([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])

// 面试题四
// 已知数组 arr = [8,11,20,5,20,8,0,2,4,0,8]
// 去掉数组重复的数据

// 方法一
function removal(arr) {
	let newArr = []
	for (let i = 0; i < arr.length; i++) {
		if (newArr.indexOf(arr[i]) < 0){
			newArr.push(arr[i])
		}
	}
	// console.log(newArr)
}
removal([8, 11, 20, 5, 20, 8, 0, 2, 4, 0, 8])

// 方法二
function removal2(arr){
	let newArr = []
	for(let i=0;i<arr.length;i++){
		if(!newArr.includes(arr[i])){
			newArr.push(arr[i])
		}
	}
	// console.log(newArr);
}
removal2([8, 11, 20, 5, 20, 8, 0, 2, 4, 0, 8])

// 方法三
function removal3(arr){
	let newArr = [...new Set(arr)]
	// console.log(newArr);
}
removal3([8, 11, 20, 5, 20, 8, 0, 2, 4, 0, 8])

// 方法四
function removal4(arr){
	let newArr = []
	newArr[0] = arr[0]
	for(let i=0;i<arr.length;i++){
		for(let j=0;j<newArr.length;j++){
			if(newArr[j] === arr[i] ){
				break
			}
			if(j === newArr.length-1){
				newArr.push(arr[i])
			}
		}
	}
	// console.log(newArr)
}
removal4([8, 11, 20, 5, 20, 8, 0, 2, 4, 0, 8])

六.1物理像素实现

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>1物理像素实现</title>
		<meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no"/>
		<style>
			*{
				margin: 0;
				padding: 0;
			}
			#box{
				width: 0.5rem;
				height: 0.5rem;
				border-bottom: 1px solid #000;
			}
		</style>
	</head>
	<body>
		<div id="box"></div>
	</body>
	<script>
		// 像素比 = 物理像素 / css像素
		//像素比
		let dpr = window.devicePixelRatio
		// 缩放比例/css像素
		let scale = 1 / dpr
		// 获取屏幕宽度
		let width = document.documentElement.clientWidth
		// 获取mate标签
		let meta = document.querySelector("meta[name='viewport']")
		meta.setAttribute('content',`width=device-width,initial-scale=${scale},user-scalable=no`)
		// 获取html标签
		let htmlNode = document.querySelector('html')
		htmlNode.style.fontSize = width * dpr + 'px'
	</script>
</html>

七.实现移动端rem适配

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no"/>
		<title>实现移动端rem适配</title>
		<style>
			*{
				margin: 0;
				padding: 0;
			}
			.box{
				width: 0.5rem;
				height: 0.5rem;
				background-color: red;
				font-size: 0.05rem;
				color: #fff;
				text-align: center;
				line-height: 0.5rem;
			}
		</style>
	</head>
	<body>
		<div class="box">实现移动端rem适配</div>
	</body>
	<script>
		window.onload = function(){
			let width = document.documentElement.clientWidth;
			let htmlNode = document.querySelector('html');
			htmlNode.style.fontSize = width + 'px'
		}
	</script>
</html>

八.什么是闭包?

闭包是指在一个函数内部定义的函数,它可以访问外部函数的变量和参数,即使外部函数已经返回。闭包可以用来创建私有变量和方法,以及实现模块化和高阶函数等功能。以下是一个使用闭包实现私有变量和方法的示例:

function createCounter() {
  let count = 0;

  return {
    increment() {
      count++;
    },

    decrement() {
      count--;
    },

    getCount() {
      return count;
    }
  };
}

const counter = createCounter();

console.log(counter.getCount()); // 0

counter.increment();
counter.increment();

console.log(counter.getCount()); // 2

counter.decrement();

console.log(counter.getCount()); // 1

在这个示例中,createCounter函数返回一个包含三个方法的对象。这些方法可以访问createCounter函数中定义的count变量,但是外部代码无法直接访问count变量。这样,我们就创建了一个私有变量和方法,可以避免全局命名冲突和数据污染的问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值