面试题-js基础

判断 js 类型的方式

1. typeof

可以判断出'string','number','boolean','undefined','symbol'
但判断 typeof(null) 时值为 'object'; 判断数组和对象时值均为 'object'

2. instanceof

原理是 构造函数的 prototype 属性是否出现在对象的原型链中的任何位置

function A() {} 
let a = new A();
a instanceof A //true,因为 Object.getPrototypeOf(a) === A.prototype;

3. Object.prototype.toString.call()

常用于判断浏览器内置对象,对于所有基本的数据类型都能进行判断,即使是 null 和 undefined

4. Array.isArray()

用于判断是否为数组

map与forEach区别

参数相同

currentValue 必须。当前元素的值
index 可选。当前元素的索引值
arr 可选。当前元素属于的数组对象

 

 

 

 

作用相同:遍历数组

forEach:调用数组的每个元素,并将元素传递给回调函数。

注意: forEach() 对于空数组是不会执行回调函数的。允许callback更改原始数组的元素,forEach()不会返回数据。

forEach不支持 continue,用 return false 或 return true 代替

forEach不支持 break,用 try catch/every/some 代替

map:返回一个新数组,数组中的元素为原始数组元素调用函数处理后的值。

注意: map() 按照原始数组元素顺序依次处理元素,不会对空数组进行检测,不会改变原始数组。

 

addEventListener()第三个参数

1 这个参数设计到事件的捕获与冒泡,为true时捕获,false时冒泡。

捕获:从外面往里面触发事件:将每个监听事件的第三个参数设置为false,也就是默认的值。一般用的冒泡比较多

冒泡:从里面往外面触发事件:将每个监听事件的第三个参数设置为true。

2 事件流

页面中点击一个元素,事件是从这个元素的祖先元素中逐层传递下来的,这个阶段为事件的捕获阶段。

当事件传递到这个元素之后,又会把事件逐成传递回去,直到根元素为止,这个阶段是事件的冒泡阶段。

3  e.currentTarget  e.target

var ul = document.querySelector('ul')
ul.addEventListener('click', function(e) {
    e = e || window.event; // IE兼容
    console.log(this); // this返回绑定事假的对象--ul
    console.log(e.currentTarget)// 返回绑定事假的对象--ul
    console.log(e.target) // 返回触发事件的对象 ---li
})

 

数组去重

1.ES6 的 Set

let arr = [1,1,2,3,4,5,5,6]
let arr2 = [...new Set(arr)]

 

let const var区别

1 var是ES5提出的;let和const是ES6提出的。

2 let和var声明的是变量,声明之后可以更改,声明时可以不赋值;const声明的是常量,必须赋值;

  const 一旦声明必须赋值,不能使用null占位。声明后不能再修改,如果声明的是复合类型数据 ,数组,对象,可以修改其属性

3 var允许重复声明变量,后一个变量会覆盖前一个变量;let和const在同一作用域不允许重复声明变量,会报错。

4 var声明的变量存在变量提升(将变量提升到当前作用域的顶)。即变量可以在声明之前调用,值为undefined。

   let和const不存在变量提升。即它们所声明的变量一定要在声明后使用,否则报ReferenceError错。

5 var不存在块级作用域。let和const存在块级作用域。

 

常用es6功能

模板字符串 `` 和模板变量 ${ } :

解构赋值

const {a,c} = obj
const [x,y,z] = arr

箭头函数

for-of(用来遍历数据—例如数组中的值。)
ES6 将 Promise 对象纳入规范,提供了原生的 Promise 对象。
增加了 let 和 const 命令,用来声明变量。
引入 module 模块的概念  模块化 类  export class

 

== 和 ===的区别

==是非严格意义上的相等    值相等就相等

===是严格意义上的相等,会比较两边的数据类型和值大小     值和引用地址都相等才相等

 

箭头函数与普通函数的区别?

箭头函数不绑定this,会捕获其所在的上下文的this值,作为自己的this值,,,任何方法都改变不了其指向,如 call(),apply() bind()

箭头函数没有原型属性

不可以当作构造函数,也就是说,不可以使用 new 命令,否则会抛出一个错误

不可以使用 arguments 对象,该对象在函数体内不存在。如果要用,可以用 Rest 参数代替

不可以使用 yield 命令,因此箭头函数不能用作 Generator 函数

 

变量的生命周期

当一个变量的生命周期结束之后,它所指向的内存就会被释放。

js有两种变量,局部变量和全局变量,局部变量是在他当前的函数中产生作用,

当该函数结束之后,该变量内存会被释放,全局变量的话会一直存在,直到浏览器关闭为止。

 

attribute 和 property 的区别是什么?

对属性Property可以赋任何类型的值,而对特性Attribute只能赋值字符串!

attribute 是 dom 元素在文档中作为 html 标签拥有的属性

property 就是 dom 元素在 js 中作为对象拥有的属性。

property能够从attribute中得到同步;attribute不会同步property上的值

更改property和attribute上的任意值,都会将更新反映到HTML页面中

 

 

 

数组(array)方法

map : 遍历数组,返回回调返回值组成的新数组
forEach : 无法 break ,可以用 try/catch 中 throw new Error 来停止
filter : 过滤
some : 有一项返回 true ,则整体为 true
every : 有一项返回 false ,则整体为 false
join : 通过指定连接符生成字符串
push / pop : 末尾推入和弹出,改变原数组, 返回推入/弹出项
unshift / shift : 头部推入和弹出,改变原数组,返回操作项
sort(fn) / reverse : 排序与反转,改变原数组
concat : 连接数组,不影响原数组, 浅拷贝
slice(start, end) : 返回截断后的新数组,不改变原数组
splice(start,number,value…): 返回删除元素组成的数组,value 为插入项,改变原数组
indexOf / lastIndexOf(value, fromIndex) : 查找数组项,返回对应的下标
reduce / reduceRight(fn(prev, cur) ,defaultPrev) : 两两执行,prev 为上次化简函数的return 值,cur 为当前值(从第二项开始)

 

基本类型与引用类型

基本数据类型

Number、String、Boolean、Null、Undefined、Symbol、bigInt

引用数据类型

object、Array、Date、Function、RegExp

基本类型:就是值类型,在栈中申请空间,变量所对应的内存区域存储的是值

引用类型:就是地址类型,先在堆中申请空间存放数据,再把堆区的地址赋给arr,变量所对应的内存区域存储的是地址

 

 

浅拷贝与深拷贝

赋值:一个对象赋值给一个新的变量时,赋的其实是该对象的在栈中的地址,而不是堆中的数据

var arr1 = new Array(1,3,4)

Var arr2 = arr1;// 这就是一个最简单的赋值 ,arr1与arr2的在内存中存的是同样的地址

 

深拷贝和浅拷贝主要是针对引用类型(数组和对象)

基本类型赋值时,赋的是数据(所以,不存在深拷贝和浅拷贝的问题)

引用类型赋值时,赋的值地址(就是引用类型变量在内存中保存的内容)

浅拷贝:会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。

如果属性是基本类型,拷贝的就是基本类型的值;

如果属性是内存地址(引用类型),拷贝的就是内存地址 

只拷贝了一层,如果有更深一层,还是拷贝了地址

方法1 Object.assign()  

var obj = { a: {xx: "啊啊啊"},b: 1 };
var newObj = Object.assign({}, obj);
newObj.a.xx = "不不不";
console.log(obj.a.xx); //  不不不

方法2 concat()

let arr = [1, 3, {
   username: 'xx'
}];
let arr2=arr.concat();    

方法3 slice() 

let arr = [1, 3, {
   username: ' kobe'
}];
let arr3 = arr.slice();

  

深拷贝:会创建一个新对象,复制了所有层级

方法1: 递归复制

function deepClone(obj) {
    let objClone = Array.isArray(obj) ? [] : {};
    if(obj && typeof obj === "object") {
        for(key in obj) {
            if(obj.hasOwnProperty(key)) {
                 // 判断 obj 是否是对象,如果是,递归复制
                 if(obj[key] && typeof obj[key] === "object") {
                      objClone[key] = deepClone(obj[key]);
                 }else{
                      // 如果不是
                      objClone[key] = obj[key];
                 }
            }
        }
    }      
    return objClone
}     

 

方法2:JSON对象的parse和stringify

 

JSON.parse(JSON.stringify)深拷贝的问题  

JSON.parse(JSON.stringfy(X)),其中X只能是Number, String, Boolean, Array, 扁平对象,即那些能够被 JSON 直接表示的数据结构。

 

function deepClone(obj) {
  const objStr = JSON.stringify(obj);
  const target = JSON.parse(objStr);
  return target;
}

 

方法3 JQ的extend方法

$.extend( [deep ], target, object1 [, objectN ] )

deep表示是否深拷贝,为true为深拷贝,为false,则为浅拷贝

target Object类型 目标对象,其他对象的成员属性将被附加到该对象上。

object1  objectN可选。 Object类型 第一个以及第N个被合并的对象。 

 

防抖和节流

防抖
1.函数防抖 : 单位时间内,频繁触发事件,只会触发最后一次
2.经典场景 :
(1)输入框实时输入 oninput
(2)减少触发输入的频率,提高代码性能

3.防抖流程 :
(1)声明一个全局timeID存储定时器id
(2)每一次触发事件,先清除上一次定时器,以本次触发为准
(3)开启本次定时器


节流
1.函数节流 : 单位时间内,频繁触发事件,只会触发一次
2.应用场景 : 降低高频事件触发频率
(1)鼠标移动: onmousemove
(2)滚动条事件: onscroll
3.节流流程
(1)声明一个全局变量记录 本次触发时间
(2)每一次触发事件的时候, 获取当前时间
(3)判断 当前时间 - 上一次触发事件 >= 节流间隔
(4)存储本次触发事件,用于下一次判断

防抖:指触发事件后在 n 秒内函数只能执行一次,如果在 n 秒内又触发了事件,则会重新计算函数执行时间。

思路:每次触发事件时都取消之前的延时调用方法:

function debounce(fn) {
     let timeout = null; // 创建一个标记用来存放定时器的返回值
     return function () {
       clearTimeout(timeout); // 每当用户输入的时候把前一个 setTimeout clear 掉
       timeout = setTimeout(() => { // 然后又创建一个新的 setTimeout, 这样就能保证输入字符后的 interval 间隔内如果还有字符输入的话,就不会执行 fn 函数
         fn.apply(this, arguments);
       }, 500);
     };
   }
   function sayHi() {
     console.log('防抖成功');
   }

   var inp = document.getElementById('inp');
   inp.addEventListener('input', debounce(sayHi)); // 防抖

 

节流:指连续触发事件,但在 n 秒内只会执行一次,所以节流会稀释函数的执行频率。

思路:每次触发事件时都判断当前是否有等待执行的延时函数。

function throttle(fn) {
     let canRun = true; // 通过闭包保存一个标记
     return function () {
       if (!canRun) return; // 在函数开头判断标记是否为 true,不为 true 则 return
       canRun = false; // 立即设置为 false
       setTimeout(() => { // 将外部传入的函数的执行放在 setTimeout 中
         fn.apply(this, arguments);
         // 最后在 setTimeout 执行完毕后再把标记设置为 true(关键) 表示可以执行下一次循环了。当定时器没有执行的时候标记永远是 false,在开头被 return 掉
         canRun = true;
       }, 500);
     };
   }
   function sayHi(e) {
     console.log(e.target.innerWidth, e.target.innerHeight);
   }
   window.addEventListener('resize', throttle(sayHi));

 

使用Rxjs的操作符可以轻松实现防抖节流

posted @ 2022-03-18 16:56  litiyi  阅读(50)  评论(0编辑  收藏  举报