js面试

·  阅读 245

一.数据类型(6+1)

  • 基本数据类型:undefined、null、Boolean、numbe、string、symbol
  • 引用数据类型:object,里面又细分为function、Array、Date、正则等。
  • 基本数据类型:占据空间小、大小固定,属于被频繁使用数据,所以放入栈中存储。
  • 基本类型一般不可改变,值改变时,地址也改变,所以不是在改变原值,而是产生了新值。
  • 引用数据类型同时存放在栈和堆中,引用数据类型在栈中存储了指针(变量名),该指针指向堆中该实体的起始地址(变量值)
  • 引用数据类型值可以改变的,是在原值基础上修改。
  • 引用数据类型变量复制的时候,复制的是同一个指针地址,共享同一个堆实体。

1.1 基本数据类型

  • typeof 可以判断所有的值类型数据,返回的都是字符串

  • 常见的值类型数据有:number,string,boolean,undefined,symbol,返回都是对应的类型(小写)

  • 可以判断常见的引用类型数据:object,array,null.返回的都是 object

  • 判断函数 function 返回的是 function

1.undefined

  • 声明后变量的没有初始化就是undefined,是唯一值;
  • 特殊情况是即使是没有声明变量a,typeof a ;//undefined

2. null类型

  • 空对象指针,表现为检验typeof null会返回'Object';

  • 在定义将来要保存的对象时建议用null初始化;

  • null是个假值;所以,null == undefined ;为true。

  • 一律用===,只有判断是不是等于 null 或 undefined 才用==。

  • null==0 ;//false

  • !null==true;//true 常用于if判断

3.Boolean

所有数据类型都可以转化为布尔值。数据转化为布尔值的规律:只有0/NaN/null/undefined五个值是false,其余全为true

4.number

5.string

  • 1、toString(): 除了null和undefined值没有tostring()方法,其他值都有这个方法,该方法返回字符串的一个副本。

  • 2.String():如果不确定一个值是null或undefined,可以使用String()转型函数,始终会返回相应类型值的字符串.

  • 3.使用+" ": 即可以通过要转换的值 + 空字符串(" "),也可以实现转换。

  • ES6新增的模板字面量使用反引号包裹,它的作用是保留换行字符,可以跨行定义字符串,可以通过${}插值。

1.2 object引用数据类型

Object,即对象,是一组数据和功能的集合。

1.3 隐式转化

  • 1转成String类型---- 1.字符串连接符(+)转成字符串。
  • 2.转成Number类型(数学运算中)--判断相等吗?大于吗,一般都要转化成数字进行比较
  • 3.转成Boolean型(逻辑判断中)--一般if()里面写上null就不执行,写上{}、[]、!null都会执行

1.4 数据类型检测(四种)

1.typeof基本数据类型检测

  • typeof [val] 检测基本类型值还是很准确的,返回当前值对应的数据类型(小写字符串),但不能检测基本数据类型的null值,typeof null =>"object" 【01开头存储的是对象。】;

  • typeof 无法对对象数据类型细分,返回的都是 "object";

2.[val] instanceof 类

  • 判断当前类是否在这个被检测数据的原型链上。
  • 对于数组、正则、对象,函数可以细分一下,但是无法检测基本数据类型
xxx instanceof Object/Array/Function
复制代码

3.constructor

[val].constructor === 类;因为获取实例的constructor实际上获取的是直接所属的类,判断比较准确

4.Object.prototype.toString.call([val])

  • 在其它数据类型的内置类原型上有toString,但是都是用来转换为字符串的,只有Object基类原型上的toString是用来检测数据类型的。
console.log(Object.prototype.toString.call(a));//[object Array]
复制代码

1.5 如何判断数组和对象?isArray、instanceof、constructor、toString

//方法一:ES6中通过Array.isArray()识别
Array.isArray([])//true
Array.isArray({})//false

//方法二:通过 `实例 instanceof 类`:
console.log({} instanceof Array)//false
console.log(['111','222'] instanceof Array)//true

//方法三:constructor
console.log([].constructor.name==='Array')//true
console.log({}.constructor.name==='Object')//true

//方法四:通过Object.prototype.toString.call()
Object.prototype.toString.call({})  //"[object Object]"
Object.prototype.toString.call([])  //"[object Array]"
复制代码

二.变量提升

  • 变量提升机制可以使变量先使用,后声明。var和function定义的变量存在变量提升机制,var是先声明,再初始化。function是声明和初始化一起进行。
  • var : 可以重复定义;变量提升;定在单独{}中的依然是全局变量。
  • let : 同一作用域不能重复定义;没有变量提升;定在单独{}中的是局部变量。
  • const :定义常量,const仅保证指针不发生改变,所以定义的基本数据类型不可修改;但是引用数据类型可以被修改,
  • let /const/function会把当前所在的大括号(除函数之外)作为一个全新的块级上下文,应用这个机制,在开发项目的时候,遇到循环事件绑定等类似的需求,无需再自己构建闭包来存储,只要基于let的块作用特征即可解决

三. 函数执行过程

  • 形成私有上下文--》进栈执行---》一列列操作(初始化this、形参赋值、变量提升、代码执行)---》执行完后释放私有上上下文(执行环境栈)
  • 若果当前私有上下文的某个东西(一般是一个堆)被上下文以外的事物占用了,则不会出战释放,形成不销毁的上下文.也就是闭包。

3.1 高阶函数与闭包

高阶函数有两种形式:(1)、如果一个函数可以接受函数作为它的参数,那么就把这个函数称之为高阶函数。(2)、如果一个函数的返回值是一个函数,那么这个函数就是高阶函数。

3.2 闭包

  • 如果一个函数的作用域(A)有权访问另一个函数作用域(B)中的变量,那么我们就可以认为被访问的函数(B)是闭包。通常是嵌套函数中实现的。
  • 闭包的作用:延伸了变量的作用范围,fn外面的作用域可以访问fn里面的作用域
  • 闭包应用1
      const li = document.getElementsByTagName("li");

      for (var i = 0; i < li.length; i++) {
        (function (i) {
          li[i].onclick = function () {
            console.log(i);
          };
        })(i);
      }
复制代码

3.3 堆栈内存

  • 栈内存是代码执行环境栈,用来执行代码和存储基本类型值的(创建的变量也存栈里面了);
  • 栈内存有全局执行上下文、函数执行形成的私有上下文、块级作用域
  • 堆内存:用来存储引用数据类型值的 , 浏览器会把内置的属性和方法放到一个单独的内存中;然后在栈中存储一个指针指向它。
  • 堆内存销毁如果堆内存用完后想释放可以让变量指向空指针对象null。
  • 栈内存销毁全局栈内存:关掉页面的时候才会销毁;一般情况下,函数只要执行完成,形成的私有栈内存就会被销毁释放掉,但是如果是闭包函数,可能不会被立马销毁。

四.一些运算符

4.1 自增自减++i和i++

++i是先自增然后参与运算; i++是先运算,再自增

4.2 相等和全等

  • js中一律用===,只有判断是不是等于 null 或 undefined 才用==。

4.3 逻辑运算符||(或),&&(与),!(非)

逻辑短路现象:

//与运算--- 条件A && 条件B
如果条件A不成立, 那么就返回条件A
如果条件A成立, 无论条件B是否成立, 都会返回条件B
短路现象----一假则假, 所以只要条件A是假, 那么条件B就不会运算,综合结果结果就是为假


//条件A || 条件B
如果条件A成立, 那么就返回条件A。
如果条件A不成立, 无论条件B是否成立, 都会返回条件B。
因为在逻辑或运算中,有一个逻辑短路现象:由于逻辑或运算的规则是一真则真, 所以只要条件A是真, 那么条件B就不会运算
复制代码

4.4 剩余/扩展运算符(...)

  • 剩余运算符就是使用三个点...把项拆开,可以拆开字符串、数组、对象。
  • 剩余参数是将多余的参数保存到数组中;扩展运算符是将数组每项拆开
  • 1.数组拷贝(copy)
var arr = [1, 2, 3];
 var arr2 = [...arr]; 
     arr2.push(4);
 console.log(arr, arr2);//[1, 2, 3]  [1, 2, 3, 4]
复制代码
  • 2.连接数组
var arr1 = [0, 1, 2];
var arr2 = [3, 4, 5];
//var arr3 = arr1.concat(arr2);和下面一句一样
var arr3 = [...arr1, ...arr2];
//或者
arr1.push(...arr2)
复制代码
  • 3.函数调用时用展开语法展开实参。
  • 4.作为剩余参数--当函数的实参个数大于形参个数,我们可以把多余的参数放到一个数组中,给这个数组加...
  • 5.将类数组转化为真正的数组--元素集合、实参列表,【其实利用数组的Array.from(arguments)也可以】

五.逻辑结构-if、switch、while、do...while...、for

  • 1.if 用于区间判断;
  • 2.可列举的固定值判断可以用switch;
        let day = 7;
        switch (day) {
            case 1:
                console.log("星期1");
                break;
            case 2:
                console.log("星期2");
                break;
            case 3:
                console.log("星期3");
                break;
            default:
                console.log("Other");//执行这个打印other
                break;
        }
复制代码
  • 3. while 循环语法--找到0-100之间能被7整除的
while(条件表达式){
        条件满足执行的语句;
    }
复制代码
  • 4.do...while 循环--输入密码验证
 do{
        需要重复执行的代码;
    }while(条件表达式);
    
//无论条件表达式是否为真, 循环体都会被执行一次    

      let pwd;
      do {
        pwd = prompt("请输入正确的密码");
      } while (pwd !== "123456");
      alert("欢迎回来");
复制代码
  • 5.for 循环
1.for循环的格式
    for(初始化表达式;条件表达式;循环后增量表达式){
        需要重复执行的代码;
    }

2.for循环的特点
    for循环的特点和while循环的特点一样, 只有条件表达式为真, 才会执行循环体

3.for循环的执行流程
    3.1首先会执行初始化表达式, 并且只会执行一次
    3.2判断条件表达式是否为真, 如果条件表达式为真, 就执行循环体
    3.3执行完循环体就会执行循环后增量表达式
    3.4重复3.2~3.3, 直到条件表达式不为真为止
复制代码
      //正三角
      for (let i = 0; i <= 5; i++) {
        for (let j = 0; j < i; j++) {
          document.write("❤");
        }
        document.write("<br />");
      }
      //倒三角
      for (let m = 0; m < 6; m++) {
        for (let n = m; n < 6; n++) {
          document.write("❤");
        }
        document.write("<br />");
      }
复制代码

99表,

//外循环控制换行,内循环控制每行显示
      for (let i = 1; i < 10; i++) {
        for (let j = 1; j <= i; j++) {
          document.write(`${j}x${i}=${i * j}&nbsp&nbsp&nbsp&nbsp`);
        }
        document.write("<br />");
      }
复制代码

六.数组Array

  • 数组是指一组有序数据的集合,其中的每个数据被称作元素,在数组中可以存放任意类型的元素。
  • 数组的索引(index)从0开始,数组的长度(length)从1开始。
  • 数组的索引用于获取数组项arr[index],数组的长度可以用来遍历数组,在数组末尾增加或删除元素。

6.1创建数组的四个方法

  • 1.字面量法
  • 2.构造函数方法
  • 3.Array.from()可以将类数组或者可迭代对象转化为数组
  • 4.Array.of(0, 2, 4, 6, 8));// [0, 2, 4, 6, 8]可以将一组参数转化为数组实例

6.2四种数组检测方法

  • 1.Array.isArray(arr)
  • 2.实例 instanceof 类
  • 3.constructor[].constructor.name==='Array'
  • 4.Object.prototype.toString.call(arr) // [object Array]

6.3 增删改查

6.3.1 增加五种(下标、push、unshift、splice、concat)

let array=[1,2,3,4,5]
//1.通过下标增加
array.[array.length]=6

//2.Array.push()从后面增加
 array.push("a", "b");

//3.unshift()前面数组
array.unshift('0')

//4.splice(a,b,5,6,78,9) 指定位置增加
//这个方法的意思:从第a个元素的后面删除b个,并把后面的都加到数组,a是index

//5.concat拼接
var arr = ['a','b','c'];
var arr2 = arr.concat('d',1,2,['e',3]);
console.log(arr2); // ["a", "b", "c", "d", 1, 2, "e", 3]
复制代码

6.3.2 删除四种(pop、shift、slice、splice)

var arr = ["a", "b", "c", "d", 1, 2];
//1.pop()方法删除数组的最后一项,返回被删除的项。
let de = arr.pop();

//2.shift()删除数组的第一项并返回删除项
arr.shift();

//3.使用slice(a,b)截取多个,不操作原数组,返回的数组是截取的值
//从索引a截取,到索引b,包含a不包含b

//4.使用splice() 方法指定位置删除
//splice()原本的意思是在删除元素的同时可以在指定位置插入新元素,
//传入3个参数:开始位置,要删除元素个数,要插入的任意多个元素;
//
var arr = [1,2,3,4,'a','b','c'];
var arr2 = arr.splice(2,2);//从index为2的位置删除两个,包头
console.log(arr); // [1, 2, "a", "b", "c"]
console.log(arr2); // [3, 4]
复制代码

6.3.3 更改数组一种---splice() 方法改变一个数组

splice(a,b,1,2,3,4)从index为a的那个元素开始删除b个元素,然后把后面的元素添加进来,并返回

6.3.4 查询数组(索引查找、indexOf、include、find、findIndex、slice)

除了用原始的索引号查询数组外,js还提供了许多方法查询数组

let arr=[1,2,3,4]
//1.index查询
//arr[0]、arr[1]、arr[arr.length-1]

/*
2.indexOf()通过元素查找索引
    返回数组中第一个满足条件的索引(从0开始), 不满足返回-1;
    .有两个参数,第一个是要查找的元素;第二个是从哪个位置(索引值)开始查找。
    lastIndexOf()从后往前查找
*/
var array = [2, 5, 9];
array.indexOf(2);     // 0
array.indexOf(7);     // -1
array.indexOf(9, 2);  // 2
array.indexOf(2, -1); // -1
array.indexOf(2, -3); // 0

//3. include()表示数组中是否有这样一项,返回 true 或 false
var num = [10, 20, 30, 40, 50, 60, 70, 80, 90];
var newNum7 = num.includes(40);
var newNum8 = num.includes(40, 5);//从索引5的位置开始向后查找
console.log(newNum7);//true
console.log(newNum8);//false

/*
4.find()返回第一个符合条件的数组项
接受一个函数参数,函数依次接受三个参数:数组当前项value,数组当前项索引,数组本身
函数里面写上return语句,ruturn返回的是true则find返回的是满足条件的项
*/
      var inventory = [
        { name: "apples", quantity: 2 },
        { name: "bananas", quantity: 0 },
        { name: "cherries", quantity: 5 },
      ];

      function findCherries(value, index, item) {
        // console.log(value, "数组当前项的值");
        // console.log(index, "当前项索引");
        // console.log(item, "数组本身");
        return value.name === "cherries";
      }

      console.log(inventory.find(findCherries)); // { name: 'cherries', quantity: 5 }
   
//5. findIndex()返回数组中第一个满足条件的索引(从0开始), 不满足返回-1;和find用法一样

//6. 使用slice() 方法提取数组的数据:
var arr = [1,2,3,4,5,6];
var arr2 = arr.slice(-3);
console.log(arr); // [1, 2, 3, 4, 5, 6]
console.log(arr2); // [4, 5, 6]
复制代码

6.4 排序(reverse、sort)

1.翻转数组

Array.reverse():将数组中元素的位置颠倒,并返回该数组。该方法会改变原数组

2.大小排序sort

  • sort接受一个函数参数,函数接受两个值a,b;若果函数返回a-b则正序,返回b-a则倒序(大到小),sort改变原数组,返回处理好的原数组
let a=[2,1,5,7,21,12,33,46,64,0,2,7,8]
a.sort((a,b)=>a-b)//升序
a.sort((a,b)=>b-a)//降序
复制代码

6.5 其他方法

6.5.1 Array.concat()

用于合并两个或多个数组。此方法不会更改现有数组,而是返回一个新数组。

let array=[10,20,30,40];
let array1=[50,60];
console.log(array.concat(array1));//[10, 20, 30, 40, 50, 60]

var arr = ['a','b','c'];
var arr2 = arr.concat('d',1,2,['e',3]);
console.log(arr2); // ["a", "b", "c", "d", 1, 2, "e", 3]
复制代码

6.6 Array.splice()增删改

  • 删除 =>可以删除任意数量的项,只需指定两个参数,要删除第一项的位置和要删除的项数
  • 插入=>可以向指定位置插入任意数量的项,只需提供3个参数,起始位置,0,要插入的项,如果要插入多个项,可以传入第n项,第n+1项,...
  • 替换=>可以向指定位置插入任意数量的项,且同时删除任意数量的项,只需指定3个参数,起始位置,要删除的项数和要插入的的任意数量的项,插入的项数不必与删除的项数相等

6.7 Array.join()

join() 方法将一个数组(或一个类数组对象)的所有元素连接成一个字符串并返回这个字符串。如果数组只有一个项目,那么将返回该项目而不使用分隔符。

var a = ['Wind', 'Rain', 'Fire'];
var myVar1 = a.join();      // myVar1的值变为"Wind,Rain,Fire"不写分隔符默认以逗号分割
var myVar4 = a.join('');    // myVar4的值变为"WindRainFire"
复制代码

6.8 数组迭代(5种)

1.Array.every()判断所有条件是否都满足,满足返回true

Array.every((item,index,array)=>item>10):对数组中的每一项运行给定函数,如果该函数对每一项都返回true,则返回true.

2.Array.some()只要有满足条件的就返回true

Array.some((item,index,array)=>条件判断):对数组中的每一项运行给定函数,如果该函数对任一项返回true,则返回true

3.Array.filter()将满足条件的数组筛选出来

Array.filter((item,index,array)=>条件语句判断):对数组中的每一项运行给定函数,返回该函数会返回true的项组成的数组。

      let arrays = [11, 22, 3, 4, 5, 4, 3, 2, 1];
      let everyResult = arrays.filter((item, index, array) => {
        return item > 10;
      });
      console.log(everyResult); [11,22]
复制代码

4.Array.forEach()没有返回值只是对每个元素都执行--可以批量操作

Array.forEach((item,index,array)=>{操作数组}):forEach() 方法对数组的每个元素执行一次给定的函数,没有返回值。

      let arrays = [
        {
          name: "cc",
        },
        {
          name: "aa",
        },
      ];
      arrays.forEach((item, index, array) => {
        item.time = new Date();
      });
      console.log(arrays);
复制代码

5.Array.map()对数组所有项做相同操作并返回新数组

Array.map((item,index,array)=>操作数组):对数组中的每一项运行给定函数,返回每次函数调用的结果返回的数组

let arrays = [1, 2, 3, 4, 5, 4, 3, 2, 1];
let mapResult=arrays.map((item,index,array)=>{
    return item*2;
})
console.log(mapResult);// 2,4,6,8,10,8,6,4,2
复制代码

数组归并方法Array.reduce(total,current)可以作为一个累加器

接受两个参数第一个是前一项,第二个是当前项

      let arrays = [1, 2, 3, 4, 5, 4, 3, 2, 1];
      let sum = arrays.reduce((total, current) => {
        return total + current;
      });
      console.log(sum);
复制代码

es6新增数组方法

1.Array.of()和Array.from()

  • Array.of总是返回参数值组成的数组。如果没有参数,就返回一个空数组。
Array.of(1,2,3,4);//[1,2,3,4]
复制代码
  • Array.from()将类似数组对象和可遍历(iterable)的对象转化为数组。

常见类数组对象:NodeList 集合,函数内部的arguments对象, 可遍历对象terator 接口:字符串和 Set 结构

2.Array.copyWithin()填充数组和 Array.fill()批量替换

Array.fill()方法将固定值填充到数组指定位置,接受参数如下:

  • value:需要填充的值
  • start:填充的起点
  • end:填充的终点(不包含的index),若果star和end相同填充失败
[1, 2, 3].fill(4, 1);            // [1, 4, 4]
[1, 2, 3].fill(4, 3, 3);         // [1, 2, 3]
复制代码

3.Array.find()和Array.findIndex()和Array.includes()

数组深拷贝

  • 方法一:原生
      Array.prototype.mySlice = function () {
        const arrNew = [];
        for (let i = 0; i < this.length; i++) {
          arrNew.push(this[i]);
        }
        return arrNew;
      };
      var array = [1, 2, 3, 4, 6];
      var arrayNew = array.mySlice();
复制代码
  • 方法二:concat拼接数组,返回新值
      const array = [1, 2, 3, 4, 6];
      const arrayNew = array.concat();
复制代码
  • 方法三:slice截取数组,返回截取值
      const array = [1, 2, 3, 4, 6];
      const arrayNew = array.slice();
复制代码
  • 方法四:扩展运算符
      const array = [1, 2, 3, 4, 6];
      const arrayNew = [...array];
      
      //或者
      var arr1 = [1, 2, 3];
      var [...arr2] = arr1;
复制代码

数组最大值

  • 方法一:原始
   let arr = [3, 5, -2, 7, 4];
    let max = arr[0];
    for(let i = 1; i < arr.length; i++){
        if(arr[i] > max){
            max = arr[i];
        }
    }
    console.log(max);
复制代码
  • 方法二:sort数组排序(从大到小)取第一个
      const arr1 = [66, 31, 2, 68, 3, 66];
      let max = arr1.sort((a, b) => b - a)[0];
      console.log(max);
复制代码
  • 方法三:forEach()循环里面取item判断然后赋值
      const arr1 = [66, 31, 2, 68, 3, 66];
      let max = arr1[0];
      arr1.forEach(function (item, index, arr1) {
        if (item > max) {
          max = item;
        }
      });
      console.log(max);
复制代码
  • 方法四:用math.max()方法
      let arr = [3, 5, -2, 7, 4];
      let max = Math.max(...arr);
      // let max = Math.max.apply(Math, arr); // max中的this还是Math
      console.log(max);
复制代码

数组去重

  • 1.定义一个空数组放去重后的数组元素,forEach()遍历数组每一项,然后判断去重数组里面有没有该项,没有的话就添加进去。
      let arr = [3, 5, -2, 7, 4, 4, 6, 5, 3];
      const arrUnique = [];
      arr.forEach((item) => {
        if (arrUnique.indexOf(item) === -1) {
          arrUnique.push(item);
        }
      });
      console.log(arrUnique);
复制代码
  • 2.先排序在去重--先将原数组排序,在与相邻的进行比较,如果不同则存入新数组
      let arr = [3, 5, -2, 7, 4, 4, 6, 5, 3];
      const arrUnique = [];
      arr.sort((a, b) => b - a); 
      arrUnique.push(arr[0]);
      arr.reduce((pre, cur) => {
        if (pre !== cur) {
          arrUnique.push(cur);
          pre = cur;
        }
        return pre;
      });
      console.log(arrUnique);
复制代码
  • 3.对象的属性去重(推荐)

每次取出原数组的元素作为对象的属性名进行赋值,然后用对象方法访问这些属性名赋值给数组,最后用map去除字符串

      arr = [1, 7, 4, 5, 3, 4, 5, 6, 8, 6, 9, 6, 5, 6, 4, 6, 67, 7];
      arrUnique = {};
      arr.forEach((item, index) => {
        arrUnique[item] = "蔡徐坤";
      });
      arrUnique = Object.keys(arrUnique);

      arrUnique = arrUnique.map((item) => ~~item);
      console.log(arrUnique);
复制代码
  • 4.下标去重for+indexof
      function arrUnique(arr) {
        let uni = [arr[0]];
        for (let i = 1; i < arr.length; i++) {
          if (uni.indexOf(arr[i]) === -1) {
            uni.push(arr[i]);
          }
        }
        return uni;
      }
复制代码
  • 5.ES6 中的Set实现去重(ES6中最常用)
     function arrUnique(arr) {
        arr = new Set(arr);
        arr = Array.from(arr);
        return arr;
      }
复制代码
  • 6.es6的filter()和indexOf()
      function arrUnique(arr) {
        let arrNew = arr.filter((item, index) => arr.indexOf(item) === index);
      /*
        在原数组里面找到当前项返回的索引是当前值吗,如果相等就添加到去重数组里面,
        不相等就代表前面还有一个和当前item相同的项,当前的不应该添加
      */
        return arrNew;
      }
复制代码
  • 7.遍历原数组,includes判断新数组中是否有遍历的当前项,没有则添加入新数组
      function arrUnique(arr) {
        const arrNew = [arr[0]];
        for (let i = 0; i < arr.length; i++) {
          if (!arrNew.includes(arr[i])) {
            arrNew.push(arr[i]);
          }
        }
        return arrNew;
      }
复制代码

七.函数

分类:
前端