八股(自己整理)JS篇1

42 阅读12分钟

1.数据类型

js中的数据类型分为两类

①基本(原始)数据类型

number

string字符串

boolean布尔值

null

undefined

bigint 可以表示超出number安全整数(Number.MAX_SAFE_INTEGER)范围的大整数

symbol 创建后独一无二且不可变

②引用数据类型

object对象

array数组

function函数

原始数据类型和引用数据类型的区别:

原始数据类型存储在栈中 引用数据类型存储在堆中

2.数据类型检测方式

①typeof

数组、对象、null都会被识别成object

②instanceof

console.log(2 instanceof Number) //false
console.log([] instanceof Array) //true

只能判断引用数据类型

可以正确判断对象类型

通过判断在其原型链中能否找到该类型的原型

③constructor

console.log(('str').constructor === String) //true

判断数据类型+对象实例通过constructor对象访问它的构造函数

如果创建一个对象来改变它的原型,就不能用constructor来判断了

④Object.prototype.toString.call()

3.对JSON的理解

基于文本的轻量级的数据交换格式,可以被任何编程语言读取和作为数据格式来传递

前后端数据交换的方式:

在前端将一个符合JSON格式的数据结构序列化为json字符串

JSON.stringify()

传递到后端,后端将其解析成对应的数据结构

JSON.parse()

JSON中对象格式更严格,如属性值不能为函数,不能出现NaN

4.脚本延迟加载的方法

延迟加载:等页面加载完成后再加载JS文件,有助于提高页面加载速度

HTML中浏览器遇到<script>标签时默认会暂停对HTML的解析去下载并执行js文件

①defer属性

给js脚本添加defer属性

浏览器异步下载JS文件,不会立即执行,等HTML解析完成后再顺序执行

让脚本的加载与文档的解析同步,在文档解析完成后再执行这个脚本文件

如果有多个带defer属性的<script>标签,按照在HTML中出现的顺序依次执行

②async属性

浏览器异步下载JS文件,下载完成后会立即暂停HTML解析,执行JS文件

如果有多个带async属性的<script>标签,加载顺序不确定

③动态创建DOM方式

可以对文档的加载事件进行监听,文档加载完成后再动态创建script标签来引入js脚本

④setTimeout

⑤把js脚本放在文档底部

5.类数组对象

拥有length属性和若干索引属性的对象

和数组类似但不能调用数组的方法

常见的类数组对象:

arguments

DOM方法的返回结果

函数

常见的类数组转化为数组的方法:

①call调用数组的slice方法

Array.prototype.slice.call(arrayLike)

②call调用数组的splice方法

Array.prototype.splice.call(arrayLike,0)

③apply调用数组的concat方法

Array.prototype.concat.apply([],arrayLike)

④Array.from

Array.from(arrayLike)

遍历类数组:

① ② ③

6.数组的原生方法

数组和字符串的转换 toString() toLocalString() join()

尾部操作方法 pop() push()

首部操作方法 shift() unshift() reverse() sort()

连接方法 concat()

截取方法 slice()

插入方法 splice()

归并方法 reduce() reduceRight()

join()

把数组中的所有元素转换成一个字符串

元素通过指定的分隔符分隔,默认逗号

var arr = [1,2,3];
console.log(arr.join());   // 1,2,3
console.log(arr.join("-"));   // 1-2-3
console.log(arr);   // [1, 2, 3](原数组不变)

通过join()方法可以实现重复字符串

function repeatString(str, n) {
//一个长度为n+1的空数组用string去拼接成字符串,就成了n个string的重复
	return new Array(n + 1).join(str);
}
console.log(repeatString("abc", 3));   // abcabcabc
console.log(repeatString("Hi", 5));   // HiHiHiHiHi

push()和pop()

push从数组末尾向数组添加元素,可以添加一个/多个,返回数组长度

pop删除数组的最后一个元素并返回删除的元素

var arr = ["Lily","lucy","Tom"];
console.log(arr.push("Jack","Sean"));  // 5
console.log(arr);   // ["Lily", "lucy", "Tom", "Jack", "Sean"]
console.log(arr.pop());   // Sean
console.log(arr);   // ["Lily", "lucy", "Tom", "Jack"]

shift()和unshift()

shift删除数组的第一个元素,返回第一个元素

unshift向数组的开头添加一个/多个元素,返回数组长度

var arr = ["Lily","lucy","Tom"];
console.log(arr.unshift("Jack","Sean"));   // 5
console.log(arr);   //["Jack", "Sean", "Lily", "lucy", "Tom"]
console.log(arr.shift());   // Jack
console.log(arr);   // ["Sean", "Lily", "lucy", "Tom"]

reserve()

颠倒数组中元素的顺序

var arr = [13, 24, 51, 3];
console.log(arr.reverse());   //[3, 51, 24, 13]
console.log(arr);   //[3, 51, 24, 13](原数组改变)

sort()

排序,默认顺序为按字母升序

var arr1 = ["a", "d", "c", "b"];
console.log(arr1.sort());   // ["a", "b", "c", "d"]
arr2 = [13, 24, 51, 3];
console.log(arr2.sort());   // [13, 24, 3, 51]
console.log(arr2);   // [13, 24, 3, 51](原数组被改变)

也可以接收一个比较函数作为参数

比较函数接收两个参数,如果第一个参数应该位于第二个之前则返回一个负数,如果两个参数相等则返回 0,如果第一个参数应该位于第二个之后则返回一个正数。

function compare(value1, value2) {
    if (value1 < value2) {
    	return -1;
    } else if (value1 > value2) {
   		return 1;
    } else {
    	return 0;
    }
}
arr2 = [13, 24, 51, 3];
console.log(arr2.sort(compare));   // [3, 13, 24, 51]

concat()

连接两个或多个数组

不改变原有数组,返回被连接数组的一个副本

var arr = [1,3,5,7];
var arrCopy = arr.concat(9,[11,13]);
console.log(arrCopy);   //[1, 3, 5, 7, 9, 11, 13]
console.log(arr);   // [1, 3, 5, 7](原数组未被修改)

传入的不是数组,则直接把参数添加到数组后面,

如果传入的是数组,则将数组中的各个项添加到数组中

slice()

返回从原数组中截取的新数组

接收一/两个参数,返回项的起始和结束位置

只有一个参数,返回从参数指定位置开始到当前数组末尾的所有项

接收两个参数,返回起始至结束,但不包括结束位置的项

参数有负数时,将负数加上数组长度的值

var arr = [1,3,5,7,9,11];
console.log(arr.slice(1));   //[3, 5, 7, 9, 11]
console.log(arr.slice(1,4));   //[3, 5, 7]
console.log(arr.slice(1,-2));   //[3, 5, 7]
console.log(arr.slice(-4,-1));   //[5, 7, 9]
console.log(arr);   //[1, 3, 5, 7, 9, 11](原数组没变)

splice()

可以实现删除、插入和替换

①删除

可以删除任意数量的项 接收两个参数:要删除的第一项的位置和要删除的项数 返回删除的元素

var arr = [1,3,5,7,9,11];
var arrRemoved = arr.splice(0,2);
console.log(arr);   //[5, 7, 9, 11](原数组改变)
console.log(arrRemoved);   //[1, 3]
②向指定索引处添加元素

传参:起始位置,0(要删除的项数),要插入的项

var array1 = [22, 3, 31, 12];
array1.splice(1, 0, 12, 35);  //[]
console.log(array1); // [22, 12, 35, 3, 31, 12]
③替换指定位置的元素

可以向指定位置插入任意数量的项,同时删除任意数量的项

传参:起始位置,要删除的项数,要插入的项

const array1 = [22, 3, 31, 12];
array1.splice(1, 1, 8);   //[3]
console.log(array1);  // [22, 8, 31, 12]

indexOf()和lastIndexOf()

接收两个参数:要查找的项和查找的起点位置

indexOf:从数组开头(0)开始向后查找

lastIndexOf:从数组末尾向前查找

返回要查找的项在数组中的位置,没找到返回-1

比较时使用全等操作符

var arr = [1,3,5,7,7,5,3,1];
console.log(arr.indexOf(5));   //2
console.log(arr.lastIndexOf(5));   //5
console.log(arr.indexOf(5,2));   //2
console.log(arr.lastIndexOf(5,4));   //2
console.log(arr.indexOf("5"));   //-1

forEach()

遍历数组,没有返回值

参数都是function类型,默认有传参

传参为:遍历的数组内容,对应的数组索引,数组本身

map()

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

不会改变原数组

var arr = [1, 2, 3, 4, 5];
var arr2 = arr.map(function(item){
	return item*item;
});
console.log(arr2);  //[1, 4, 9, 16, 25]

filter()

过滤

返回新数组,不改变原数组

var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
var arr2 = arr.filter(function(x, index) {
	return index % 3 === 0 || x >= 8;
});
console.log(arr2);  //[1, 4, 7, 8, 9, 10]

fill() ES6新增

使用特定值填充数组

改变了原数组

只传一个参数时,用参数值填充整个数组

let arr = [1, 2, 3, 'cc', 5];
arr.fill(1);
console.log(arr);//[1,1,1,1,1];

传三个参数:填充数值,起始位置参数,结束位置参数(不包括结束位置的元素)

let arr = [1, 2, 3, 'arr', 5];
arr.fill(1, 2);
console.log(arr);//[1,2,1,1,1]

arr.fill(0, 1, 3);
console.log(arr);//[1,0,0,1,1];

every()

判断数组中的每一项是否满足条件

所有项都满足才返回true

var arr = [1, 2, 3, 4, 5];
var arr2 = arr.every(function(x) {
	return x < 10;
});
console.log(arr2);  //true
var arr3 = arr.every(function(x) {
	return x < 3;
});
console.log(arr3);  // false

some()

判断数组中是否存在满足条件的项

只要有一项满足就返回true

var arr = [1, 2, 3, 4, 5];
var arr2 = arr.some(function(x) {
	return x < 3;
});
console.log(arr2);  //true
var arr3 = arr.some(function(x) {
	return x < 1;
});
console.log(arr3);  // false

includes()

判断一个数组是否包含一个指定的值,是返回true,否则false

接收两个参数:查找的元素值,开始查找元素的位置(可不传)

const array1 = [22, 3, 31, 12, 'arr'];
const includes = array1.includes(31);
console.log(includes); // true

使用===运算符来进行比较,但NaN被认为与自身相等

reduce()和reduceRight()

reduce()接收两个参数,回调函数reducer和初始值(可不传)

reducer接收四个参数:

acc:累加器,存储上一次回调的返回值

current:当前的数组元素

index:当前的数组元素索引

array:原数组

如果传了第二个参数,reduce方法会在此基础上开始累积执行

如数组求和

    const arr = [1, 2, 3, 4]
    const accumulator = (total, current, currentIndex, arr) => {
      console.log(total, current, currentIndex, arr);
      return total + current
    }
    console.log(arr.reduce(accumulator))

reduce从头开始 reduceRight从末尾开始

toString()和toLocalString()

将数组转换成字符串

const array1 = [22, 3, 31, 12];
console.log(array1.toLocaleString()); // 22,3,31,12
console.log(array1.toString()); // 22,3,31,12
tolocalstring会根据用户的区域设置进行格式化

find()和findIndex()

接收两个参数:回调函数,可选值用于指定回调函数内部的this

回调函数接收三个参数:

数组的某个元素,该元素的索引位置,数组本身

回调函数会在给定的元素满足定义的条件时返回true

find()和findIndex()会在回调函数第一次返回true时停止查找

find()返回匹配的值

findIndex()返回匹配位置的索引

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

console.log(arr.find((value, keys, arr) => {
    return value > 2;
})); // 3 返回匹配的值

console.log(arr.findIndex((value, keys, arr) => {
    return value > 2;
})); // 2 返回匹配位置的索引

传递第二个参数时,回调函数内部的this指向该参数

在使用非箭头函数作为回调函数时使用

const team = {
  members: ['Alice', 'Bob', 'Charlie'],
  leader: 'Alice',
  
  findLeader() {
    return this.members.find(function(member) {
      // this 指向 team 对象(由 thisArg 绑定)
      return member === this.leader;
    }, this); // 传递 this 作为 thisArg
  }
};

console.log(team.findLeader()); // "Alice"

copyWithin()

从数组的指定位置拷贝元素到数组的另一指定位置中

会改变原数组

let arr = [1, 2, 3, 'arr', 5];
arr.copyWithin(3, 0);
console.log(arr);//[1,2,3,1,2]

默认情况下会复制到数组末尾

传第三个参数指定复制停止的位置

let arr = [1, 2, 3, 'arr', 5, 9, 17];

//从索引3的位置开始粘贴
//从索引0的位置开始复制
//遇到索引3时停止复制
arr.copyWithin(3, 0, 3);
console.log(arr);//[1,2,3,1,2,3,17]

flat()

按一个可指定的深度递归遍历数组,

将所有元素和遍历到的子数组里的元素合并为一个新数组返回

不改变原数组

传参:指定要提取嵌套数组的结构深度,默认为1

const arr1 = [0, 1, 2, [3, 4]];

console.log(arr1.flat());
// expected output: [0, 1, 2, 3, 4]

const arr2 = [0, 1, 2, [[[3, 4]]]];

console.log(arr2.flat(2));
// expected output: [0, 1, 2, [3, 4]]

//使用 Infinity,可展开任意深度的嵌套数组
var arr4 = [1, 2, [3, 4, [5, 6, [7, 8, [9, 10]]]]];
arr4.flat(Infinity);
// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

// 扁平化数组空项,如果原数组有空位,flat()方法会跳过空位
var arr4 = [1, 2, , 4, 5];
arr4.flat();
// [1, 2, 4, 5]

改变原数组的方法:

push() pop() shift() unshift()

reserve()

sort()

splice()

fill()

copyWithin()

不改变:

concat()

filter()

flat()

7.对AJAX的理解

8.尾调用

指函数的最后一步调用另一个函数

ES6中尾调用优化只在严格模式下开启

代码执行基于执行栈,在一个函数中调用另一个函数时,会保留当前的执行上下文,再新建另一个执行上下文加入栈中

使用尾调用,可以不必保留当前的执行上下文,节省了内存

9.常见的DOM操作

①DOM节点的获取

var imooc=document.getElementById('imooc') //按id查询
var pList=document.getElementByTagName('p')  //按标签名查询
var moocList=document.getElementsByClassName('mooc')
//按类名查询
var pList=document.querySelectorAll('.mooc')
//按css选择器查询

②DOM节点的创建

已知HTML结构:

<div id="container">
     <h1 id="tiitle">我是标题</h1>
</div>

添加一个有内容的span节点到id为title的节点后面:

var container=document.getElementById('container')
var targetSpan=document.createElement('span')
targetSpan.innerHTML='hello world'
container.appendChild(targetSpan)

③DOM节点的删除

删除id为title的元素

var container=document.getElementById('container')
var targetNode=document.getElementById('title')
container.removeChild(targetNode)

④修改DOM元素

交换两个DOM元素的位置

var title=document.getElementById('title')
var content=document.getElementById('content')
container.insertBefore(content,title)

10.use strict模式

禁止使用with语句

禁止this关键字指向全局对象

对象不能有重名的属性

目的:

消除js语法的不合理不严谨之处

消除代码运行的不安全之处

11.forEach和map方法的区别

都是用来遍历数组的

forEach():

针对每一个元素执行提供的函数,对数据的操作会改变原数组,没有返回值

map():

不改变原数组,返回一个新数组,为原数组调用函数处理之后的值

12.原型与原型链

13.arguments

函数里的一个内建对象,包含了函数接收到的所有变量

是类数组

在实际开发中,可以使用arguments获取到所有实参

遍历接收的实参:

for(let i = 0; i <arguments.length;i++){
      console.log(arguments[i]);
}

14.函数柯里化

只传递给函数一部分参数来调用它,让它返回一个函数去处理剩下的参数

简单实现add(1)(2)(3):

const add = x => y => z => x + y + z;
console.log(add(1)(2)(3));

如果被要求实现add函数同时支持:

add(1, 2, 3);
add(1, 2)(3);
add(1)(2, 3);

可以自己实现一个工具函数专门生成柯里化函数:

const curry = (fn, ...args) => 
    // 函数的参数个数可以直接通过函数数的.length属性来访问
    args.length >= fn.length // 这个判断很关键!!!
    // 传入的参数大于等于原始函数fn的参数个数,则直接执行该函数
    ? fn(...args)
    /**
     * 传入的参数小于原始函数fn的参数个数时
     * 则继续对当前函数进行柯里化,返回一个接受所有参数(当前参数和剩余参数) 的函数
    */
    : (..._args) => curry(fn, ...args, ..._args);

function add1(x, y, z) {
    return x + y + z;
}
const add = curry(add1);
console.log(add(1)(2, 3));