《红宝书》学习笔记

第一章 认识js

js的组成部分有哪些?
①ecma 核心语法 api
②dom 提供与网页内容交互的方法和接口
③bom 浏览器对象模型,提供了和浏览器交互的接口

use strict 是什么?
use strict 是一种 ECMAscript5 添加的(严格模式)运行模式,这种模式使得 Javascript 在更严格的条件下运行。设立严格模式的目的如下:

①消除 Javascript 语法的不合理、不严谨之处,减少怪异行为;
②消除代码运行的不安全之处,保证代码运行的安全;
③播提高编译器效率,增加运行速度;
④为未来新版本的 Javascript 做好铺垫。

区别:
①禁止使用 with 语句。
②禁止 this 关键字指向全局对象。
③对象不能有重名的属性

js的语言特性?
①运行在客户端浏览器上;
②不用预编译,直接解析执行代码;
③是弱类型语言,较为灵活;
④与操作系统无关,跨平台的语言;
⑤脚本语言、解释性语言

语言类型的区别?
(1)解释型语言
使用专门的解释器对源程序逐行解释成特定平台的机器码并立即执行。是代码在执行时才被解释器一行行动态翻译和执行,而不是在执行之前就完成翻译。解释型语言不需要事先编译,其直接将源代码解释成机器码并立即执行,所以只要某一平台提供了相应的解释器即可运行该程序。其特点总结如下

(2)编译型语言
使用专门的编译器,针对特定的平台,将高级语言源代码一次性的编译成可被该平台硬件执行的机器码,并包装成该平台所能识别的可执行性程序的格式。在编译型语言写的程序执行之前,需要一个专门的编译过程,把源代码编译成机器语言的文
(3)强类型语言
强类型定义语言,是一种总是强制类型定义的语言,要求变量的使用要严格符合定义,所有变量都必须先定义后使用。Java和C++等语言都是强制类型定义的,也就是说,一旦一个变量被指定了某个数据类型,如果不经过强制转换,那么它就永远是这个数据类型了。例如你有一个整数,如果不显式地进行转换,你不能将其视为一个字符串。
(2)弱类型语言
弱类型定义语言,与强类型定义相反。JavaScript语言就属于弱类型语言。简单理解就是一种变量类型可以被忽略的语言。比如JavaScript是弱类型定义的,在JavaScript中就可以将字符串’12’和整数3进行连接得到字符串’123’,在相加的时候会进行强制类型转换。

两者主要区别在于: 前者源程序编译后即可在该平台运行,后者是在运行期间才编译。所以前者运行速度快,后者跨平台性好。

第二章 html中的js

<script> 标签为什么不放在head 和body 里面?
按顺序下载执行,造成页面堵塞,渲染延迟
面试题:js中延迟加载的方式有哪些?
defer 属性立即下载,等整个页面文档解析完成后才执行,不阻塞多个defer按顺序
async 属性: 是立即下载,立即执行,和html同步加载,可能造成阻塞,不按顺序执行使用
setTimeout 延迟方法:设置一个定时器来延迟加载js脚本文件
让 JS 最后加载: 将 js 脚本放在文档的底部,来使 js 脚本尽可能的在最后来加载执行。

第三章 语言基础

面试题 :const let var 的区别?
暂时性死区:ES6 明确规定,代码块内如果存在 let 或者 const,代码块会对这些命令声明的变量从块的开始就形成一个封闭作用域。代码块内,在声明变量 PI 之前使用它会报错。
①Var 声明的变量会挂载在 window 上,全局变量,而 let 和 const 声明的变量不会,会形成块级作用域
②Var 声明的变量存在变量提升,let 和 const 不存在变量提升
③同一作用域下 var 可以声明同名变量,let 和 const、不可以
④Const 一旦声明必须赋值,不能用 null 占位,声明后不能再修改,如果声明的是数组或者对象类型数据,可以修改属性,但是不能重新赋值

面试题 : const和let的区别 ?
相同点:
都是块级作用域仅仅在函数内部有效,退出循环即终止.
都不能和其所在的作用域中变量或者函数拥有相同的名称
都不能提升变量,必须先定义再使用
区别:
let可以申明但是没有赋值,默认就是undefined,而const不可以只申明没有赋值,必须初始化操作.
const声明不能重新赋值和再次申明,而let可以重新赋值.
注意点:使用const声明的并非完全不可修改的常数,如果使用const来定义一个对象或者数组.可以通过修改对象的属性,而不可以直接给对象赋值新的数据

面试题 :null和undefined的区别?
undefined 变量声明了但是没有 赋值,一般不会显示变量为undefined 类型就是 undefined
null 是一个空对象,类型是objet 一般可以用来初始化一个空对象

面试题:如何清空数组?
第一种,使用splice ary.splice(0,ary.length);
第二种,arr.length = 0
第三种,arr=[ ]

let 和 var 在循环中的区别?

  for(var i =0;i<5;++i) {
      setTimeout(() => {
        console.log(i);
      }, 1000);
      // 5 5 5 5 5 所有的i都是同一个变量
    }
    for(let i =0;i<5;++i) {
      setTimeout(() => {
        console.log(i);
      }, 1000);
      // 0 1 2 3 4  每次迭代声明的是一个独立变量我们一般不适用const 来循环,除非不改变循环
    }

变量声明风格:不使用var , const 优先,let次之

if(a) 是什么意思?
if控制语句会自动将括号中的数据转为布尔值
什么情况会转为false?
① ‘ ’ ② 0、NaN ③ null ④undefined

数值的范围?
-Infinity 负无穷 可以使用isFinite()来判断,NaN 非数值,涉及nan的计算都会返回nan,nan的类型是number
symbol 是什么?
symbol(符号)是es6 新增的数据类型,用途是确保对象属性使用唯一标识符
let s = Symbol()也可以传入一个字符串参数作为对符号的描述不能和 new 关键字作为构造函数使用,避免创建符号包装对象
break和contine 的区别?
break 立即退出循环,结束continue 退出当前循环,从头还是继续

面试题 :isNaN 和 Number.isNaN 函数的区别?

函数 isNaN 接收参数后,会尝试将这个参数转换为数值,如果可以转换为数值就返回false,任何不能被转换为数值的的值都会返回 true,因此非数字值传入也会返回 true ,会影响 NaN 的判断。不能用来判断是否严格相等。是es5中判断数值的方法

let s = isNaN('hhh');
console.log(s); true 因为转换失败了,所以返回了true
let a = Number.isNaN('kkk');
console.log(a);

函数 Number.isNaN 会首先判断传入参数是否为数字,如果是数字再继续判断是否为 NaN ,不会进行数据类型的转换,这种方法对于 NaN 的判断更为准确。es6.

面试题:字符串转化为数字类型?
1.转换函数:
js提供了parseInt()和parseFloat()两个转换函数。前者把值转换成整数,后者把值转换成浮点数。只有对String类型调用这些方法,这两个函数才能正确运行;对其他类型返回的都是NaN(Not a Number)。
2.Number(value)——把给定的值转换成数字(可以是整数或浮点数);
3.前面加+

面试题:for in 和 for of 的区别?
for… in 会遍历对象的整个原型链,性能非常差不推荐使用,获取的是对象的key。是为了遍历对象而生的不适用于遍历数组。
而 for … of 适用于遍历数组,获取的是的value。
如果用它直接遍历对象会报错

   Uncaught TypeError: obj is not iterable
var obj = {
    'name': 'Jim Green',
    'age': 12
}
for(let key of obj) {
    console.log('for of obj', key)
}
let arr = [2,45,6,7,8,8]for(let key of arr) {
    console.log('for of arr', key)
}

面试题:字符串常用的方法?
length 返回长度

var txt = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
var sln = txt.length;

toLowerCase(): 把字符串转为小写,返回新的字符串。

var str="Hello World";
var str1=str.toLowerCase();

toUpperCase(): 把字符串转为大写,返回新的字符串。

var str="hello world";
var str1=str.toUpperCase();

charAt(): 返回指定下标位置的字符。如果index不在0-str.length(不包含str.length)之间,返回空字符串。

var str="hello world";
var str1=str.charAt(6);
console.log(str1); 

indexOf(): 返回某个指定的子字符串在字符串中第一次出现的位置

var str = "The full name of China is the People's Republic of China.";
var pos = str.indexOf("China");

lastIndexOf(): 返回某个指定的子字符串在字符串中最后出现的位置。

var str = "The full name of China is the People's Republic of China.";
var pos = str.indexOf("USA");

search() 方法搜索特定值的字符串,并返回匹配的位置:

var str = "The full name of China is the People's Republic of China.";
var pos = str.search("locate");

字符串截取的三种方式:
slice() 提取字符串的某个部分并在新字符串中返回被提取的部分。 包头不包尾,接受负数。如果没有第二个参数,剩下的全都截取

var str="Hello World";
var str1=str.slice(2); //如果只有一个参数,则提取开始下标到结尾处的所有字符串
var str2=str.slice(2,7); //两个参数,提取下标为2,到下标为7但不包含下标为7的字符串

substirng 和slice差不多,不能接受负数参数。如果没有第二个参数,剩下的全都截取

var str = "Apple, Banana, Mango";
var res = str.substring(7,13);

substr 第二个参数是长度,第一个是起止位置。如果没有第二个参数,剩下的全都截取
var str = “Apple, Banana, Mango”;
var res = str.substr(7,6);

replace(): 在字符串中用一些字符替换另一些字符,或替换一个与正则表达式匹配的子串。不会改变调用它的字符串。它返回的是新字符串。。只替换收割匹配的

str = "Please visit Microsoft!";
var n = str.replace("Microsoft", "W3School");
concat  连接两个或多个字符串
var text1 = "Hello";
var text2 = "World";
t3 = text1.concat(" ",text2);

trim() 方法删除字符串两端的空白符:

var str = "       Hello World!        ";
alert(str.trim());

可以通过 split() 将字符串转换为数组:

var txt = "a,b,c,d,e";   // 字符串
txt.split(",");          // 用逗号分隔
txt.split(" ");          // 用空格分隔
txt.split("|");  

match() 方法根据正则表达式在字符串中搜索匹配项,并将匹配项作为 Array 对象返回。

let text = "The rain in SPAIN stays mainly in the plain";
text.match(/ain/g)    // 返回数组 [ain,ain,ain]

如果字符串包含指定值,includes() 方法返回 true。

let text = "Hello world, welcome to the universe.";
text.includes("world")    // 返回 true

如果字符串以指定值开头,则 startsWith() 方法返回 true,否则返回 false:

string.startsWith(searchvalue, start默认时候0)
let text = "Hello world, welcome to the universe.";

text.startsWith(“Hello”) // 返回 true
如果字符串以指定值结尾,则 endsWith() 方法返回 true,否则返回 发

false:string.endswith(searchvalue, length)
var text = "John Doe";
text.endsWith("Doe")  

第四章 变量,作用域和内存

面试题:常见的数据类型?
基本数据类型:undefined,null,Boolean,number,string,symbol
引用数据类型:function ,object,Date,Array,RegExp
区别:基本数据类型在栈中,深拷贝
引用数据类型堆中,浅拷贝,栈中保存的是指针

面试题:栈和堆的区别?
内存被分为栈区和堆区:
栈区内存由编译器自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
堆区内存一般由开发着分配释放,若开发者不释放,程序结束时可能由垃圾回收机制回收。
引用数据类型存储在堆内存中,因为引用数据类型占据空间大、占用内存不固定。 如果存储在栈中,将会影响程序运行的性能;引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址。 当解释器寻找引用值时,会首先检索其在栈中的地址,取得地址后从堆中获得实体在操作系统中,
1、申请方式的不同。栈由系统自动分配,而堆是人为申请开辟;
2、申请大小的不同。栈获得的空间较小,而堆获得的空间较大;
3、申请效率的不同。栈由系统自动分配,速度较快,而堆一般速度比较慢;
4、存储内容的不同。栈在函数调用时,函数调用语句的下一条可执行语句的地址第一个进栈,然后函数的各个参数进栈,其中静态变量是不入栈的。而堆一般是在头部用一个字节存放堆的大小,堆中的具体内容是人为安排;
5、底层不同。栈是连续的空间,而堆是不连续的空间。

面试题:深拷贝和浅拷贝的区别,如何实现?
基本数据类型存储栈内存中,引用数据类型存储在堆内存中。
浅拷贝和深拷贝都是对于引用数据类型来说的。基本数据类型默认就是深拷贝。
浅拷贝只是栈内存复制某个对象的指针(地址),导致它们都指向了堆内存中同一个数据,互相影响。
深拷贝是在堆内存中创建一个一模一样的数据,然后把新数据的内存地址赋给新变量,这样旧变量和新变量就指向了不同的数据,也就不会互相影响。
实现:
方法一:JSON.stringify:将一个 JS 值转为 JSON 字符串。JSON.parse:将 JSON 字符串转成 JS 值或对象。不足:这种方法虽然可以实现数组或对象深拷贝,但不能处理函数:undefined、函数、symbol在序列化过程种会忽略或被转为null

var originObj, cloneObj;
originObj = { name: "cez", age: 18 };
cloneObj = JSON.parse(JSON.stringify(originObj));
originObj.name = "zlz";
cloneObj.age = 180;
console.log(originObj === cloneObj); // false
console.log(originObj); // {name: 'zlz', age: 18}
console.log(cloneObj);  // {name: 'cez', age: 180}

方法二:
concat()方法用于合并两个或多个数组。此方法不会更改现有数组,而是返回一个新数组。不足:
concat()只是对数组的第一层进行深拷贝。如果数组内嵌套对象是浅拷贝

// 第一种情况:数组为一维数组
let originArr = [1, 2, 3, 4];
let cloneArr = originArr.concat();
console.log(originArr === cloneArr);  // false
console.log(cloneArr);  // [1, 2, 3, 4]
originArr.push(5);
console.log(cloneArr);  // [1, 2, 3, 4]
// 第二种情况:数组为多维数组或包含对象
let originArr = [1, 2, 3, 4, [5, 6, 7], { name: "cez" }];
let cloneArr = originArr.concat();
console.log(originArr === cloneArr); // false
console.log(JSON.stringify(cloneArr)); // [1,2,3,4,[5,6,7],{"name":"cez"}]
originArr.push(5);
originArr[4][0] = "a";
originArr[5].name = "zlz";
console.log(JSON.stringify(cloneArr)); // [1,2,3,4,["a",6,7],{"name":"zlz"}]

方法三:数组的slice方法,不足:slice()只是对数组的第一层进行深拷贝。

// 第一种情况:数组为一维数组
let originArr = [1, 2, 3, 4];
let cloneArr = originArr.slice(0);
console.log(originArr === cloneArr);  // false
console.log(cloneArr); // [1, 2, 3, 4]
originArr.push(5);
console.log(cloneArr);  // [1, 2, 3, 4]
// 第二种情况:数组为多维数组或包含对象
let originArr = [1, [2, 3], { name: "cez" }];
let cloneArr = originArr.slice(0);
console.log(originArr === cloneArr); // false
console.log(JSON.stringify(cloneArr));  // [1,[2,3],{"name":"cez"}]
originArr.push(4);
originArr[1][0] = "a";
originArr[2].name = "zlz";
console.log(JSON.stringify(cloneArr));  // [1,["a",3],{"name":"zlz"}]

方法四:展开运算符,不足:…只是对数组和对象的第一层进行深拷贝。

// 拷贝数组
let originArr = [1, [2, 3]];
let cloneArr = [...originArr];
console.log(JSON.stringify(cloneArr));  // [1,[2,3]]
originArr[0] = 'a';
originArr[1][0] = 'a'
console.log(JSON.stringify(cloneArr)); // [1,["a",3]]
// 拷贝对象
let originObj = { a: 1, b: { c: 2 } };
let cloneObj = {...originObj};
console.log(JSON.stringify(cloneObj));  // {"a":1,"b":{"c":2}}
originObj.a = 'a';
originObj.b.c = 'c';
console.log(JSON.stringify(cloneObj)); // {"a":1,"b":{"c":"c"}}

方法五:Object.assign,不足:Object.assign()只是对对象的第一层进行深拷贝。

let originObj, cloneObj;
originObj = { a: 1, b: { c: 2 } };
cloneObj = Object.assign({}, originObj);
console.log(JSON.stringify(cloneObj)); // {"a":1,"b":{"c":2}}
originObj.a = "a";
originObj.b.c = "c";
console.log(JSON.stringify(cloneObj)); // {"a":1,"b":{"c":"c"}}

方法六:递归

var originArr, cloneArr;
function deepClone(source) {
const targetObj = source.constructor === Array ? [] : {};
for (let key in source) {
  if (source.hasOwnProperty(key)) {
  只拷贝自身的属性,不拷贝原型的属性
    if (source[key] && typeof source[key] === "object") {
  元素为引用数据类型
     targetObj[key] = source[key].constructor === Array ? [] : {};
     targetObj[key] = deepClone(source[key]);
 } else {
元素为基本数据类型
  targetObj[key] = source[key];}}}
  return targetObj}
  originArr = [1, 2, 3, function () {}];
  cloneArr = deepClone(originArr);
  originArr[2] = "cez";
  console.log(originArr);  // [1, 2, 'cez', f]
  console.log(cloneArr);   // [1, 2, 3, f]

方法七:lodash 的deepClone 方法,适用于多层的数组、对象

const nextDataSource = cloneDeep(dataSource);

对象适用的拷贝方法:Object.assign 、…展开运算符、JSON.stringify、deepClone
数组适用的拷贝方法:slice 、 concat 、Object.assign 、…展开运算符、JSON.stringify、deepClone

面试题:如何判断数据类型?
①基本数据类型使用type of ,但是对于引用类型null和array都返回object
可以发现当数据是 object,array,null 时,全部返回 object 类型

typeof function(){} // "function" 

②instance of 左边是要判断的值,右边是指定的类型,只能判断引用数据类型 用来判断数组或者object类型,如:Object,Function,Array,Date,RegExp等。判断原理:a的原型链上能否找到B的构造函数。

num instanceof Number // true

③Constructor 构造函数

var foo=5
foo.constructor 为number

但是如果判断null和undefine会报错

arr.constructor    ===  Array  true

④最好的类型判断方法:
Object.prototype.toString.call(需要判断的值)

判断是否是数组的方法:
方式一:Object.prototype.toString.call(arr)===‘Array’
方式二:Array.isArray(obj);
方式三:obj instanceof Array
方式四:obj.proto === Array.prototype;

typeof 判断基本数据类型,但是对象和null都是object不准确instance of 判定引用数据类型

面试题:什么是内存泄漏?
内存泄漏就是指由于疏忽或者程序的某些错误造成未能释放已经不再使用的内存的情况。简单来讲就是假设某个变量占用100M的内存,而你又用不到这个变量,但是这个变量没有被手动的回收或自动回收,即仍然占用100M的内存空间,这就是一种内存的浪费,即内存泄漏

根据内存泄漏的定义,有些变量或数据不再被使用或不需要了,那么它就是垃圾变量或垃圾数据,那么此时就需要对这些垃圾数据进行回收而
JavaScript采用的则是自动回收的机制,即我们不需要关心何时为变量分配多大的内存
造成内存泄漏的情况
1.闭包使用不当
2.忽略的全局变量
4.控制台的打印
5.遗忘的定时器

面试题:什么是垃圾回收?
GC 即 Garbage Collection ,程序工作过程中会产生很多 垃圾,这些垃圾是程序不用的内存或者是之前用过了,以后不会再用的内存空间
为什么要清理垃圾?
用官方一点的话说,程序的运行需要内存,只要程序提出要求,操作系统或者运行时就必须提供内存,那么对于持续运行的服务进程,必须要及时释放内存,否则,内存占用越来越高,轻则影响系统性能,重则就会导致进程崩溃
js自动回收垃圾的策略是什么?
第一种:标记清楚法
垃圾回收器在运行的时候会给存储在内存中的变量都加上标记(所有都加),然后去掉环境变量中的变量,以及被环境变量中的变量所引用的变量(条件性去除标记)删除所有被标记的变量,删除的变量无法在环境变量中被访问所以会被删除,最后垃圾回收器,完成了内存的清除工作,并回收他们所占用的内存。
缺点:
标记清除算法有一个很大的缺点,就是在清除之后,剩余的对象内存位置是不变的,也会导致空闲内存空间是不连续的,出现了
内存碎片(如下图),并且由于剩余空闲内存不是一整块,它是由不同大小内存组成的内存列表,这就牵扯出了内存分配的问题
解决以上的缺点可以使用 **标记整理(Mark-Compact)算法 **,标记结束后,标记整理算法会将活着的对象(即不需要清理的对象)向内存的一端移动,最后清理掉边界的内存(如下图)

第二种:引用计数法
另一种不太常见的方法就是引用计数法,引用计数法的意思就是每个值没引用的次数, 当声明了一个变量,并用一个引用类型的值赋值给改变量,则这个值的引用次数为 1,;相反的,如果包含了对这个值引用的变量又取得了另外一个值,则原先的引用值引用次数就减 1,当这个值的引用次数为 0 的时候,说明没有办法再访问这个值了,因此就把所占的内存给回收进来,这样垃圾收集器再次运行的时候,就会释放引用次数为 0 的这些值。
用引用计数法会存在内存泄露.
V8 的垃圾回收机制也是基于标记清除算法,不过对其做了一些优化。
针对新生区采用并行回收。
针对老生区采用增量标记与惰性回收。

第五章 基本引用类型

正则表达式?
let expression = / partten / flags格式可以是任何简单或复杂的正则表达式,字符串,限定类的标记可以是g 、i、u等等
正则表达式的创建方式?
字面量 let partten = /.at/i
构造函数 let patternn = new RegExp(‘at’,‘i’)

第六章 集合引用类型

对象的创建方式?

方式一:字面量,由于字面量法代码简洁, 具有封装性,用的最多
let person = {
      name:'zz',
      age:10
}
方式二:构造函数
let person2 =new Object();
person2.name='ll'

存取属性的方式?
person.name = ’ ’ person[ ‘’name “] 中括号可以通过变量访问属性
数组的创建方式?
方式一:字面量 let arr = [ 1,2 ]
方式二:构造函数 let arr = new Array (20)-- 初始长度为20 new Array (‘’20‘’)数组中放一个20 的值Array
构造函数的静态方法?
Array from 用于将类数组结构转化为数组实例,可以是对象,map等
console.log(Array.from(‘KIND’)); // K I N D
Array of 将一组参数转为数组,代替es5 的arry.prototype.slice,call(arguments)
console.log(Array.of(‘KIND’)); [‘KIND’]
数组空位?
只有逗号可以,默认就是undefined [1,,,4] [1,undef,undef,4]
Array 原型上用于检索数组内容的方法?
arr.keys arr.entries arr.values

面试题:不改变原数组的方法有哪些 ?
concat , join ,toString, slice, map, foreach, filter, every ,some, find ,findeindex ,flat ,indexof
改变数组的方法有哪些:
pop push unshift shift sort reverse splice

map 和 object 的区别?
①创建方式不同,object可以使用字面量以及构造函数的方式创建,map只能使用内置构造函数 Map 创建
const map = new Map([])
const obj = new Object()
②键的类型不一样object 的key 只能是string、symbol,如果使用数值作为key也会转为string;map的key 可以是任意的js 类型,如函数,对象,symbol 等
③object会从原型中继承属性,如果想要建一个真正的空对象,可以使用object.create(null) ;而map默认情况不包含任何键。
④object不会保留key的插入顺序,而map会保留;
⑤map提供了许多object没有的属性和方法,如set,get ,has,clear
⑥map可以迭代,而object需要以某种方式获取key然后才能迭代

 const map = new Map([
   [1, 'hello'],
   [2, 'wordl'],
]);
map.forEach((value, key) => {console.log(`value+${value}+key+${key}`);
});
const obj = {
  1:'hello',
  2:'word'
}
Object.entries(obj).forEach(([key,value])=>{
 console.log(); 
 })

⑦JSON序列化支持object 但是不支持map

 const map = new Map([
   [1, 'hello'],
   [2, 'wordl'],
]);
const json = JSON.stringify(map)

json 是 {}
⑧增删改查的性能,map性能更好
常用方法?map.set get has clear delete

选择map 还是 object ?
Map 的大多数特性都可以通过 Object 类型实现,但二者之间还是存在一些细微的差异。
①内存 : 相同内存下,map可以比object多存储50%
②性能:map的插入性能更好
③删除:map 删除更快
④查找:少量数据object更快,大量数据差不多

面试题:map和weakmap的区别?
①weakmap 的key 只能是object或者继承自object的类型,只要有一个key无效,整个初始化将失败,map的key可以是任意类型
②weakmap 的key 是弱引用,不会阻止垃圾回收
③因为weakmap 任何时候都可能被销毁,所以不可迭代,也没有clear方法
Set类型只能存储key,可以是任意类型,类似数组,自动过滤重复元素,有add set has size delete clear keys values 方法

面试题:数组常用的方法?
1.Array.push(1,2,3),向数组的末尾添加一个或多个元素,并返回新的数组长度。原数组改变。

var fruits = ["Banana", "Orange", "Apple", "Mango"];
fruits.push("Kiwi");  

2.Array.pop(),删除并返回数组的最后一个元素,若该数组为空,则返回undefined。原数组改变。

var fruits = ["Banana", "Orange", "Apple", "Mango"];
fruits.pop();   

3.Array.unshift(),向数组的开头添加一个或多个元素,并返回新的数组长度。原数组改变。

var fruits = ["Banana", "Orange", "Apple", "Mango"];
fruits.unshift("Lemon");    // 向 fruits 添加新元素 "Lemon"

4.Array.shift(),删除数组的第一项,并返回第一个元素的值。若该数组为空,则返回undefined。原数组改变。

var fruits = ["Banana", "Orange", "Apple", "Mango"];
fruits.shift();     

5.Array.concat(arr1,arr2…),合并两个或多个数组,生成一个新数组。原数组不变。

var myGirls = ["Cecilie", "Lone"];
var myBoys = ["Emil", "Tobias", "Linus"];
var myChildren = myGirls.concat(myBoys); 

6.Array.join(),将数组的每一项用指定字符连接形成一个字符串,返回报告所有数组项的字符串。默认连接字符为 “,” 逗号。

var fruits = ["Banana", "Orange","Apple", "Mango"];
document.getElementById("demo").innerHTML = fruits.join(" * "); 
// Banana * Orange * Apple * Mango

7.toString 将数组转为用逗号分开的字符串,和jion功能比较像,但是不能指定连接符号

let arr=[8,9,7,5];
 let res= arr.toString();

7.Array.reverse(),将数组倒序。原数组改变。

var fruits = ["Banana", "Orange", "Apple", "Mango"];
fruits.sort();            // 对 fruits 中的元素进行排序
fruits.reverse();   

8.Array.sort(),对数组元素进行排序。按照字符串UniCode码排序,原数组改变。

var fruits = ["Banana", "Orange", "Apple", "Mango"];
fruits.sort();  
var points = [40, 100, 1, 5, 25, 10];
document.getElementById("demo").innerHTML = points;
function myFunction2() {
        points.sort(function(a, b){return  a - b});
        document.getElementById("demo").innerHTML = points;
}

9.Array.slice(start,end),从start开始,end之前结束,不到end;如果不给end值,从start开始到数组结束。start可以给负值,-1表示数组最后位置,-2表示倒数第二个,以此类推,顾前不顾后。这个是截取返回截取部分的子数组,是一个新的数组,不会改变原数组,也不会删除。

var fruits = ["Banana", "Orange", "Lemon", "Apple", "Mango"];
var citrus = fruits.slice(3); 返回截取元素组成的新数组

10.Array.splice(index,howmany,arr1,arr2…) ,删除元素并添加元素,如果是删除:从index位置开始删除,删除howmany个元素 。如果是添加:从index位置开始删除,删除0个元素 ,后面就是要删除的元素。会直接改变原数组,返回值是删除元素数组。

var fruits = ["Banana", "Orange", "Apple", "Mango"];
fruits.splice(2, 2, "Lemon", "Kiwi");

11.Array.map(function),原数组的每一项执行函数后,返回一个新的数组。原数组不变。注意map方法一定要使用return 来返回处理(注意该方法和forEach的区别)

var numbers1 = [45, 4, 9, 16, 25];
var numbers2 = numbers1.map(myFunction);

function myFunction(value) {return value * 2}

12.Array.forEach(function),用于调用数组的每个元素,并将元素传递给回调函数。原数组不变。
(注意该方法和map的区别,若直接打印Array.forEach,结果为undefined,而map的结果是一个新数组。直接return也是undefined,map是一个新数组)。这个方法只是利用数组里面的每个元素而已,无法改变。

var txt = "";
var numbers = [45, 4, 9, 16, 25];
numbers.forEach(myFunction);
function myFunction(value, index, array) {
  txt = txt + value + "<br>"; 
}

13Array.filter(function),过滤数组中,符合条件的元素并返回一个新的数组。

var numbers = [45, 4, 9, 16, 25];
    var over18 = numbers.filter(myFunction);
    function myFunction(value, index, array) {
      return value > 18;
    }

14.Array.every(function),对数组中的每一项进行判断,若都符合则返回true,否则返回false。

  var numbers = [45, 4, 9, 16, 25];
    var allOver18 = numbers.every(myFunction);
    function myFunction(value, index, array) {
      return value > 18;
    }

15.Array.some(function),对数组中的每一项进行判断,若都不符合则返回false,否则返回true。

var numbers = [45, 4, 9, 16, 25];
    var allOver18 = numbers.every(myFunction);
    function myFunction(value) {
      return value > 18;
    }

16.Array.reduce(function),reduce() 方法接收一个函数作为累加器,数组中的每个值(从左到右)开始 缩减,最终计算为一个值

 var numbers1 = [45, 4, 9, 16, 25];
    var sum = numbers1.reduce(myFunction);
    function myFunction(total, value, index, array) {
      return total + value;
    }

reduce方法使用注意事项: 在reduce 里面不可以使用push 来累加。为什么呢?
因为我们通常是写return total.push(item) 但是push默认返回的是数组的新长度是一个数字,他不符合reduce 方法想要返回给我们的是一个数组。
解决方案1:返回数组而不是推送结果.

const peeps = ['sally','dave'];

console.log(peeps.reduce((acc,val) => {
    acc.push(['hello']);
    return acc;
},[]));

解决方案2:使用Array#concat.

The concat() method is used to merge two or more arrays. This method does not change the existing arrays,but instead returns a new array.
我会避免使用大量数据,因为它比推送效率低很多. jsPerf

const peeps = ['sally',val) => acc.concat([['hello']]),[]));

解决方案3:使用Array#map.如果结果数组的长度与给定数组的长度相同,则效果最佳.

The map() method creates a new array with the results of calling a provided function on every element in this array.
const peeps = ['sally','dave'];

console.log(peeps.map(val => ['hello']));

17.Array.find(函数)返回数组中第一个通过测试的元素的值。当数组中的元素在测试条件时返回 true 时, find() 返回符合条件的元素,之后的值不会再调用执行函数。如果没有符合条件的元素返回 undefined。

   var numbers = [4, 9, 16, 25, 29];
    var first = numbers.find(myFunction);
    function myFunction(value, index, array) {
      return value > 18;
    }

18.Array.findIndex(函数,) 返回数组中通过测试的第一个元素的索引,没有符合条件就返回-1

 var numbers = [4, 9, 16, 25, 29];
    var first = numbers.findIndex(myFunction);
    function myFunction(value, index, array) {
      return value > 18;
    }

19.Array.flat() 该犯法用于拍平数组,接受一份参数,需要拍得级数。默认就是一级的。返回的也是一个新的数组

let arr = [2,4,6,6,7,[4,9,[3,0]]];
let a = arr.flat(3);
console.log(a); // 拍平的数

20.indexOf (要查找的元素,从哪个开始找)返回数组中某个指定元素的位置,找不到就-1

var fruits = ["Apple", "Orange", "Apple", "Mango"];
var a = fruits.indexOf("Apple");

21.Array.lastIndexOf() 与 Array.indexOf() 类似,但是从数组结尾开始搜索

var fruits = ["Apple", "Orange", "Apple", "Mango"];
var a = fruits.lastIndexOf("Apple");

第七章 迭代器与生成器

第八章 对象,类

对象属性的类型:数据属性和访问器属性
当外界向对象请求属性的时候,会先经过访问器属性,然后访问器属性就会想对象属性请求数据属性,最终返回出去。
数据属性:
①writeable:就是这个属性可不可以被更改,当值为true的时候,就是可以被更改,当只为false的时候,就是不可以被更改。
②value:就是设置这个对象属性的值。
③enumerable:设置对象属性是否可以遍历出来,当值为true,就是可以被遍历,反之不会。
④configurable:设置数据属性的值是否可以更改,就是在当设置属性值为false是,在后面的代码中不可以更改该对象属性的数据属性,反之则可以进行更改。

<script>
let person = {};
      Object.defineProperty(person,"name",{
        writable:false,
        value:"dongli",
        enumerable:true,
        configurable:false
      })
</script>

在访问器函数中也存在四个属性
① set();函数可以判断外界对属性进行更改是否可以满足条件。
② get():该函数就是外界获取属性值。
③enumerable:设置对象属性是否可以遍历出来,当值为true,就是可以被遍历,反之不会。
④configurable:设置数据属性的值是否可以更改,就是在当设置属性值为false是,在后面的代码中不可以更改该对象属性的数据属性,反之则可以进行更改。

Object.defineProperty(obj, "address", {
      get: function () {
        return this._address;
      },
      set: function (value) {
        if (value > 10 && value < 60) {
          this._address = value
        } else {
          throw Error("address范围应该在10-65之间");
        }
      },
      enumerable: true,
      configurable: false,
    })

参考:https://blog.csdn.net/weixin_47450807/article/details/112417188
创建对象的几种模式
https://article.itxueyuan.com/52qO0

说下你对原型链的理解
JS是一种基于原型的语言,每一个对象都有一个_proto_ 属性指向原型对象,原型对象主要是存放一些属性和方法供所有实例共享,减少每次实例化对象中重复方法的占用内存现象。
当我们访问一个属性的时候,首先查找对象自身有没有该属性-------如果没有查找原型__proto__(__proto__指向prototype原型对象)------还没有找到,就去查找原型对象的原型(Object的原型对象-------一直找到Object为止,终点是Object.prototype.proto = null

在这里插入图片描述

判断、获取、设置原型的方法?

function Person () {};
   let person1 = new Person();
   let bid = {
    name:'zz'
}
// 判断传入的参数的原型是否是person,是返回true
let res = Person.prototype.isPrototypeOf(person1);
// 获取传入参数的原型对象
let res1 = Object.getPrototypeOf(person1);
// 设置对象的原型,影响性能,一般使用Object.create代替
let res2 = Object.setPrototypeOf(person1,bid);
console.log(res); //true
console.log(res1);
console.log(res2)

如果实例和原型有相同属性,是否会将原型属性覆盖?
①对于基本属性,不会覆盖,因为访问属性的时候在实例中存在,就屏蔽了对原型的访问。就算我们设置为null 也不会访问原型,除非使用delete person1.name 删除实例的这个属性。但是访问 的时候实例中没有依然会去原型中查找;
②对于引用类型,因为不同的实例属性中的内存地址是同一个,所以一个属性的修改会影响到原型,其他实例也会改变。

对象的遍历?

let bid = {
    name:'zz',
    age:9,
    grade:3

}
console.log(Object.values(bid)); //['zz', 9, 3]
console.log(Object.entries(bid));//  [['name', 'zz'] ['age', 9]  ['grade', 3]]
console.log(Object.keys(bid));//['name', 'age', 'grade']

继承的实现方式?
方式一:原型链继承
将父类的实例指向子类的原型

function Father() {};
    Father.prototype.getValue = function () {}
    function Son() {}
    Son.prototype = new Father();
    // 在继承来的原型上拓展方法
    Son.prototype.setValue = function () {}
    // 使用字面量方式写原型会导致
    // Son.prototype = new Father()这行代码无效,报错
    Son.prototype = {
      setValue() {
        console.log('oo');
      }
    }

原型链继承的问题:
① 引用值共享,互相影响
② 子类型在实例化时不能给父类型的构造函数传参

方式二:盗用构造函数
使用call

function Father(name) {
      this.name = name;
    };
    function Son() {
      Father.call(this,'ppp');
      this.age = 99;
    };
    let instance = new Son();
    console.log(instance);

问题:函数不能复用

方式三:组合继承
结合了原型链继承和盗用构造函数

   function Father(name) {
      this.name = name;
    };
    Father.prototype.say = function (params) {
      console.log(this.name);
    }
    function Son(name,age) {
      // 继承属性
      Father.call(this,name);
      this.age = age;
    };
    // 继承方法
    Son.prototype = new Father();

    Son.prototype.sayAge = function (params) {
      console.log(this.age);
    }
    let instance = new Son('pp',7);
    console.log(instance);

方式四:原型式继承
使用Object.create

  let father = {
      name:'baba'
    }
    let son = Object.create(father,{
      age:{
        value:'88'
      }
    })
    console.log(son.name);

非常适合不需要单独创建构造函数

方式五:寄生式继承
和原型式比较接近:创建你一个实现继承的函数,以某种方式增强对象,返回这个对象

  function object(o) {
      function F() {}
      F.prototype = 0;
      return new F()
    }
    function create (origin) {
      let clone = object(origin);
      clone.say = function (params) {
        console.log('hi'); 
      }
      return clone;
    }
    let father = {
      name:'baba'
    }
    let son2 = create(father);

方式六:寄生式组合继承
使用寄生式继承来继承父类原型,然后将返回的新对象赋值给子类原型。这是引用类型继承的最佳模式

function object(o) {
      function F() {}
      F.prototype = 0;
      return new F()
    }
    function jicheng(father,son) {
      let prototype = object(father.prototype); // 创建对象 
      prototype.constructor = son; // 增强对象
      son.prototype = prototype; // 赋值对象
    }
    function Father(params) { }
    function Son(params) {  }
    jicheng(Father,Son)

第十章 函数

函数本质就是一个对象,是 Function 的实例,函数名就是指向对象的指针
函数的表示方式?
1.函数声明式 function sun (n1,n2) {}
2.函数表达式 let sum =function (n1,n2)
3.箭头函数 let sum = (n1,n2)=> { }
4.构造函数 let sum =new Function ()性能差
箭头函数的语法?
没有参数、多个参数 — 必须使用()
只有一个参数的情况可以不使用()
大括号省略默认只执行一行代码,隐式的返回这行代码的值
如:(x)=> { return ab } (x)=> ab (x)=> return a*b 第三种写法不正确
函数名可以理解为函数的指针
默认所有函数都有一个只读属性name如:function foo () { foo.name=foo函数的参数:没有个数,类型限制 可以使用arguments 类数组对象获取所有的参数,可以和命名参数同时使用
面试题:箭头函数没有arguments 对象 ?
ES5:

var add = function(a,b){console.log(arguments);}
add(1,2);//[1, 2, callee: ƒ, Symbol(Symbol.iterator): ƒ]

ES6:

let sum = (a,b) => console.log(arguments);
sum(1,2);//Uncaught ReferenceError: arguments is not defined

解决方案:rest参数
ES6:

let sum = (...rest) => console.log(rest);
sum(1,2);//[1, 2]

面试题:为什么不能直接new 一个箭头函数?
箭头函数是ES6中的提出来的,它没有prototype,也没有自己的this指向,更不可以使用arguments参数,所以不能New一个箭头函数。

new操作符的实现步骤如下:

创建一个对象
将构造函数的作用域赋给新对象(也就是将对象的__proto__属性指向构造函数的prototype属性)
指向构造函数中的代码,构造函数中的this指向该对象(也就是为这个对象添加属性和方法)
返回新的对象

所以,上面的第二、三步,箭头函数都是没有办法执行的。

面试题:箭头函数与普通函数的区别
(1)箭头函数比普通函数更加简洁
(2)箭头函数没有自己的this
箭头函数不会创建自己的this, 所以它没有自己的this,它只会在自己作用域的上一层继承this。所以箭头函数中this的指向在它在定义时已经确定了,之后不会改变。
(3)箭头函数继承来的this指向永远不会改变

var id = 'GLOBAL';
var obj = {
  id: 'OBJ',
  a: function(){
    console.log(this.id);
  },
  b: () => {
    console.log(this.id);
  }
};
obj.a();    // 'OBJ'
obj.b();    // 'GLOBAL'
new obj.a()  // undefined
new obj.b()  // Uncaught TypeError: obj.b is not a constructor

(4)call()、apply()、bind()等方法不能改变箭头函数中this的指向
(5)箭头函数不能作为构造函数使用
构造函数在new的步骤在上面已经说过了,实际上第二步就是将函数中的this指向该对象。 但是由于箭头函数时没有自己的this的,且this指向外层的执行环境,且不能改变指向,所以不能当做构造函数使用。

(6)箭头函数没有自己的arguments
箭头函数没有自己的arguments对象。在箭头函数中访问arguments实际上获得的是它外层函数的arguments值。类数组

let h = (...args)=> {
    console.log(...args)
}
function sum () {
    console.log(arguments)
}
h(2,4,6,8); 箭头函数没有argumens ,必须使用拓展运算符来定义形参才可以拿到
sum(2,2,2,2,2); 普通函数的params也就是形参即使没有也可以直接获取到arguments 实参

(7)箭头函数没有prototype

函数声明和函数表达式的区别?
函数声明会提升声明,并在执行上下文中生成函数定义表达式没有提升

面试题:apply call bind 的区别作用?
call、apply和bind区别:
相同点:
作用相同,都是动态修改this指向;都不会修改原先函数的this指向。在非严格模式中,如果第一个参数是nul或者undefined,会把全局对象(浏览器是window)作为this的值,要注意的是,在严格模式中,null 就是 null,undefined 就是 undefined

异同点:
1、执行方式不同:
call和apply是改变后页面加载之后就立即执行,是同步代码。
bind是异步代码,改变后不会立即执行;而是返回一个新的函数。

2、传参方式不同:
call和bind传参是一个一个逐一传入,不能使用剩余参数的方式传参。
apply可以使用数组的方式传入的,只要是数组方式就可以使用剩余参数的方式传入。

call 的使用

function fn (a,b,c) {
    console.log(this.name)
    console.log(a, b, c)
}
let obj = {name: '码上游'}
fn.call(obj, 1, 2, 3)
// 输出  码上游  1 2 3

apply 的使用


 function fn (a,b,c) {
    console.log(this.name)
    console.log(a, b, c)
}
let obj = {name: '码上游'}
fn.apply(obj, [1,2,3])
// 输出  码上游  1 2 3

bind的使用

function fn (a, b, c, d) {
    console.log(this.name)
    console.log(a, b, c, d)
}
let obj = {name: '码上游'}
let bindFn = fn.bind(obj, 1, 2, 3)
bindFn('bind') // 输出  码上游  1 2 3 'bind'

面试题:什么是闭包?
首先要理解作用域的概念:
函数内部可以直接读取全局变量,但是在函数外部无法读取函数内部的局部变量。
出于种种原因,我们有时候需要获取到函数内部的局部变量。

function f1(){

    var n=999;

    function f2(){
      alert(n); // 999
    }

  }

闭包是指引用了另一个函数作用域中变量的函数,通常发生在嵌套函数中;父对象的所有变量,对子对象都是可见的,反之则不成立。
闭包可以用在许多地方。它的最大用处有两个:
一个是前面提到的可以读取函数内部的变量
另一个就是让这些变量的值始终保持在内存中,不会在f1调用后被自动清除。

什么是内存泄漏
在一个函数内部定义的函数会把外部函数的活动对象添加到自己的作用域链中,所以内部如对其引用,就不会在内存中销毁,除非内部函数被销毁如 fn = null什么是私有变量就是定义在函数内部的,局部的变量;

使用闭包的注意点
(1)由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
(2)闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。

面试题:js这this的指向问题?
①全局函数 this就是window ,在严格模式下是undefined

var Car = function() {
    console.log(this); // window
    console.log(this.Car==window.Car,Car==window.Car); // true true
}
Car();

②如果是对象中的某个属性,并且是这个对象调用的,this就是这个对象

var car = {
    name:'丰田',
    run() {
        console.log(this); // {name: "丰田", run: ƒ}
    }
}

③箭头函数的this 是外层作用域决定的

 setTimeout(() => {
            setTimeout(function() {
                console.log(this); // window
            })
            setTimeout(()=>{
                console.log(this); // obj 
            })
        })
    }

④构造函数的this是构造的对象 构造的实例

var Car = function(name) {
    this.name = name;
    console.log(this); // Car {name: "亚洲龙"}
                       // Car {name: "汉兰达"}
}
var myCar_1 = new Car('亚洲龙');
var myCar_2 = new Car('汉兰达');

⑤在事件中,this 指的是接收事件的元素。

<button onclick="this.style.display='none'">
  点击来删除我!
</button>

⑥一般通过apply call bind 显示调用函数,函数体的this就会被绑定到指定参数的对象

fn.apply(obj)

注意事项:
①在嵌套关系中,this会指向最后调用它的对象。foo.fn 和 foon.fn() 是不一样的
②匿名定时器的this是window
③apply 是直接进行函数调用的,而bind不会执行相关函数,而是返回一个函数,需要手动调用。 fn.bind(obj)()
④在构造函数中,如果出现了return的情况,分两种。如果return的是一个对象,那么this就是这个返回的对象。如果是基本数据类型,那么this指向的还是构造函数的实例。
⑤一般通过apply call bind new 对this进行绑定叫显示绑定,而通过函数调用关系确定this叫隐式绑定。new>显示的优先级>隐式
⑥剪头函数绑定的this不能修改,通过const绑定的是局部的变量,所以当this指向了window的时候!就是undefined了

面试题:es5和es6的区别,说一下你所知道的es6
ECMAScript5,即ES5,是ECMAScript的第五次修订,于2009年完成标准化
ECMAScript6,即ES6,是ECMAScript的第六次修订,于2015年完成,也称ES2015
ES6是继ES5之后的一次改进,相对于ES5更加简洁,提高了开发效率
ES6新增的一些特性:
1)let声明变量和const声明常量,两个都有块级作用域
ES5中是没有块级作用域的,并且var有变量提升,在let中,使用的变量一定要进行声明
2)箭头函数
ES6中的函数定义不再使用关键字function(),而是利用了()=>来进行定义
3)模板字符串
模板字符串是增强版的字符串,用反引号(`)标识,可以当作普通字符串使用,也可以用来定义多行字符串
4)解构赋值
ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值
5)for of循环
for…of循环可以遍历数组、Set和Map结构、某些类似数组的对象、对象,以及字符串
6)import、export导入导出
ES6标准中,Js原生支持模块(module)。将JS代码分割成不同功能的小块进行模块化,将不同功能的代码分别写在不同文件中,各模块只需导出公共接口部分,然后通过模块的导入的方式可以在其他地方使用
7)set和map数据结构
Set数据结构,类似数组。所有的数据都是唯一的,没有重复的值。它本身是一个构造函数
8)… 展开运算符
可以将数组或对象里面的值展开;还可以将多个值收集为一个变量
9)修饰器 @
decorator是一个函数,用来修改类甚至于是方法的行为。修饰器本质就是编译时执行的函数
10)class 类的继承
ES6中不再像ES5一样使用原型链实现继承,而是引入Class这个概念
11)async、await
使用 async/await, 搭配promise,可以通过编写形似同步的代码来处理异步流程, 提高代码的简洁性和可读性
async 用于申明一个 function 是异步的,而 await 用于等待一个异步方法执行完成
12)promise
Promise是异步编程的一种解决方案,比传统的解决方案(回调函数和事件)更合理、强大
13)Symbol
Symbol是一种基本类型。Symbol 通过调用symbol函数产生,它接收一个可选的名字参数,该函数返回的symbol是唯一的
14)Proxy代理
使用代理(Proxy)监听对象的操作,然后可以做一些相应事情

什么是斐波那契数列?
1,1,2 从第三项开始每项都是前两项的和递归实现:

function fib (n) {
    if(n <2{ return
    } 
    else return fib(n-1+ (n-2;
}

面试题:拓展运算符?
(1)对象扩展运算符
对象的扩展运算符(…)用于取出参数对象中的所有可遍历属性,拷贝到当前对

象之中。
let bar = { a: 1, b: 2 };
let baz = { ...bar }; // { a: 1, b: 2 }

如果用户自定义的属性,放在扩展运算符后面,则扩展运算符内部的同名属性会被覆盖掉。

let baz = {...bar, ...{a:2, b: 4}};  // {a: 2, b: 4}

相当于:Object.assign方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target)。
Object.assign(目标对象 , 源对象)
Object.assign方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target)。Object.assign(目标对象 , 源对象)

(2)数组扩展运算符

数组的扩展运算符可以将一个数组转为用逗号分隔的参数序列,且每次只能展开一层数组。

console.log(...[1, 2, 3])
// 1 2 3
console.log(...[1, [2, 3, 4], 5])
// 1 [2, 3, 4] 5
const numbers = [1, 2];
add(...numbers) // 3
复制数组
const arr1 = [1, 2];
const arr2 = [...arr1];
合并数组
const arr1 = ['two', 'three'];
const arr2 = ['one', ...arr1, 'four', 'five'];

说说你对promise的理解
promise 是异步编程的解决方案,里面保存着某个未来才会结束的事件;
promise有三种状态pending待定、fulfilled兑现、rejected拒绝
Promise对象的状态不受外界影响;
一旦状态改变,就不会再变,任何时候都可以得到这个结果;
Promise接受一个「函数」作为参数,该函数的两个参数分别是resolve(在异步操作成功时将参数传递出去)和reject(在异步操作失败将参数传递出去)
then()方法: then 方法就是异步操作执行完后,用链式调用的方式执行回调函数。
catch()方法: 当执行 resolve 的回调抛出异常了(代码出错了),会进到这个 catch 方法中。
Promise.finally( ) 无论什么状态都会执行,无法获取状态,避免冗余代码
all()方法: Promise 的 all方法在所有异步操作执行完后才执行回调。
race()方法:而 race 的话只要有一个异步操作执行完毕,就立刻执行 then 回调。

使用try catch 捕捉一个promise 可以吗
try catch 只能捕捉到同步的错误,如果try 一个promise 的错误不会抛到同步线程中,而是通过浏览器的异步消息队列来处理,所以会抛出错误但捕捉不到错误。

面试题:说下async、await
async 是用来声明异步函数的,返回一个promise
await关键字暂停异步函数的执行,等待promise 的解决,但是await只能用在asycn中,不能用于同步函数;
await到底在等待什么?
当js运行到await关键字的时候,会记录暂停,为右边的值向消息队列中添加一个任务并退出当前异步函数,等同步戴拿执行完,js再从消息队列中取出异步任务继续执行异步函数。

第十二章 BOM

BOM属性对象方法
1、window对象
2、location对象
3、navigator对象
关于浏览器的特殊标准,包含了检测插件,注册处理程序的方法
4、screen对象
大多是用来获取浏览器的外部窗口信息
5、history对象
详解 : https://www.pianshen.com/article/74901346299/

第十四章 DOM

document表示每个文档的根节点,根节点唯一的子节点就是html元素,也就是文档元素,每个文档只有一个文档元素;
创建元素,docment.createElement ( ) 还没添加到文档树中,浏览器不会显示出来,需要使用appendChid() insertBefore()
replaceChild() 来插入到文档流中;
面试题:BOM和DOM 的区别?
BOM是browser object model的缩写,简称浏览器对象模型。是用来获取或设置浏览器的属性、行为,例如:新建窗口、获取屏幕分辨率、浏览器版本号等。 比如 alert();弹出一个窗口,这属于BOM BOM的最根本对象是window。
DOM是Document ,简称文档对象模型。是用来获取或设置文档中标签的属性,例如获取或者设置input表单的value值。document.getElementById(“”).value; 这属于DOM DOM最根本对象是document
面试题:说下DOM常见的操作?
1)DOM 节点的获取
DOM 节点的获取的API及使用:
getElementById // 按照 id 查询
getElementsByTagName // 按照标签名查询
getElementsByClassName // 按照类名查询
querySelectorAll // 按照 css 选择器查询

// 按照 id 查询
var imooc = document.getElementById('imooc') // 查询到 id 为 imooc 的元素
// 按照标签名查询
var pList = document.getElementsByTagName('p')  // 查询到标签为 p 的集合
// 按照类名查询
var moocList = document.getElementsByClassName('mooc') // 查询到类名为 mooc 的集合
// 按照 css 选择器查询
var pList = document.querySelectorAll('.mooc') // 查询到类名为 mooc 的集合

2)DOM 节点的创建

创建一个新节点,并把它添加到指定节点的后面。 已知的 HTML 结构如下:
要求添加一个有内容的 span 节点到 id 为 title 的节点后面,做法就是:

// 首先获取父节点
var container = document.getElementById('container')
// 创建新节点
var targetSpan = document.createElement('span')
// 设置 span 节点的内容
targetSpan.innerHTML = 'hello world'
// 把新创建的元素塞进父节点里去
container.appendChild(targetSpan)

3)DOM 节点的删除
删除指定的 DOM 节点, 已知的 HTML 结构如下:
需要删除 id 为 title 的元素,做法是:

// 获取目标元素的父元素
var container = document.getElementById('container')
// 获取目标元素
var targetNode = document.getElementById('title')
// 删除目标元素
container.removeChild(targetNode)

者通过子节点数组来完成删除:

 获取目标元素的父元素var container = document.getElementById('container')
 获取目标元素var targetNode = container.childNodes[1]
 删除目标元素container.removeChild(targetNode)

4)修改 DOM 元素
修改 DOM 元素这个动作可以分很多维度,比如说移动 DOM 元素的位置,修改 DOM 元素的属性等。
将指定的两个 DOM 元素交换位置, 已知的 HTML 结构如下:
现在需要调换 title 和 content 的位置,可以考虑 insertBefore 或者 appendChild:

// 获取父元素
var container = document.getElementById('container')   
 
// 获取两个需要被交换的元素
var title = document.getElementById('title')
var content = document.getElementById('content')
// 交换两个元素,把 content 置于 title 前面
container.insertBefore(content, title)

第十五章 DOM扩展

html5 通过给所有元素增加了classList 属性,有add、contains、remove、toggle(已存在删除,不存在则添加)
div.classList.add ( )
document.hasFoucs() 返回一个布尔值,判断文档当前是否处于焦点;
document.activeElement 返回当前拥有焦点的元素;
document.readyState loading 文档正在加载… complete 文档加载完成
document.compatMode 检查页面渲染模式,标准、怪异;
自定义数据属性,data-xx = ‘xxx’ 通过元素的dataset属性可以访问,示例:

第十六章 DOM演进

元素尺寸 p473

第十七章 事件

mouseover 和 mouseenter 的区别?
mouseover:当鼠标移入元素或其子元素都会触发事件,所以有一个重复触发,冒泡的过程。对应的移除事件是mouseout
mouseenter:当鼠标移除元素本身(不包含元素的子元素)会触发事件,也就是不会冒泡,对应的移除事件是mouseleave

事件对象?
事件处理程序的唯一参数event;常用属性:
event.currentTarget 当前事件处理程序所在的元素
event.target 事件目标
event.eventPhase 调用事件处理程序的阶段1捕获2目标3冒泡
event.type 触发的事件类型
event.stopPropagation 取消所有捕获或冒泡
event.preventDefault 取消事件默认行为
事件类型?
用户界面事件:load、unload、resize、scroll、select、error、
焦点事件:focus、blur
鼠标事件:click、mousedown、mouseup、mouseenter(光标从元素外部移动到内部时触发,不会冒泡也不会在经过后代元素触发)、mouseleave(不会冒泡也不会在经过后代元素触发)、mouseover(光标从元素外部移动到内部时触发)、mousemove(光标在元素上移动时反复触发)、mouseout(光标从一个元素移动到另一个元素时触发)

滚轮事件:mousewheel

document.addEventListener("mousewheel", (event) => {
    console.log(event.clientX,event.clientY,'光标在视口中的坐标');
    console.log(event.pageX,event.pageY,'光标在页面的坐标,包含滚动距离');
    console.log(event.screenX,event.screenY,'屏幕坐标');
  });

输入事件:textInput

 const my = document.getElementById("myText");
    my.addEventListener("textInput", (event) => {
      console.log(event.data); // 只能拿到每次输入的字符
    });

键盘事件:keydown、keyup
合成事件:pageshow、pagehide

第十八章 动画与canvas

为什么会有requestAnimationFrame?
早期js实现动画都是通过定时器来实现的,问题:浏览器的计时器精度不足毫秒,同步代码优先级更高,都导致了定时器执行的时机不准确;
window.requestAnimationFrame() 告诉浏览器——你希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行;requestAnimationFrame 仅仅只是在浏览器每一次刷新渲染新帧之前的一个切片或者说一个时机.你用或者不用,这个切片都在那里

// 使用定时器的方式,由于存在eventLoop导致代码优先级的问题.会出现定时并不准确的情况.
    let box = document.querySelector("#box"),
      rbox = document.querySelector("#request-animation-box");
    // normalWay();
    function normalWay() {
      let id = setInterval(() => {
        box.style.width = `${++box.clientWidth}px`;
        if (box.clientWidth > 300) clearInterval(id);
      }, 17); // 1000ms / 17 = 每分钟 60 fps
    }

    // rWay
    rWay();
    function rWay() {
      rbox.style.width = `${++rbox.clientWidth}px`; // 场景内,有物体的变化,必须刷新浏览器
      if (rbox.clientWidth <= 300) {
        var id = requestAnimationFrame(rWay);
      } else {
        cancelAnimationFrame(id);
      } // 因为必须要刷新浏览器,所以会调用 requestAnimationFrame .
      // requestAimationFrame 的参数是一个函数.
      // 但是只调用一次,它只会在下一帧渲染之前调用.后续帧刷新时,就不会调用.
      // 所以需要不停的调用这个函数才可以.
    }

第二十章 JS API

为什么要进行编码和解码?
数据在网络上传输和处理时,实际上要经过许多不同的设备,不同设备上的系统对于数据处理存在差异性,可能会造成数据传输和处理的失败;
编码:文字>数字;人能看懂的内容转成计算机能理解的数字。解码:数字>文字;计算机能理解的数字转成人能看懂的内容。
常见的有 URL编码 和 base64
为什么要对url进行编码?
一般来说,URL只能使用英文字母、阿拉伯数字和某些标点符号,不能使用其他文字和符号,如果URL中有汉字或者其他特殊字符,就必须编码后使用;
常见的编码方式?
1、escape() / unescape()
规则:除了ASCII字母、数字、标点符号"@ * _ + - . /"以外,对其他所有字符进行编码(注:空格会被转化为+字符);
escape用来对某个字符串进行编码的,不能直接用于url编码,尽管url也是个字符串。所以如果你需要编码整个URL,那么用
decodeURI()是专门着眼于对整个url进行编码的。
ECMAScript v3 反对使用
escape()方法,现在已经很少使用,应用使用 decodeURI() 和 decodeURIComponent() 替代它
2、encodeURI() / decodeURI()
用途:encodeURI()是Javascript中真正用来对URL编码的函数。
规则:不会对下列字符编码
ASCII字母 数字 ~ ! @ # $ & * ( ) = : / , ; ? + '(注:这是单引号,若果双引号"会被编码%22)进行编码
结果:输出utf-8形式,并且在每个字节前加上%。
3、encodeURIComponent() / decodeURIComponent
用途:与encodeURI()的区别是,它用于对URL的组成部分(如查询参数、路径等)进行个别编码,而不用于对整个URL进行编码。
规则: 不会对下列字符编码
ASCII字母 数字 ~ ! * ( ) '(注:这是单引号,若果双引号"会被编码%22)进行编码
结果:和encodeURI一样,输出utf-8形式,并且在每个字节前加上%
4、区别
encodeURIComponent比encodeURI编码的范围更大,即
@ # $ & = : / , ; ? +,这些在encodeURI()中不被编码的符号,在encodeURIComponent()中统统会被编码
如果只是编码字符串,不和URL有半毛钱关系,那么用escape。当然该方法已经不推荐使用,所以不用深究
encodeUR用于编码整个URL
encodeURIComponent用于编码url中的查询参数或路径等(若参数为url,还是相当于参数,用
encodeURIComponent())

FileReader 类型
允许 Web 应用程序异步读取存储在用户计算机上的文件(或原始数据缓冲区)的内容,使用 File 或 Blob 对象指定要读取的文件或数据。

 input.addEventListener("change", (event) => {
      let files = event.target.files,
        reader = new FileReader();
      if (/image/.test(files[0].type)) {
        reader.readAsDataURL(files[0]);
        //开始读取指定的Blob中的内容。一旦完成,result属性中将包含一个data: URL 格式的 Base64 字符串以表示所读取文件的内容。
      } else {
        reader.readAsText(files[0]); //开始读取指定的Blob中的内容。一旦完成,result属性中将包含一个字符串以表示所读取的文件内容。
      }
      reader.onerror = () => {}; //处理error事件。该事件在读取操作发生错误时触发。
      reader.onprogress = () => {}; //处理progress事件。该事件在读取Blob时触发。
      reader.onload = () => {}; //处理load事件。该事件在读取操作完成时触发。
    });

什么是Blob
二进制大对象(binary larget object) 是表示一个不可变、原始数据的类文件对象;是js对不可修改二进制数据的封装类型
当需要读取部分文件时,file对象提供了一个slice方法,这个方法返回一个Blob实例;
创建一个blob

const blob = new Blob([JSON.stringify(obj, null, 2)], {type : 'application/json'});

从blob中读取数据

reader.readAsArrayBuffer(blob);

什么是对象url?
也叫Blob URL ,指的是引用存储在file或者blob中数据的url
使用这个方法传入file 或者blob对象,这个函数返回一个指向内存地址的字符串,可以在DOM中直接使用;

let url = window.URL.createObjectURL(new Blob([typedArray.buffer]),

什么是ArrayBuffer ?
对象用来表示通用的、固定长度的原始二进制数据缓冲区;构造函数创建一个以字节为单位的给定长度的新 ArrayBuffer

const buffer = new ArrayBuffer(8)

Page Visibility API

visibilitychange 这个事件,该事件会在文档从隐藏到可见的时候触发;
document.addEventListener("visibilitychange", function() {
  console.log( document.visibilityState );hidden visible prerender
  console.log( document.hidden );true false 这个是为了向后兼容才继续被浏览器支持,应该优先使用上面的
});

媒体标签?
video 和 audio 有很多通用的属性和事件

<video autoplay loop muted controls width="200" height="200">
      <source src="" />
      <!-- 
        ended 是否播放完成
        paused 是否暂停
        played 是否已经在播放
        volume 取得或设置当前音量 -->
    </video>
    <audio src="" id="palyer">音频</audio>
    const palyer = document.getElementById("palyer");
    palyer.addEventListener("click", (event) => {
      if (palyer.paused) {
        palyer.play();
      } else {
        palyer.paused();
      }
    });

audio 原生js构造函数
let audio = new Audio(“a.mp3”);下载指定文件使用paly() 方法播放,
这样的好处就是不需要插入dom就可以直接传入一个音频
在ios中调用play() 方法会弹出一个对话框,请求用户授权播放声音;

第二十三章 JSON

面试题: 数据交互的格式是什么?
JSON是一种轻量级的数据格式,可以方便的表示复杂的数据结构,用于和服务器间通信;虽然xml也可以实现,但是json更简洁,js的支持也更好,并且所有浏览器都已经原生支持全局json对象;
json字符串必须使用双引号,单引号会有语法错误;
json的对象不需要声明,最后没有分号,属性名要加双引号;

{
    "name":"aaa",
    "age":10,
    "authors":[
        "jhon","mary"
    ]
}
JSON.stringify;将一个js对象序列化为一个json字符串
JSON.parse;将序列化json转为js对象
JSON.stringify(book, ["title", "edition"]);第二个参数是数组,那么序列化的对象中只会包含这两个属性
JSON.stringify(book,(key,val)=>{
 return key; 第二个参数是函数,相当于一个过滤器
})
JSON.stringify(book,null,8) 第三个参数控制缩进和空格

什么是webSocket?
通过一个长时链接实现与服务器全双工、双向的通信;在js中创建websocket时,一个http请求会发送到服务器链接,服务器响应后,头部协议从http切换到了webscoket协议,所以路径不能使用http而是使用ws:/

  let socket = new WebSocket('ws://www.explame.com/server.php');
    socket.send(new Blob(['a'])); // 向服务器发送数据
    socket.onmessage = function (event) {
        let data = event.data; // 服务器发送消息时,客户端会触发mesage事件
    }
    socket.onopen = () =>{} //链接成时触发
    socket.onerror = () =>{}
    socket.onclose = () =>{}

跨域问题解决方案
js事件循环机制
说一下Commonjs和AMD和CMD
ES6模块与CommonJS模块有什么区别?

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值