js相关面试点-2021

182 阅读7分钟

1:js运行机制

js离不开用户的操作, js原理运行机制:单线程---->同一时间做同一件事

2:arguments的对象是什么

在JavaScript中,arguments属于当前对象的一个内置属性,arguments非常类似于Array对象,但是又不是实例的Array。 有属性无方法,类数组对象 箭头函数无arguments 转换为数组:

1:array.prototype.slice.call(arguments)
2:...

3:在调用这个函数时,代码中’b‘为啥会变成全局变量

function fun() {
    //let a = b = 0
    //转换为下面
    let a=(b=0)
    //从右往左执行,在为b赋值0的时候,因为没有b,会在引擎里创建b。又因为b没有声明,所以b会变成全局变量
}

4:哪些操作会造成内存泄露

1:闭包 2:意外的全局变量 3:被遗忘的定时器 4:脱离dom的引用(获取某div,然后它被清除了,但内存中仍然保存着对它的引用)

5:高阶函数

高阶函数:将函数作为参数的函数,或者将函数作为返回值的函数

function highOrder(params, callback) {
    return callback(params)
}

6:var let和const的区别

  • 使用var声明的变量,其作用域为该语句所在的函数内,且存在变量提升现象;
  • 使用let声明的变量,其作用域为该语句所在的代码块内,不存在变量提升;
  • 使用const声明的是常量,在后面出现的代码中不能再修改该常量的值。 更深入了解的话请看我以前写的文章: var let和const的区别 ES6的const

7:promise的概念和相关用法

1、Promise的含义 所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。

企业开发中为了保证异步代码的执行顺序,那么就会出现回调层层嵌套,如果回调函数嵌套的层数太多就会导致代码的阅读性,可维护性大大降低,promise对象可以将异步操作同步流程来表示,避免了回调函数层层嵌套(回调地狱)

2、 Promise对象的基本特点

(1)对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:pending(进行中),fulfilled(已成功)和 rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。

(2)一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从 pending 变为 fulfilled 和从 pending 变为rejected 。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果。如果改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。

3 .Promise的基本用法 我们通过console.dir(Promise)在终端打印一下,如下图所示

image.png

可以看到Promise是一个构造函数,他本身有 all, reject, resolve 方法,在他的原型上有 then, catch方法.

下面来创建一个Promise实例:

/*
 *代码利用setTimeout函数实现异步操作,2秒生成一个数字,通过异步操作结果确定执行resolve函数还是reject函数
 */
function getNumber(){
    return new Promise(function(resolve, reject) {
        setTimeout(function(){
        var number = Math.ceil(Math.random()*10);
        if(number <= 5){
            resolve(number)
        }
        else{
            reject('数字太大了');
        }
      }, 2000)  
    })
}
getNumber()
    .then(function(data){
          console.log('resolve');
          console.log(data);
    },function(reason){
          console.log('reject');
          console.log(reason);
    }
)
  • Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject。
  • resolve函数的作用,将Promise对象的状态从“未完成”变成“成功”(即从Pending变为Resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;
  • reject函数的作用是,在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。
  • then方法可以接受两个回调函数作为参数,第一个对应resolve的回调,第二个对应reject的回调。

Promise对象除了then方法,还有一个catch方法.它和then的第二个参数一样,用来指定reject的回调.用法如下:

getNumber()
    .then(function(data){
        console.log('resolved');
        console.log(data);
    })
    .catch(function(reason){
        console.log('rejected');
        console.log(reason);
    });

它还有另外一个作用:在执行resolve的回调(也就是上面then中的第一个参数)时,如果抛出异常了(代码出错了),那么并不会报错卡死js,而是会进到这个catch方法中
栗子如下:

getNumber()
    .then(function(data){
        console.log('resolved');
        console.log(data);
        console.log(odata); 
    })
    .catch(function(reason){
        console.log('rejected');
        console.log(reason);
    });

image.png

如果要在三个异步操作执行完成后再执行某操作,那么就需要用到all方法. 如下:

function runAsync1(){
    var p = new Promise(function(resolve, reject){
        setTimeout(function(){
            var number = Math.ceil(Math.random()*10);
            console.log('1执行完成');
            resolve(number);
        }, 2000)
    });
    return p;
}
function runAsync2(){
    var p = new Promise(function(resolve, reject){
        setTimeout(function(){
            var number = Math.ceil(Math.random()*10);
            console.log('2执行完成');
            resolve(number);
        }, 2000)
    });
    return p;
}
function runAsync3(){
    var p = new Promise(function(resolve, reject){
        setTimeout(function(){
            var number = Math.ceil(Math.random()*10);
            console.log('3执行完成');
            resolve(number);
        }, 2000)
    });
    return p;
}
Promise.all([runAsync1(), runAsync2(), runAsync3()])
        .then(function(result){
            console.log(result);
        })

image.png

Promise的all方法提供了并行执行异步操作的能力,并且在所有异步操作执行完后才执行回调

8:change,input,blur区别(change和input是如何实现的)

  • keydown事件发生时,输入值并没有发生变化,所以keydown可用于阻止某些输入字符的显示。

  • input事件发生时,无法获取到keyCode值,并且紧随在keydown事件之后。

  • keyup事件最后发生,一次键盘敲入事件彻底完成。

  • change事件只会发生在输入完成后,也就是输入框失去焦点之前。

  • 输入完成后观察, 当用户完成所有的输入时,这时候必定会发生blur事件,只有这时才会触发change,所以可用blur与change监听用户输入是否完成,输入框的验证多半发生在此时。

  • 输入框发生的事件流程依次为focus、keydown、input、keyup、change与blur,见下图所示。 image.png

ps:blur与change事件在绝大部分的情况下表现都非常相似,输入结束后,离开输入框,会先后触发change与blur,唯有两点例外。

1、没有进行任何输入时,不会触发change

在这种情况下失焦后,输入框并不会触发change事件,但一定会触发blur事件。在判断表单的修改状态时,这种差异会非常有用,通过change事件能轻易地找到哪些字段发生了变更以及其值的变更轨迹。

2、输入后值并没有发生变更

这种情况是指,在没有失焦的情况下,在输入框内进行的删除与输入操作,但最终的值与原值一样,这种情况下失焦后,keydown、input、keyup、blur都会触发,但change依旧不会触发。

9:去重的方法有哪些

1、ES6的new Set()

let arr = [1, 1, 1, 3, 3, 2, 5, 5, 5];
let set = new Set(arr);
console.log(set);  // Set { 1, 3, 2, 5 }
console.log([...set]);  // [ 1, 3, 2, 5 ]
console.log(Array.from(set));  // [ 1, 3, 2, 5 ]

详见 ES6新增数据结构Set和Map

2、双重for循环

let arr = [1, 1, 1, 3, 3, 2, 5, 5, 5];
for (let i=0, len=arr.length; i<len; i++) {
  for (let j=i+1; j<len; j++) {
      if (arr[i] == arr[j]) {
          arr.splice(j, 1);
          // splice 会改变数组长度,所以要将数组长度 len 和下标 j 减一
          len--;
          j--;
      }
  }
}
console.log(arr);

3、Array.sort() 使用sort()将数组进行排序,比较相邻元素是否相等,从而排除重复项

let arr = [1, 1, 1, 3, 3, 2, 5, 5, 5];

arr.sort((a, b) => a - b);
let result = [arr[0]]
for (let i=1, len=arr.length; i<len; i++) {
  if (arr[i] !== arr[i-1])
    result.push(arr[i]);
}
console.log(result);

4、for…of + Object 利用对象的属性不能相同

let arr = [1, 1, 1, 3, 3, 2, 5, 5, 5];
let obj = {};
let result = [];
for (let i of arr) {
    if (!obj[i]) {
        result.push(i);
        obj[i] = 1;
    }
}
console.log(result);

5、for…of + includes()

let arr = [1, 1, 1, 3, 3, 2, 5, 5, 5];
let result = [];
for (let i of arr) {
    if (!result.includes(i)) {
        result.push(i);
    }
}
console.log(result);

6、Array.filter() + indexOf

let arr = [1, 1, 1, 3, 3, 2, 5, 5, 5];
let result = arr.filter((item, index)=> {
  return arr.indexOf(item) === index; 
})
console.log(result);

10:Array.from和new Set(arr))有哪些坑

1:

  • Array.from()方法就是将一个类数组对象或者可遍历对象转换成一个真正的数组。但是它会受到length属性的影响。无length属性的对象就不行。

- 缺点:

1、该类数组对象必须具有length属性,用于指定数组的长度。如果没有length属性,那么转换后的数组是一个空数组。

2、该类数组对象的属性名必须为数值型或字符串型的数字

ps: 该类数组对象的属性名可以加引号,也可以不加引号

2:

new Set()对数组进行去重,但是返回的不是一个正常的数组,而是一个类数组结构。

11:深浅拷贝区别及实现方式

浅拷贝:创建一个新对象,有着原始对象属性值的一份精确拷贝。 如果属性是基本类型,拷贝的是基本类型的值。 如果属性是引用类型,拷贝的就是内存地址,如果其中一个对象改变了这个地址,就会影响到另一个对象

深拷贝:将一个对象从内存中完整的拷贝一份出来,放到从内存中新开辟的区域,且修改新对象不影响原对象

image.png

详见--深拷贝和浅拷贝

12:数组常用的方法介绍和用法,map,every,some等

1、filter:把符合条件的数组项返回,形成一个数组

var arrayObj = [15,3,2,6,7,1,9,10];
var result = arrayObj.filter(function(item,index,array){
return item > 7;
});//[15,9,10]

2、forEach() 用于遍历数组无返回值,会改变原来数组中的值

let arr = [1, 3, 12, 2, 20, -1, 6, 17];
arr.forEach((item, index, array) => {
    array[index] = item * 2;
});
console.log(arr);//	[2, 6, 24, 4, 40, -2, 12, 34]

3、map() 用于遍历数组,返回处理之后的新数组

const arr = [1, 3, 12, 2, 20, -1, 6, 17];
const newArr = arr.map((item, index, array) => item * 2);
console.log(arr);	//[1, 3, 12, 2, 20, -1, 6, 17]
console.log(newArr);//	[2, 6, 24, 4, 40, -2, 12, 34]

4、every() 用于判断数组中的每一项元素是否都满足条件,返回一个布尔值

const arr = [1, 3, 12, 2, 20, -1, 6, 17];
const bool = arr.every((item, index, array) => item < 12); 
console.log(bool); // false

5、some() 用于判断数组中是否存在满足条件的元素,返回一个布尔值

const arr = [1, 3, 12, 2, 20, -1, 6, 17];
const bool = arr.some((item, index, array) => item < 12); 
console.log(bool); // true

13:实现数组的交集和并集

let a = new Set([1, 2, 3]);
let b = new Set([3, 4, 5]);
 
let c = new Set([...a, ...b])
console.log(c) //1,2,3,4,5并集
 
let d = new Set([...a].filter(x => b.has(x)));
console.log(d); //3交集

14:如果有两个变量,在没有第三个变量时候,如何相互赋值

方法一,使用es6解构赋值

var a = 111
var b = 222
[a,b] = [b,a] // [222,111]

a = [a,b][0]
b = [a,b][1]
console.log(a); // 222
console.log(b);	// 111

方法二,上一种方式的简写

var a = 111
var b = 222

a = [b,b=a][0] // 数组内从左向右开始赋值,第一个b=222,到了第二个b时,b=a=111。然后再取下标为0b赋值给aa=b=222

console.log(a); // 222
console.log(b);	// 111

第三种方法,通过数值计算

通过两个数值变量的总和来计算出每一个变量的值然后再进行赋值。 因为第三种方法,因为是通过数值计算来达到变量交换,所以只能局限为进行数值类型的变量交换,如果变量交换类型非数值类型则会对交换的结果产生影响或者NaN。

var a = 111
var b = 222

a += b // a = a + b // 111 = 111 + 222,此时a为333
b = a - b // 222 = 333 - 222,		   此时b为111
a -= b // a = a -b // 333 = 333 - 111, 此时a为222

console.log(a); // 222
console.log(b);	// 111

以下为错误:---------------
var a = 1
var b = '哈哈'

a += b
b = a - b 
a -= b

console.log(a);// NaN
console.log(b);// NaN

15:ES5和ES6用过哪些方法

es5:

• ES5 JSON扩展

JSON.parse JSON.stringify

ES5 Object扩展

Object.create Object.keys

Date对象

Date.now

ES5 Function扩展

Function.bind

ES5 String扩展

String.trim

ES5 数组方法扩展

Array.isArray Array.forEach Array.map Array.filter Array.some Array.every Array.indexOf Array.lastIndexOf Array.reduce Array.reduceRight

es6:

1. let/const 特性

2. 箭头函数

  • 没有this、super、arguments和new.target绑定
  • 不能通过new关键字调用
  • 没有原型
  • 不可以改变this的绑定
  • 不支持arguments对象
  • 不支持重复的命名参数

3. 字符串

.includes()判断字符串中是否包含某些字符串的时候 startWith() :如果在字符串的起始部分检测到指定文本则返回true

endsWith() :如果在字符串的结束部分检测到指定文本则返回true

4.模板字符串

单反引号

5.展开运算符

...

6.类 class

7. Set/Map

8. Promise

9.Async Await

16:class有了解过吗

详见。。写了再放