前端JS自查

常见的循环遍历

for、forEach、for …of

  • 三者都是基本的由左到右遍历数组
  • forEach 无法跳出循环;for 和 for …of 可以使用 break 或者 continue 跳过或中断。
  • for …of 直接访问的是实际元素。for 遍历数组索引,forEach 回调函数参数更丰富,元素、索引、原数组都可以获取。
  • for …of 与 for 如果数组中存在空元素,同样会执行。

some、every

  • 二者都是用来做数组条件判断的,都是返回一个布尔值
  • 二者都可以被中断
  • some 若某一元素满足条件,返回 true,循环中断;所有元素不满足条件,返回 false。
  • every 与 some 相反,若有益元素不满足条件,返回 false,循环中断;所有元素满足条件,返回 true。

filter、map

  • 二者都是生成一个新数组,都不会改变原数组(不包括遍历对象数组是,在回调函数中操作元素对象)
  • 二者都会跳过空元素。有兴趣的同学可以自己打印一下
  • map 会将回调函数的返回值组成一个新数组,数组长度与原数组一致。
  • filter 会将符合回调函数条件的元素组成一个新数组,数组长度与原数组不同。
  • map 生成的新数组元素是可自定义。
  • filter 生成的新数组元素不可自定义,与对应原数组元素一致。

链接: link.

array.join()

join() 方法用于把数组中的所有元素放入一个字符串。
元素是通过指定的分隔符进行分隔的。

var arr = new Array(3)
arr[0] = "George"
arr[1] = "John"
arr[2] = "Thomas"
document.write(arr.join("."))//George.John.Thomas

str.split()

将字符串以分隔符隔开成数组

"2:3:4:5".split(":")	//将返回["2", "3", "4", "5"]
"|a|b|c".split("|")	//将返回["", "a", "b", "c"]

js结束循环continue,break和return的差别

使用continue语句跳出当前循环并继续执行下一次循环。
if=3时,0 1 2 4 5 执行完了

使用break语句立即退出for循环并不再执行该循环体。
if=3时,0 1 2 执行完了

return 语句官方定义是会终止函数的执行并返回函数的值。使用return语句会直接退出循环所在的函数体,该循环之后的函数体内的语句也不会执行。
if=3时,0 1 2

indexOf()

indexOf() 方法可返回某个指定的字符串值在字符串中首次出现的位置。

stringObject.indexOf(searchvalue,fromindex)
searchvalue 必需。规定需检索的字符串值。
fromindex 可选的整数参数。规定在字符串中开始检索的位置。它的合法取值是 0 到 stringObject.length - 1。如省略该参数,则将从字符串的首字符开始检索。

注释:indexOf() 方法对大小写敏感!
如果要检索的字符串值没有出现,则该方法返回 -1。

addEventListener() 和 removeEventListener()事情监听

事件流
冒泡型事件流
事件的传播是从最特定的事件目标到最不特定的事件目标。即从DOM树的叶子到根。【推荐】

捕获型事件流
事件的传播是从最不特定的事件目标到最特定的事件目标。即从DOM树的根到叶子。

DOM标准采用捕获+冒泡。两种事件流都会触发DOM的所有对象,从document对象开始,也在document对象结束。

addEventListener(“事件名” , “事件处理函数” , “布尔值”);

false 事件冒泡 true 事件捕获

removeEventListener("事件名",事件处理函数,布尔值);

阻止默认事件preventDefault()

document.body.addEventListener('touchmove', function (event) {
    event.preventDefault();
},false)

阻止事件冒泡stopPropagation()

 function showType(event){
     alert(event.type);
     event.stopPropagation();
 }

枚举

在js的标准中是没有枚举这一说的,但在typescript中是有这个类型的。枚举的意思是把所有相关的子数据,都收集起来

enum Weekday {
  Monday,
  Tuseday,
  Wednesday,
  Thursday,
  Friday,
  Saturday,
  Sunday
}

然后直接通过Weekday.Monday就可以获取到对应的值,默认情况下,第一个枚举值是0,然后每个后续值依次递增 1。

当然,也可以把枚举值设置成其他的类型,如字符串:

enum EvidenceTypeEnum {
  UNKNOWN = '',
  PASSPORT_VISA = 'passport_visa',
  PASSPORT = 'passport',
  SIGHTED_STUDENT_CARD = 'sighted_tertiary_edu_id',
  SIGHTED_KEYPASS_CARD = 'sighted_keypass_card',
  SIGHTED_PROOF_OF_AGE_CARD = 'sighted_proof_of_age_card'
}

前端如何使用枚举: link.

sort()

sort() 方法用于对数组的元素进行排序,并返回数组。默认排序顺序是根据字符串UniCode码。因为排序是按照字符串UniCode码的顺序进行排序的,所以首先应该把数组元素都转化成字符串(如有必要),以便进行比较。

arrayObject.sort(sortby)

注意:sortby必须是函数,规定排序顺序。可选参数

Math.random()

random() 方法可返回介于 0 ~ 1 之间的一个随机数。

  1. 获取 [0,x) 的随机整数

    parseInt(Math.random() * x);

  2. 获取 [0,y] 的随机整数

    Math.floor(Math.random() * (y+1));

  3. 获取 [n,m) 的随机整数

    Math.floor(Math.random() * (m-n) + n);

  4. 获取 [p,q) 的随机整数

    parseInt(Math.random() * (q-p+1) + p);

清空数组

arr.splice(0,arr.length);
arr.length = 0;  //(效率最快)
arr = [];

diff算法

Diff算法的性能优势就是在对比新旧两个 DOM 的不同的时候只对比同一级别的 DOM 节点,一旦发现不同,后续 DOM子节点将被删掉不作对比
在对比发现不同后,将不同的子节点进行简单的创建删除操作

虚拟DOM提升渲染能力

用操作虚拟DOM来代替操作真实DOM,不仅可以保存DOM状态,而且虚拟DOM在更新时只会修改变化的DOM,提升了复杂视图的渲染能力。

trim()

去除字符串左右两端的空格

function myTrim(x) {
    return x.replace(/^\s+|\s+$/gm,'');
}

窗口变化触发resize

window.onresize = function() {
  alert('触发成功');
};

setInterval

定时器指定的时间间隔,表示的是何时将定时器的代码添加到消息队列,而不是何时执行代码。所以真正何时执行代码的时间是不能保证的,取决于何时被主线程的事件循环取到,并执行。

setInterval 有两个缺点:
1、使用 setInterval 时,某些间隔会被跳过;
2、可能多个定时器会连续执行;

可以使用普通定时器来模仿循环定时器作用,来规避这些缺点

function mySettimeout(fn, t) {
    let timer = null;
    function interval() {
      fn();
      timer = setTimeout(interval, t);
    }
    interval();
    return {
      cancel:()=>{
        console.log('暂停')
        clearTimeout(timer)
      }
    }
  }

或者

var i = 0;
function time(){  //每隔1秒让++i
    console.log(++i);
    setTimeout(time,1000);
}
time(); //执行time函数

btn.onclick = function(){
    time = null; //重写time函数,从而起到关闭定时器的效果
}

链接: link.

unshift()

arr.unshift() 方法可向数组的开头添加一个或更多元素,并返回新的长度,直接修改原有的数组。

map()

方法创建一个新数组,其结果是该数组中的每个元素都调用一个提供的函数后返回的结果。

array.map(function(currentValue,index,arr), thisValue)

let newArr = arr.map((item)=>{item * 10});

assign()

Object.assign() 方法用于将所有可枚举属性的值从一个或多个源对象分配到目标对象。它将返回目标对象。

var o1 = { a: 1 };
var o2 = { b: 2 };
var o3 = { c: 3 };
 
var obj = Object.assign(o1, o2, o3);
console.log(obj); // { a: 1, b: 2, c: 3 }
console.log(o1);  // { a: 1, b: 2, c: 3 }, 

深拷贝和浅拷贝

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

深拷贝常见实现方式: JSON.parse(JSON.srtingfy())、递归实现

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

浅拷贝常见方法: 数组的slice()、concat()、Array.from()、Object.assign()、…(拓展运算符)、递归方式实现
链接: link.

递归和迭代

递归:程序调用自身的编程技巧称为递归,就是函数重复的自己调用自己。在使用递归时,必须有一个明确的递归结束条件,称为递归出口.

//递归的实例:折半查找法
function sort(arr){
   if (arr.length<=1) {
       return arr;
   }
   var pivotIndex=Math.floor(arr.length/2);
   var privot = arr.splice(pivotIndex,1)[0];
   var left = [];
   var right = [];
   
   for (var i=0;i<arr.length;i++) {
       if (arr[i]<=privot) {
           left.push(arr[i]);
       } else{
           right.push(arr[i]);
       }
   }
   
   //递归
   return sort(left).concat([privot],sort(right));
 }

迭代:利用变量的原值推算出变量的一个新值.如果递归是自己调用自己的话,迭代就是A不停的调用B。

//迭代的实例:计算1-100所有实数的和
var v=1for(var i=100;i<=100;1--){
 v=v+i;
}

链接: link.
链接: 拓展:迭代、递推、穷举、递归.

什么是数组

  • 普通对象是使用字符串作为属性名的,而数组是使用数字来作为索引来操作元素。索引:从0开始的整数就是索引。
  • 数组的元素可以是任意的数据类型,也可以是对象,也可以是函数,也可以是数组。

原型和原型链

基本数据类型:Number,String,Boolean,Undefined,Null,Symbol。
引用数据类型:Object,Function,Date,Array,RegExp等。

所有的对象都有__ptoto__属性,__proto__是一个访问器属性,它指向创建它的构造函数的原型prototype。

  • prototype:是函数的一个属性(每个函数都有一个prototype属性),这个属性是一个指针,指向一个对象。它是显示修改对象的原型的属性。
  • proto:是一个对象拥有的内置属性(请注意:prototype是函数的内置属性,__proto__是对象的内置属性),是JS内部使用寻找原型链的属性。

原型:所有的函数都有一个特殊的属性prototype(原型),prototype属性是一个指针,指向的是一个对象(原型对象),原型对象中的方法和属性都可以被函数的实例所共享。所谓的函数实例是指以函数作为构造函数创建的对象,这些对象实例都可以共享构造函数的原型的方法。

原型链:原型链是用于查找引用类型(对象)的属性,查找属性会沿着原型链依次进行,如果找到该属性会停止搜索并做相应的操作,否则将会沿着原型链依次查找直到结尾。常见的应用是用在创建对象和继承中。
由于原型对象的本身也是对象,它也有自己的原型,而它的原型对象又可以有自己的原型,这样就组成了一条链,就是原型链,在访问对象的属性时,如果在对象本身中没有找到,则回去原型链中查找,如果找到返回值,否则返回Undefined。

构造函数:原型对象包含一个constructor属性,对应创建所有指向该原型的实例的构造函数。

var p = new person() ;
p._proto_=person.prototype

当一个函数被用作构造函数来创建实例的时候,该函数的prototype属性值作为原型负值给它创建出来的实例对象。也就是说构造函数的prototype和他所实例化出来的对象的_proto_是相同的。

当试图得到一个对象的某个属性时,如果这个对象本身没有这个属性,那么回去它的__proto__(即它的构造函数的prototype中寻找)

闭包

概念:闭包就是能够读取其他函数内部变量的函数。由于在Javascript语言中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成“定义在一个函数内部的函数”。所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。

例子:函数b就是一个闭包函数,用于获取函数a内部的变量i。当函数a的内部函数b,被函数a外的一个变量c引用的时候,就创建了一个闭包。

function a() {
  var i = 0;
  function b() {
    alert(++i)
  }
  return;
}
var c = a();
c();

作用:闭包可以用在许多地方。它的最大用处有两个

  • 可以读取函数内部的变量
  • 让这些变量的值始终保持在内存中

注意事项

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

什么是内存泄漏

首先,需要了解浏览器自身的内存回收机制。
每个浏览器会有自己的一套回收机制,当分配出去的内存不使用的时候便会回收;内存泄露的根本原因就是你的代码中分配了一些‘顽固的’内存,浏览器无法进行回收,如果这些’顽固的’内存还在一直不停地分配就会导致后面所用内存不足,造成泄露。

闭包造成内存泄漏

因为闭包就是能够访问外部函数变量的一个函数,而函数是必须保存在内存中的对象,所以位于函数执行上下文中的所有变量也需要保存在内存中,这样就不会被回收,如果一旦循环引用或创建闭包,就会占据大量内存,可能会引起内存泄漏

如何避免闭包引起的内存泄漏
在退出函数之前,将不使用的局部变量全部删除,可以使变量赋值为null。

//这段代码会导致内存泄露
    window.onload = function(){
        var el = document.getElementById("id");
        el.onclick = function(){
            alert(el.id);
        }
    }

//解决方法为
    window.onload = function(){
        var el = document.getElementById("id");
        var id = el.id; //解除循环引用
        el.onclick = function(){
            alert(id); 
        }
        el = null; // 将闭包引用的外部函数中活动对象清除
    }

闭包的使用场景

封装功能时(需要使用私有的属性和方法),函数防抖、函数节流、函数柯里化、给元素伪数组添加事件需要使用元素的索引值。
链接: link.

set

链接: link.

slice()

通过索引位置获取新的数组,该方法不会修改原数组,只是返回一个新的子数组。arrayObj.slice(start,end)

从start开始,包括start,到end截止,不包括end。

reverse()

颠倒一下数组中元素的顺序,会改变原来的数组,而不会创建新的数组,arrayObject.reverse()

includes()

includes() 方法用于判断字符串是否包含指定的子字符串。string.includes(searchvalue, start),其中start从哪个位置开始

如果找到匹配的字符串返回 true,否则返回 false。

filter过滤

filter()方法会创建一个新数组,原数组的每个元素传入回调函数中,回调函数中有return返回值,若返回值为true,这个元素保存到新数组中;若返回值为false,则该元素不保存到新数组中;原数组不发生改变。array.filter(function(currentValue,index,arr), thisValue)

let newArr = this.exhibitionData.filter((item,index)=> {
	return item.eqmName == e
})
this.exhibitionData = newArr

作用域, 作用域链, 闭包

链接: link.

apply()和call()及bind()

call 的参数是直接放进去的,第二第三第n个参数全都用逗号分隔,直接放到后面obj.myFun.call(db,‘江西’, …, ‘string’)。
apply的所有参数都必须放在一个数组里面传进去。
call和apply都是调用后直接执行,而bind则是调用后返回一个新的函数给你执行

  • apply()方法 接收两个参数,一个是函数运行的作用域(this),另一个是参数数组。
    语法:apply([thisObj [,argArray] ]);,调用一个对象的一个方法,2另一个对象替换当前对象。
  • call()方法 第一个参数和apply()方法的一样,但是传递给函数的参数必须列举出来。
    语法:call([thisObject[,arg1 [,arg2 [,…,argn]]]]);,应用某一对象的一个方法,用另一个对象替换当前对象。
    链接: link.

原型

prototype的概念:
每一个构造函数都有一个prototype属性,这个属性会在生成实例的时候,成为实例对象的原型对象。javascript的每个对象都继承另一个对象,后者称为“原型”(prototype)对象。
一方面,任何一个对象,都可以充当其他对象的原型;另一方面,由于原型对象也是对象,所以它也有自己的原型。null也可以充当原型,区别在于它没有自己的原型对象。
JavaScript 继承机制的设计就是,原型的所有属性和方法,都能被子对象共享。
下面,先看怎么为对象指定原型。
每一个构造函数都有一个prototype属性,这个属性会在生成实例的时候,成为实例对象的原型对象

链接: link.

运行机制、事件队列和循环

  1. 同步程序
  2. process.nextTick
  3. 微任务(.then)
  4. 宏任务 (计时器、ajax、读取文件)
  5. setimmediate

链接: link.

递归和迭代

递归和迭代都是循环中的一种。
简单地说,递归是重复调用函数自身实现循环。迭代是函数内某段代码实现循环,而迭代与普通循环的区别是:循环代码中参与运算的变量同时是保存结果的变量,当前保存的结果作为下一次循环计算的初始值。
递归循环中,遇到满足终止条件的情况时逐层返回来结束。迭代则使用计数器结束循环。当然很多情况都是多种循环混合采用,这要根据具体需求。

链接: link.

函数声明提升优先级

“函数会首先被提升,然后才是变量”。

var getName = function(){
    console.log(2);
}
function getName (){
    console.log(1);
}
getName();
 
// 2

解释为:

function getName(){    //函数声明提升到顶部
    console.log(1);
}
var getName;    //变量声明提升
getName = function(){    //变量赋值依然保留在原来的位置
    console.log(2);
}
getName();    // 最终输出:2
 
// 函数声明虽然是在函数表达式后面,但由于函数声明提升到顶部,因此后面getName又被函数表达式的赋值操作给覆盖了

函数提升优先级比变量提升要高,且不会被变量声明覆盖,但是会被变量赋值覆盖。
链接: link.

reduce()

arr.reduce(function(prev,cur,index,arr){
...
}, init);

arr 表示原数组;
prev 表示上一次调用回调时的返回值,或者初始值 init;
cur 表示当前正在处理的数组元素;
index 表示当前正在处理的数组元素的索引,若提供 init 值,则索引为0,否则索引为1;
init 表示初始值。

求数组项之和

var sum = arr.reduce(function (prev, cur) {
    return prev + cur;
},0);

链接: link.

日期

let myDate = new Date()
let year = myDate.getFullYear();    //获取完整的年份(4位,1970-????)
let month = myDate.getMonth() + 1;       //获取当前月份(0-11,0代表1月)
let day = myDate.getDate();        //获取当前日(1-31)
let hours = myDate.getHours();       //获取当前小时数(0-23)
let minutes = myDate.getMinutes();     //获取当前分钟数(0-59)
let seconds = myDate.getSeconds();     //获取当前秒数(0-59)

let oldTime = year + '-' + month + '-' + day + ' ' + hours + ':' + minutes + ':' + seconds
console.log(oldTime) //2021-7-12 17:37:49

获取当前时间戳

//方法一
var timestamp1 = Date.parse(new Date());
//方法二
var timestamp2 = new Date().getTime()

时间戳转换为日期

var date = new Date(1398250549490);
Y = date.getFullYear() + '-';
M = (date.getMonth()+1 < 10 ? '0'+(date.getMonth()+1) : date.getMonth()+1) + '-';
D = date.getDate() + ' ';
h = date.getHours() + ':';
m = date.getMinutes() + ':';
s = date.getSeconds();
console.log(Y+M+D+h+m+s); //
//输出结果:2014-04-23 18:55:49

日期转换时间戳

date = new Date(2014-04-23 18:55:49:123); //传入一个时间格式,如果不传入就是获取现在的时间了,就这么简单。
// 有三种方式获取
time1 = date.getTime()
time2 = date.valueOf()
time3 = Date.parse(date)


e.currentTarget和e.target

e.currentTarget 绑定事件的对象, e.target被点击的对象

链接: link.

排序

function sortNumber(a,b){//升序
    return a - b
}
var arr = [5, 100, 6, 3, -12];
arr.sort(sortNumber);

去重

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

let arr = [1,1,2,3,4,5,5,6]
let arr2 = arr.filter(function(item,index) {
// indexOf() 方法可返回某个指定的 字符串值 在字符串中首次出现的位置
  return arr.indexOf(item) === index
})

for、var及定时器问题

总结来说:定时器不是同步的,他会自动的进入任务队列,等待同步任务的执行完毕才会执行,这也就是 为什么会先打印出 a 字符串 在打印出5次i = 5的原因。
那么为什么不是 0 1 2 3 4 a呢 ,是因为同步代码执行完毕之后,i 在for循环里 i++ 加到5停止执行 ,所以 i 已经变成了5 ,这时候循环已经结束了。
于是就可以想象成这个样子,以便理解。
会先for循环,i = 5 然后打印 a 然后在打印 5 次 i

链接: link.

class

链接: link.
链接: link.
链接: link.

some和every

every()与some()方法都是JS中数组的迭代方法。

every()是对数组中每一项运行给定函数,如果该函数对每一项返回true,则返回true。
some()是对数组中每一项运行给定函数,如果该函数对任一项返回true,则返回true。

var arr = [ 1, 2, 3, 4, 5, 6 ]; 

console.log( arr.some( function( item, index, array ){ 
    console.log( 'item=' + item + ',index='+index+',array='+array ); 
    return item > 3; 
})); 

console.log( arr.every( function( item, index, array ){ 
    console.log( 'item=' + item + ',index='+index+',array='+array ); 
    return item > 3; 
}));

随机值

function sj(max, min) { //max,min都包含
  return Math.floor(Math.random() * (max - min + 1)) + min
}
function sj(max, min) { //不包含max
  return Math.floor(Math.random() * (max - min)) + min
}
function sj1(max, min) { //不包含min
  let num 
  num =  Math.floor(Math.random() * (max - min + 1)) + min
  if(num == min) {
    num = num + 1
  }
  return num
}

箭头函数

  • 箭头函数语法更简洁,
  • 箭头函数的this是继承的上层作用域。
  • 箭头函数没有属于自己的argumnets
// 第一种
let f = (v) => {
    console.log(v)
    console.log(arguments) 
}
f(123) // 报错: arguments is not defined

// 第二种
let f = function (v) {
    console.log(arguments) // [123]
    return () => {
        // 继承上层作用域的arguments
        console.log(arguments)  // [123]
    }
}//如果上层作用域是全局就会报错,否则是继承上层作用域的arguments。
f(123)() // 不报错

  • 箭头函数使用call、bind、apply是不会更改 this指向的,就是简单的传递参数。
  • 箭头函数不能使用new关键字。
    因为new这个关键字底层使用了prototype、argumnets 和call来处理,箭头函数里没有argumnets和prototype,然后call对箭头函数没有效果。自然new就不好使了,都是连锁反应…。
  • 箭头函数没有prototype属性。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值