前端javaScript高频面试题——持续更新

目录

1.== 和 ===区别,分别在什么情况使用

2. 判断数据类型的方法

3.说说JavaScript中的数据类型?存储上的差别?

4.JavaScript中的操作符

5.var,let,const

 6.深拷贝与浅拷贝的区别以及实现方法

7.防抖与节流

8.setTimeout() 与 setInterval() 的区别

9.Math对象

10.cookies,sessionStorage 和 localStorage 的区别?

 11.get和post区别

12.http和https 

13.ajax基本步骤以及readyState的五种状态 以及ajax的原理

 14.延迟加载JS有哪些方式?

15. null和undefined的区别?

16.js宏任务和微任务

17. 数组的操作方法

18.字符串的方法 

19.为什么0.1+0.2 ! == 0.3,如何让其相等 

20.如何获取安全的 undefined 值?

21.typeof NaN 的结果是什么?

 22.Object.is() 与比较操作符 “===”、“==” 的区别?

23.const对象的属性可以修改吗

24. 如果new一个箭头函数的会怎么样

25.箭头函数与普通函数的区别

26.new操作符的实现原理

 27.为什么函数的 arguments 参数是伪数组而不是数组?如何遍历伪数组?

28.对AJAX的理解,实现一个AJAX请求

29.forEach和map方法有什么区别 

30.对Promise的理解

31.谈谈 script标签中 defer和 async属性的区别。

32.说说你对闭包的理解。

33.请解释一下事件冒泡机制

34.阻止事件冒泡的方法 

 35.什么是事件委托?它有什么好处?

36.随机从数组中取出一个值

37.javascript禁止浏览器返回上一个页面

38.jquery移动端禁止浏览器缩放 

39.如何遍历一个对象(Object)的所有属性并在控制台输出它们的值?

40.如何通过函数获取变量的数据类型?

41.生成随机字符串

42.生成指定范围内的随机数

43.格式化钱的方式有很多种,比如这两种方式。

44.持续更新中~~~


1.== 和 ===区别,分别在什么情况使用

等于操作符

等于操作符用两个等于号( == )表示,如果操作数相等,则会返回 true

我们知道JavaScript中存在隐式转换。等于操作符(==)在比较中会先进行类型转换,再确定操作数是否相等

1. let result = (true == 1); // true    
如果任一操作数是布尔值,则将其转换为数值再比较是否相等

2. let result = ("55" == 55); // true    
如果一个操作数是字符串,另一个操作数是数值,则尝试将字符串转换为数值,再比较是否相等

3. let obj = {valueof:Function(){return 1}}
   let result = (obj == 1); // true
如果一个操作数是对象,另一个操作数不是,则调用对象的 valueOf() 方法取得其原始值,再根据前面的规则进行比较

    
4. let result = (null == undefined ); // true
null 和undefined相等

5. let result = (NaN == NaN ); // false
null 和undefined相等


6. let obj1 = {name:"xxx"}
let obj2 = {name:"xxx"}
let result = (obj1 == obj2 ); // false
如果两个操作数都是对象,则比较它们是不是同一个对象。如果两个操作数都指向同一个对象,则相等操作符返回true

等于操作符总结

  • 两个都为简单类型,字符串和布尔值都会转换成数值,再比较

  • 简单类型与引用类型比较,对象转化成其原始类型的值,再比较

  • 两个都为引用类型,则比较它们是否指向同一个对象

  • null 和 undefined 相等

  • 存在 NaN 则返回 false

全等操作符 

全等操作符由 3 个等于号( === )表示,只有两个操作数在不转换的前提下相等才返回 true。即类型相同,值也需相同

1. let result1 = ("55" === 55); // false,不相等,因为数据类型不同
   let result2 = (55 === 55); // true,相等,因为数据类型相同值也相同
   
   undefined 和 null 与自身严格相等
2. let result1 = (null === null)  //true
   let result2 = (undefined === undefined)  //true

 全等操作符总结:

  • 全等运算符不会做类型转换

  • null 和 undefined 比较,全等为false

全等操作符和等于操作符的区别

        相等操作符(==)会做类型转换,再进行值的比较,全等运算符不会做类型转换 

2. 判断数据类型的方法

1.typeOf    2.instanceof    3.object.prototype.toString.call     4.constructor 

//1.typeOf
let obj={
   name:'dawn',
   age:21
}
let fn=function() {
    console.log('我是 function 类型');
}
console.log(typeof 1);       //number
console.log(typeof 'abc');   //string
console.log(typeof true);    //boolean
console.log(typeof undefined);  //undefined 
console.log(typeof fn);      //function
console.log(typeof (new Date) );  //object
console.log(typeof null);     //object
console.log(typeof [1,2,3]);  //object
console.log(typeof obj);      //object
//由结果可知typeof可以测试出number、string、boolean、undefined及function。对于null及数组、对象,typeof均检测出为object,不能进一步判断它们的类型。



//2.instanceof
let arr=[1,2,3,4,5,6,7]
let obj={
   name:'dawn',
   age:21
}
let fn=function() {
   console.log('我是 function 类型');
}
console.log(arr instanceof Array);  //true
console.log(obj instanceof Object);  //true
console.log(fn instanceof Function);  //true
console.log((new Date) instanceof Date);  //true
//obj instanceof Object ,可以左边放你要判断的内容,右边放类型来进行JS类型判断,只能用来判断复杂数据类型,因为instanceof 是用于检测构造函数(右边)的 prototype 属性是否出现在某个实例对象(左边)的原型链上。




//3.Object.prototype.toString.call
let obj = {
   name: 'dawn',
   age: 21
}
let fn = function () {
   console.log('我是 function 类型');
}    
console.log(Object.prototype.toString.call(1));        // [object Number]   
console.log(Object.prototype.toString.call('Hello tomorrow')); // [object String ]
console.log(Object.prototype.toString.call(true));     // [object Boolean]
console.log(Object.prototype.toString.call(undefined));  // [object Undefined]
console.log(Object.prototype.toString.call(fn));   // [object Function]
console.log(Object.prototype.toString.call(new Date));  // [object Date]
console.log(Object.prototype.toString.call(null));   // [object Null]
console.log(Object.prototype.toString.call([1, 2, 3]));  // [object Array]
console.log(Object.prototype.toString.call(obj));       // [object Object]
//在任何值上调用 Object 原生的 toString() 方法,都会返回一个 [object NativeConstructorName] 格式的字符串。每个类在内部都有一个 [[Class]] 属性,这个属性中就指定了上述字符串中的构造函数名。
但是它不能检测非原生构造函数的构造函数名。



//4.constructor
let arr = [1, 2, 3, 4, 5, 6, 7]
let obj = {
   name: 'dawn',
   age: 21
}
let fn = function () {
   console.log('我是 function 类型');
} 
console.log((9).constructor === Number);  //true
console.log('hello'.constructor === String);  //true
console.log(true.constructor === Boolean);  //true
console.log(fn.constructor === Function);  //true
console.log((new Date).constructor === Date);  //true
console.log(obj.constructor === Object);  //true
console.log([1, 2, 3].constructor === Array);  //true
//constructor不能判断undefined和null,并且使用它是不安全的,因为contructor的指向是可以改变的

3.说说JavaScript中的数据类型?存储上的差别?

JavaScript中的数据类型分为:

        基本数据类型 :Number,String,Boolean,Undefined,Null,Symbol

        复杂数据类型 : Object,Array,Function,Date,RegExp,...等

存储区别:

        基本数据类型存储在栈中

        引用类型的对象存储于堆中

//1.基本类型
let a = 10;
let b = a; // 赋值操作
b = 20;
console.log(a); // 10值
//a的值为一个基本类型,是存储在栈中,将a的值赋给b,虽然两个变量的值相等,但是两个变量保存了两个不同的内存地址


//2.引用类型
var obj1 = {}
var obj2 = obj1;
obj2.name = "Xxx";
console.log(obj1.name); // xxx
//引用类型数据存放在内对内中,每个堆内存中有一个引用地址,该引用地址存放在栈中obj1是一个引用类型,在赋值操作过程汇总,实际是将堆内存对象在栈内存的引用地址复制了一份给了obj2,实际上他们共同指向了同一个堆内存对象,所以更改obj2会对obj1产生影响

 总结:

  • 声明变量时不同的内存地址分配:
    • 简单类型的值存放在栈中,在栈中存放的是对应的值
    • 引用类型对应的值存储在堆中,在栈中存放的是指向堆内存的地址
  • 不同的类型数据导致赋值变量时的不同:
    • 简单类型赋值,是生成相同的值,两个对象对应不同的地址
    • 复杂类型赋值,是将保存对象的内存地址赋值给另一个变量。也就是两个变量指向堆内存中同一个对象

4.JavaScript中的操作符

//1.递增递减操作符
//使用++、--两种符号表示递增和递减,同时根据符号位置的不同,又分为前置递增递减和后置递增递减:
var num1 = 10;
var n = ++num1; // 前置递增,先执行递增计算,再对n进行赋值,所以,n=11,num1=11
var num2 = 10;
var m = --num2; // 前置递减,先执行递减计算,再对m进行赋值,所以,m=9,num2=9
var x = num2++; // 后置递增,先赋值,再执行递增计算,所以,x=9,num2=10;


//2.一元加、一元减操作符
//+表示一元加操作符,-表示一元减操作符,这两种操作符放在数值前面,相当于属数学中的正负号。
var a = 10;
console.log(-a); // -10
console.log(+a); // 10


//3.布尔操作符
//布尔操作符有三个:与、或、非
//&& 逻辑与    两边都是true,才返回true,否则返回false
//|| 逻辑或    两边只要有一个是true,就返回true,否则返回false
//! 逻辑非   用来取一个布尔值相反的值  
{} && false; // false
false && {}; // {}
{na:1} && {na:2}; // {na:2}
null && false; // null
NaN && false; // NaN
undefined && false; // undefined

{} || false; // {}
1 === 2 || false; // false
{n:1} || {n:2}; // {n:1}
null || null; // null
NaN || NaN; // NaN
undefined || undefined; // undefined 

//在进行||、&&操作时,如果有一个操作数不是布尔值,返回结果也不一定是布尔值:

5.var,let,const

  1. var定义的变量,可以预解析提前调用的结果是undefined,let定义的变量不能预解析,提前调用的结果是 报错(ReferenceError)。const定义的变量不能预解析,提前调用的结果是 报错。
  2. var定义的变量,变量名称可以重复,效果是重复赋值,let定义的变量不能重复,否则执行报错。const定义的变量不能重复,否则执行报错。
  3. var定义的变量作用域是全局/局部作用域。let定义的变量如果在{}中只能在{}中调用。
  4. 在循环语句中var定义的循环变量和使用let定义的循环变量。执行原理和执行效果不同。
  5. const 定义的变量存储的数据数值不能改变,也就是const定义的变量,不能重复赋值。
//提前使用
console.log(a)    //提前调用 预解析
console.log(b)   //报错
var a = 1
let b = 2

//const与let 定义的变量不能重复
var init = 11
var init = 22
let init2 = 11
let init2 = 22
console.log(init)   //22  var声明的变量被重新赋值
consoloe.log(init2) //会报错

//const与let 定义的变量如果在{}中那么只能在{}中来使用
if (true) {
  var a = 300;
  let b = 400;
  const c = 500;
  // let 声明的变量 在 { } 中可以调用
  console.log(b);
  console.log(c);
}
console.log(a);   //300
// let和const 声明的变量 在 { } 外 不能调用 
console.log(c);  //报错
console.log(b);  //报错


//const定义的变量 不能重复赋值
const aa = 100
aa = 200
console.log(aa)  //报错

const a = {
    name: 'zhang',
    age: '18'
}
console.log(a.name)//zhang
a.name = 'zhen';
console.log(a.name)//zhen

const b = [1, 2, 3, 4, 5]
console.log(b[0])//1
b[0] = 0;
console.log(b[0])//0
//const实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动。

考题

        下面代码输入的是什么?

                

        function sayHi() {

                console.log(name);

                console.log(age);

                var name = "熊大";

                let age = 18;

        }

A: 熊大和 undefined  B: 熊大和 ReferenceError  C: ReferenceError 和 18  D: undefined 和 ReferenceError

答案是:D    怎么样是否真的理解了呢?

 6.深拷贝与浅拷贝的区别以及实现方法

浅拷贝:

        将原对象或原数组的引用直接赋给新对象,新数组,新对象只是对原对象的一个引用,而不复制对象本身,新旧对象还是共享同一块内存

        如果属性是一个基本数据类型,拷贝就是基本类型的值,如果属性是引用类型,拷贝的就是内存地址,

深拷贝:

        创建一个新的对象和数组,将原对象的各项属性的“值”(数组的所有元素)拷贝过来,是“值”而不是“引用”

        深拷贝就是把一个对象,从内存中完整的拷贝出来,从堆内存中开辟了新区域,用来存新对象,并且修改新对象不会影响原对象

浅拷贝的方法:

        直接对象赋值

        Object.assign

深拷贝的方法:

        JSON.stringify转为字符串再JSON.parse转对象

        深度递归遍历

        写一个递归(推荐,比较完美的解决方案)封装一个DeepClone的深拷贝函数

//浅拷贝
//1.直接对象赋值
let a = [0,1,2,3,4];
b = a;
console.log(a === b);
a[0] = 1;
console.log(a, b) // a=11234  b=11234

//2. 解构赋值
var obj1 = {a: 1, b: 2}
var obj2 = {...obj1}
obj2.a = 4
console.log(obj1, obj2) //obj1 = {a:1,b:2}   obj2 = {a:4,b:2}

//3.Object.assign()
let obj1 = { 
	a: { b: 1}, 
	c: 2
}
let obj2 = Object.assign({},obj1)
obj2.a.b = 3;  //第二层,obj1变了,是浅拷贝
obj2.c = 3;  //第一层,obj1不变,是深拷贝
console.log(obj1);  //{a:{b:3},c:2}
console.log(obj2);  //{a:{b:3},c:3}
//严格来说,Object.assign() 既不是深拷贝,也不是浅拷贝——而是第一级属性深拷贝,第一级以下的级别属性浅拷贝。

//深拷贝
//1.封装deepClone深拷贝的方法
function DeepClone(data) {
  const newData = Array.isArray(data) ? [] : {}
  for (let key in data) {
    if (data[key] && typeof data[key] === 'object') {
       newData[key] = DeepClone(data[key])
    } else {
      newData[key] = data[key]
    }
  }
  return newData
}
//调用他
const obj2 = DeepClone(obj1)

//2.JSON.stringify转为字符串再JSON.parse
//JSON.stringify把对象转成字符串,再用JSON.parse把字符串转成新的对象
//缺点:当值为undefined、function、symbol 会在转换过程中被忽略

//3.jquery 提供一个 $.extend 可以用来做深拷贝;
var obj = {a:{name:"kaiqin",age:19}};
var obj1 = {b:{name:"wang",age:19}};
var obj2 = $.extend(true,{},obj,obj1); //true为深拷贝,false为浅拷贝,默认为false
obj2.a.name="zhang";
console.log(obj2)
//{a: {name: "zhang", age: 19},b: {name: "wang", age: 19}}
console.log(obj)
//{{a:{name:"kaiqin",age:19}}}

7.防抖与节流

防抖:防抖就是指触发事件后在 n 秒内函数只能执行一次,如果在 n 秒内又触发了事件,则会重新计算函数执行时间。 可用于input.change实时输入校验和window.resize事件,比如窗口缩放完成后,才会重新计算部分 DOM 尺寸

节流:所谓节流,就是指连续触发事件但是在 n 秒中只执行一次函数。两种方式可以实现,分别是时间戳版和定时器版用于监听 mousemove、 鼠标滚动等事件,通常可用于:拖拽动画、下拉加载。

//防抖
//用定时器实现防抖
function debounce(fn, wait) {
    let timout = null;
    return function () {
        clearTimeout(timout)
        timout = setTimeout(fn, wait)
    }
}


//节流
//定时器实现节流
function throttle(func, wait) {
    var timer = null;
    return function () {
        var _this = this;
        var args = arguments;
        if (!timer) {
            timer = setTimeout(function () {
                timer = null;
                func.apply(_this, args)
            }, wait)
        }
    }
}

//时间戳实现节流
function throttle(func, wait) {
    //定义初始时间
    var oldTime = 0;
    return function () {
        var _this = this;
        var args = arguments;
        //当前时间戳
        var newTime = +new Date();
        //判断用当前时间减去旧的时间,如果大于wait指定的时间就会触发
        if (newTime - oldTime > wait) {
            //执行触发的函数
            func.apply(_this, args)
            //将旧时间更新
            oldTime = newTime;
        }

    }

8.setTimeout() 与 setInterval() 的区别

1.相同条件下,setTimeout() 只执行一次,setInterval() 则循环执行
2.setTimeout() 延迟执行一次: setTimeout(fn, 1000); //延迟1秒,执行一次fn();
3.setInterval() 隔段时间循环执行; setInterval(fn, 1000); //每隔1秒,循环执行fn()

9.Math对象

Math.PI                // 圆周率
Math.random()          // 生成随机数
Math.floor()           // 向下取整
Math.ceil()            //向上取整
Math.round()           // 取整,四舍五入
Math.abs()             // 绝对值
Math.max()             //求最大值
Math.min()             //求最小值
Math.sin()             // 正弦
Math.cos()             //余弦
Math.power()           // 求指数次幂
Math.sqrt()            //求平方根


//求10到20的随机数
//生成 min 到 max 之间的随机数
var min = 10;
var max = 20;
console.log( parseInt(Math.random() * (max - min) + min));

10.cookies,sessionStorage 和 localStorage 的区别?

相同点:

        SessionStorage, LocalStorage, Cookie这三者都可以被用来在浏览器端存储数据,而且都是字符串类型的键值对。

不同点:

        SessionStorage:存放数据大小大约为5M,不参与服务器的通信,关闭当前页面或浏览器就会被清楚。

        localStorage:存放数据大小大约为5M或者更大,不参与服务器的通信,永久有效,除非手动删除或者代码删除。

        cookies:大小大约为4k的小型文本数据,一般服务器来生成,可以设置失效时间,没有设置关闭浏览器失效。

 11.get和post区别

  1. get一般用于获取请求,post一般用于提交请求
  2. get倾向于放在url中,post倾向于放在body体内
  3. 从根本上来说两种请求方式都不够安全,因为http本身就是明文协议,所以安全的普遍做法是采用https密钥加密协议。而由于get是放在请求体内,所以相对来说比较安全的做法是采用post+body的做法
  4. get数据的长度限制其实是指url的长度限制,但http协议本身其实对url的长度并未做任何限定,实际的限制是由客户端/浏览器以及服务器限定的

12.http和https 

  1. https协议需要到ca申请证书,一般免费证书较少,因而需要一定费用。
  2. http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议。
  3. http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
  4. http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。

13.ajax基本步骤以及readyState的五种状态 以及ajax的原理

//1创建对象xmlhttprequest
const xhr = new XMLHTTPRequest();
//2创建一个http请求初始化
xhr.open('GET','/api',false);
//3send发送请求
xhr.send();
//4监听状态变化
xhr.onreadystatechange = function(){
 //5查看状态码判断状态
 if(xhr.readyState == 4){
   if(xhr.status >= 200 &&xhr.status <= 300){
   }
 }
}

readyState的五种状态:

  1. 未初始化0
  2. 正在载入1
  3. 载入完成2
  4. 开始解析3
  5. 解析完成4

ajax的原理:

        ajax的原理就是浏览器让xhr(XmlHttpRequest)向服务器要数据,而浏览器去干别的事情,当服务器将数据返回给xhr后,xhr通知浏览器,浏览器将数据渲染到页面

 14.延迟加载JS有哪些方式?

<script defer type="text/javascript" src='script.js'></script>
//defer : 等html全部解析完成,才会执行js代码,顺次执行js脚本。

15. null和undefined的区别?

null是一个表示"无"的对象(空对象指针),转为数值时为0;

undefined是一个表示"无"的原始值,转为数值时为NaN。

16.js宏任务和微任务

js宏任务有:setTimeout、setInterval、Ajax、DOM事件

js微任务有:process.nextTick、Promise.then catch

 js宏任务和微任务谁先执行?

        JS是单线程,碰见同步执行同步直到执行完毕,遇到异步放到执行队列中去,异步(宏任务和微任务),在异步中微任务是优于宏任务执行的

setTimeout(() => {
  console.log(4);
})
new Promise(resolve => {
  resolve()
  console.log(1)
}).then(_ => {
  console.log(3)
})
console.log(2)

         setTimeout市异步所以跳过,来到Promise执行里面的同步,console.log(1)接下来是一个 then 的异步,跳过。最后一个是一段同步代码 console.log(2)。下一步,即之前我们跳过的异步的代码。从上往下,第一个是 setTimeout,还有一个是Promise.then()。

        setTimeout 是宏任务的异步,Promise.then()是微任务的异步,微任务是优先于宏任务执行的,所以,此时会先跳过 setTimeout 任务,执行两个 Promise.then() 的微任务。所以此时会执行 console.log(3) 函数。最后就只剩下 setTimeout 函数没有执行,所以最后执console.log(4)。

        综上分析:执行结果 1 2 3 4        

17. 数组的操作方法

//数组的操作方法
    push() //1.从数组末尾追加
    unshift() //2.从数组前面添加
    pop() //3.从数组末尾删除
    shift() //4.从数组头部删除
    reverse() //5.数组反转
    sort() //6.数组排序
    splice() //7.删除 添加 替换
    //以上七种可以修改数组本身
    forEach() //8.循环遍历
    concat() //9.拼接数组
    filter() //10.过滤数组
    slice() //11.截取元素
    every() //12.判断数组中是否有满足
    some() //13.只要有一个满足条件返回值就是true,没有满足条件的则为false
    reduce() //14.数组求和
    indexO() //15.查找值  从前往后查找
    lastIndexOf() //16.查找值  是从后往前查找。
    toString() //17.把数组转换为字符串
    join() //18.把数组转换为字符串, 可以传入不同的符号进行连接 默认用逗号连接
    toLocaleString() //19.把数组转换为字符串
    map() //20.指“映射”,对数组中的每一项运行给定函数,返回每次函数调用的结果组成的数组。返回新数组
    find() //21.返回数组中满足提供的测试函数的第一个元素的值。否则返回 undefined。
    flat() //22.按照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回。此方法不会改变原数组。depth 可选 指定要提取嵌套数组的结构深度,默认值为 1。如果不确定嵌套多少层,可以使用ES10新增:BigInt 表示任意大的整数
    findIndex() //23.方法返回传入一个测试条件(函数)符合条件的数组第一个元素位置。
    Array.at()  //24.返回指定索引处的值。
    
//数组的操作方法
//1.push()从数组末尾追加
//push从数组末尾添加,返回值是数组的长度,会改变原数组
var arr = [1, 2, 3, 4, 5]
console.log(arr.push(6, 7));        // 7 arr数组的长度 
console.log(arr);                   //输出结果:[1, 2, 3, 4, 5, 6, 7]

//2.unshift()从数组前面添加
//unshift从数组前面添加,返回值是数组的长度,会改变原数组
var arr = [1, 2, 3, 4, 5]
console.log(arr.unshift(6, 7));        // 7 arr数组的长度 
console.log(arr);                      //输出结果:[ 6, 7, 1, 2, 3, 4, 5 ]

//3.pop()从数组末尾删除
//pop 从数组末尾删除,返回值是删除的值,会改变原数组
var arr = [1, 2, 3, 4, 5]
console.log(arr.pop());        // 5 删除的值
console.log(arr);              //输出结果:[ 1, 2, 3, 4 ]

//4.shift()从数组头部删除
//shift 从数组头部删除,返回值是删除的值,会改变原数组
var arr = [1, 2, 3, 4, 5]
console.log(arr.shift());        // 1 删除的值
console.log(arr);                //输出结果:[ 2, 3, 4,5 ]

//5.reverse()数组反转
//reverse数组反转,返回值是反转后的数组,会改变原数组
var arr = [1, 2, 3, 4, 5]
console.log(arr.reverse());        // [5, 4, 3, 2, 1]
console.log(arr);                  //输出结果:[5, 4, 3, 2, 1]

//6.sort()数组排序
//sort数组排序,返回值是排序后的数组,会改变原数组
var arr = [1, 2, 3, 4, 5, 9, 8, 7, 6]
console.log(arr.sort());           //[1, 2, 3, 4, 5, 6, 7, 8, 9]
console.log(arr);                  //输出结果:[1, 2, 3, 4, 5, 6, 7, 8, 9]

//7.splice()删除 添加 替换
//splice(index,howmany,arr1,arr2),删除元素并添加元素,从index位置开始删除howmany个元素,
//并将arr1,arr2数据从index位置依次插入。howmany为0时,则不删除元素。
//splice数组排序,返回值是删除的数组,会改变原数组
var arr = [1, 2, 3, 4, 5]
console.log(arr.splice(0,2,111,222));           //[1, 2]
console.log(arr);                  //输出结果: [111, 222, 3, 4, 5]

//以上七种方法是修改原数组的

//8.forEach()循环遍历
//forEach循环遍历,没有返回值,回调函数的参数,第一个参数是数组的每一项,第二个参数是数组中每一项的下标,第三个参数是数组的本身。
var arr = [1, 2, 3, 4, 5]
arr.forEach((item,index,abc) => {console.log(item,index,abc)})

//9.concat()拼接数组
//用来拼接数组 并且 会返回一个新数组。
var arr = [1, 2, 3, 4, 5, 6];
console.log(arr.concat([7, 8, 9]));    //输出结果:[ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
var arr1 = [1, 2, 3, 4, 5, 6];
var arr2 = [7, 8, 9]
var arr3 = arr1.concat(arr2)
console.log(arr1);      //[ 1, 2, 3, 4, 5, 6 ]
console.log(arr3);      //输出结果:[ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]

//10.filter()过滤数组
//filter返回值是一个新的数组,filter里面可以直接 return 筛选条件 
let arr = [20, 50, 80, 90]
let newArr = arr.filter((item, index, array) => {    
//item:数组每一项,index:数组每一项的x下标,array:数组本身
    return item >= 70
})
console.log(newArr);  //返回筛选后的数组 [ 80, 90 ]

//11.slice()截取元素
//slice( ):数组元素的截取,返回一个新数组,新数组是截取的元素,可以为负值。
//从数组中截取,如果不传参,会返回原数组。如果只传入一个参数,会从头部开始删除,直到数组结束,原数组不会改变;传入两个参数,第一个是开始截取的索引,第二个是结束截取的索引,不包含结束截取的这一项,原数组不会改变。最多可以接受两个参数。
let arr = ['a', 'b', 'c', 'd', 'e', 'f', 'g']
console.log(arr.slice(0, 2));     // 返回值是被删除的元素['a','b']  
console.log(arr);                 //['a', 'b', 'c', 'd', 'e', 'f', 'g']

//12.every()判断数组中是否有满足
//every遍历数组  一般用于判断数组中是否有满足条件的数组元素,所有元素遍历之后,所有元素都满足条件的元素是返回值为true,若有一个不满足则返回false.
var arr = [1, 2, 3, 4, 5, 6]
console.log(arr.every(item => item > 1));     //输出结果:false
var arr = [1, 2, 3, 4, 5, 6]
console.log(arr.every(item => item > 0));     //输出结果:true

//13.some()只要有一个满足条件返回值就是true,没有满足条件的则为false
//some方法和every方法非常的类似 ,所有元素遍历之后,所有元素都满足条件的元素是返回值为true,若有一个不满足则返回false.
var arr = [1, 2, 3, 4, 5, 6]
console.log(arr.every(item => item > 6));     //输出结果:false
var arr = [1, 2, 3, 4, 5, 6]
console.log(arr.every(item => item > 3));     //输出结果:true

//14.reduce()数组求和
//1.reduce方法接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值。
​//2.reduce方法可以接收两个参数,第一个参数:要执行的函数,第二个参数:函数迭代的初始值
//3.第一个参数不是函数吗?函数里面的参数如下
    prev:上次调用函数的返回值
    cur:当前元素
    index:当前元素索引
    arr:被遍历的数组
//4.array.reduce(function(prev, currentValue, currentIndex, arr), initValue) //initValue是第二个参数
//不传入函数迭代的初始值
const arr = [11, 22, 33, 44]
const sum = arr.reduce((prev, cur, index) => {
   console.log(prev, cur, index);
   return prev + cur
})        //没有传入函数迭代的初始值, 初始值默认是数组第一位 , prev 就是计算结束后的返回值。
console.log('sum:', sum);   //110   
//传入函数迭代的初始值
const arr = [11, 22, 33, 44]
const sum = arr.reduce((prev, cur, index) => {
   console.log(prev, cur, index);
   return prev + cur
}, 10)    //传入函数迭代的初始值之后,也就是让 prev 从10开始累加,然后接着迭代累加 prev 
console.log('sum:', sum);    //120

//15.indexO()查找值  从前往后查找
//接受两个参数:查找的值、查找起始位置
//不存在,返回 -1 ;存在,返回位置。
var arr = [1, 2, 3, 4, 5]
console.log(arr.indexof(2,3))  //-1

//16.lastIndexOf()查找值  是从后往前查找。
//接受两个参数:查找的值、查找起始位置
//不存在,返回 -1 ;存在,返回位置。
var arr = [1, 2, 3, 4, 5]
console.log(arr.lastIndexOf(2,3))  //1

//17.toString()把数组转换为字符串
var arr = [1, 2, 3, 4, 5]
console.log(arr.toString())    //'1,2,3,4,5'

//18.join()把数组转换为字符串, 可以传入不同的符号进行连接 默认用逗号连接
var arr = [1, 2, 3, 4, 5]
console.log(arr.join('|'))    //'1|2|3|4|5'

//19.toLocaleString()把数组转换为字符串
var arr = [1, 2, 3, 4, 5]
console.log(arr.toLocaleString())    //'1,2,3,4,5'

//20.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]

//21.find()
//返回数组中满足提供的测试函数的第一个元素的值。否则返回 undefined。
// 获取数组中第一个大于10的值
const array1 = [5, 12, 8, 130, 44];
const found = array1.find(element => element > 10);
console.log(found);// expected output: 12

//22.flat()
//按照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回。此方法不会改变原数组。
//depth 可选 指定要提取嵌套数组的结构深度,默认值为 1。如果不确定嵌套多少层,可以使用ES10新增:BigInt 表示任意大的整数
const arr1 = [0, 1, 2, [3, 4]];
console.log(arr1.flat());// log: [0, 1, 2, 3, 4]
const arr2 = [0, 1, 2, [[[3, 4]]]];
console.log(arr2.flat(2));// log: [0, 1, 2, [3, 4]]

//23.findIndex()方法返回传入一个测试条件(函数)符合条件的数组第一个元素位置。
//IE 11 及更早版本不支持 findIndex() 方法
var arr2 = [1,18,2,99,666,44,66];
var flag2 = arr2.findIndex(item => {
   return item > 50;
});    //当数组中的元素在测试条件时返回 true 时, findIndex() 返回符合条件的元素的索引位置,如果没有符合条件的元素返回 -1
console.log(flag2)   // 得到: 3

//24.Array.at()返回指定索引处的值。
const list = [1, 2, 3, 4, 5];
list.at(1); // 2
list.at(-1); // 5
list.at(-2); // 4

18.字符串的方法 

//字符串常用方法
    charAt()        //1.返回在指定位置的字符
    charCodeAt()    //2.返回在指定的位置的字符的 Unicode 编码。
    concat()        //3.连接字符串。
    indexOf()       //4.检索字符串。indexOf() 方法对大小写敏感。
    match()         //5.是用来查找字符的 
    includes()      //6.判断字符串是否包含指定的子字符串。
    repeat()        //7.字符串复制指定次数
    replace()       //8.方法用于在字符串中用一些字符替换另一些字符,或替换一个与正则表达式匹配的子串。
    replaceAll()    //9.字符串中用一些字符替换另一些字符,或替换一个与正则表达式匹配的子串,该函数会替换所有匹配到的子字符串。
    search()        //10.方法用于检索字符串中指定的子字符串,或检索与正则表达式相匹配的子字符串。返回与指定查找的字符串或者正则表达式相匹配的 String 对象起始位置。
    slice()         //11.提取字符串的某个部分,并以新的字符串返回被提取的部分。
    split()         //12.方法用于把一个字符串分割成字符串数组
    substring()     //13.方法用于提取字符串中介于两个指定下标之间的字符。
    toLowerCase()   //14.方法用于把字符串转换为小写。
    toUpperCase()   //15.方法用于把字符串转换为大写。
    trim()          //16.删除字符串的头尾空白符,空白符包括:空格、制表符 tab、换行符等其他空白符等
    includes(), startsWith(), endsWith()    //17.来确定一个字符串是否包含在另一个字符串中
    
//1.charAt()返回在指定位置的字符
var str="abc"
console.log(str.charAt(0))//a

//2.charCodeAt()返回在指定的位置的字符的 Unicode 编码。
var str="abc"
console.log(str.charCodeAt(1))//98

//3.concat()连接字符串。
var a = "abc";  
var b = "def";  
var c = a.concat(b);
console.log(c);//abcdef

//4.indexOf()检索字符串。indexOf() 方法对大小写敏感。
var str="Hello world!"
console.log(str.indexOf("Hello"))//0
console.log(str.indexOf("World"))//-1
console.log(str.indexOf("world"))///6

//5.match()是用来查找字符的 
//方法可在字符串内检索指定的值,或找到一个或多个正则表达式的匹配。该方法类似 indexOf() 和 lastIndexOf(),但是它返回指定的值,而不是字符串的位置
var str = 'asdfghajkwyai';
var result = str.match('a');   //result = a
//如果想查找所有满足的匹配结果,就要加一个 g 才会返回所有满足匹配的结果
var str = 'asdfghajkwyai';
var result = str.match(/a/g);    //result = aaa
//若没有标志 g,.match()方法就只执行一次匹配,找到结果就返回,不会接着去查找;若没有找到任何匹配文本,.match()方法将会返回nul;
//若有标志 g,.match()就会返回一个数组,数组中存放所有满足查找条件的信息;

//6.includes()判断字符串是否包含指定的子字符串。
let str = "Hello";
let s = str.includes("e");
console.log(s); //true
//语法:string.includes(searchvalue, start)
//searchvalue 必需值,要查找的字符串。
//start 可选值,设置从那个位置开始查找,默认为 0。

//7.repeat()字符串复制指定次数
let str = "Hello";
let s = str.repeat(2);
console.log(s); //HelloHello
//语法:string.repeat(count)
//count 必需,设置要复制的次数。

//8.replace()字符替换
let str = "Hello";
let s = str.replace("l", "o");
console.log(s); //Heolo
//语法:string.replace(searchvalue,newvalue)
//searchvalue 必须。规定子字符串或要替换的模式的 RegExp 对象。
//请注意,如果该值是一个字符串,则将它作为要检索的直接量文本模式,而不是首先被转换为RegExp对象。
//newvalue 必需。一个字符串值。规定了替换文本或生成替换文本的函数。

//9.replaceAll()字符串中用一些字符替换另一些字符,或替换一个与正则表达式匹配的子串,该函数会替换所有匹配到的子字符串。
let str = "Hello";
let s = str.replaceAll("l", "o");
console.log(s); //Heooo
//语法同replace方法相同

//10.search()
//search() 方法用于检索字符串中指定的子字符串,或检索与正则表达式相匹配的子字符串。返回与指定查找的字符串或者正则表达式相匹配的 String 对象起始位置。
let str = "Hello";
let s = str.search("lo");
console.log(s); //3
//语法:string.search(searchvalue)
//searchvalue 必须。查找的字符串或者正则表达式。

//11.slice()提取字符串的某个部分,并以新的字符串返回被提取的部分。
let str = "Hello";
let s = str.slice(1, 2);
console.log(s); //e
//语法:string.slice(start,end)
//start 必须。 要抽取的片断的起始下标,第一个字符位置为 0。如果为负数,则从尾部开始截取。
//end 可选。 紧接着要截取的片段结尾的下标。若未指定此参数,则要提取的子串包括 start 到原字符串结尾的字符串。如果该参数是负数,那么它规定的是从字符串的尾部开始算起的位置。slice(-2) 表示提取原数组中的倒数第二个元素到最后一个元素(包含最后一个元素)。

//12.split() 方法用于把一个字符串分割成字符串数组
let str = "Hello";
let s = str.split("e");
console.log(str); //Hello
console.log(s); //[ 'H', 'llo' ]
//语法:string.split(separator,limit)
//separator 可选。字符串或正则表达式,从该参数指定的地方分割 string Object。
//limit 可选。该参数可指定返回的数组的最大长度。如果设置了该参数,返回的子串不会多于这个参数指定的数组。如果没有设置该参数,整个字符串都会被分割,不考虑它的长度。

//13.substring() 方法用于提取字符串中介于两个指定下标之间的字符。
let str = "Hello";
let s = str.substring(1, 3);
console.log(str); //Hello
console.log(s); //el
//语法:string.substring(from, to)
//from 必需。一个非负的整数,规定要提取的子串的第一个字符在 string Object 中的位置。
//to 可选。一个非负的整数,比要提取的子串的最后一个字符在 string Object 中的位置多 1。
如果省略该参数,那么返回的子串会一直到字符串的结尾。

//14,15.toLowerCase()和toUpperCase()方法
let str = "Hello";
let s = str.toLowerCase();
let s2 = str.toUpperCase();
console.log(str); //Hello
console.log(s); //hello
console.log(s2);//HELLO

//16.trim()删除字符串的头尾空白符,空白符包括:空格、制表符 tab、换行符等其他空白符等
let str = "    Hello   ";
let s = str.trim();
console.log(str); //    Hello
console.log(s); //Hello

//17.includes(), startsWith(), endsWith()来确定一个字符串是否包含在另一个字符串中
let s = 'Hello world!';
console.log(s.startsWith('world', 6));// true
console.log(s.endsWith('Hello', 5));// true
console.log(s.includes('Hello', 6));// false
//注意:第二个参数,表示开始搜索的位置。

字符串进行去重

var str="aahhgggsssjjj";
function removeRepeat(n){
var k=[];
var arr=n.split("");
for(var i=0;i<arr.length;i++){
    if(k.indexOf(arr[i])==-1){
        k.push(arr[i]);
    }
}
return k.join("");
}
removeRepeat(str);    //ahgsj

19.为什么0.1+0.2 ! == 0.3,如何让其相等 

在开发过程中遇到类似这样的问题:

let n1 = 0.1, n2 = 0.2
console.log(n1 + n2)  // 0.30000000000000004

这里得到的不是想要的结果,要想等于0.3,就要把它进行转化:

(n1 + n2).toFixed(2) // 注意,toFixed为四舍五入

toFixed(num) 方法可把 Number 四舍五入为指定小数位数的数字。

20.如何获取安全的 undefined 值?

因为 undefined 是一个标识符,所以可以被当作变量来使用和赋值,但是这样会影响 undefined 的正常判断。表达式 void ___ 没有返回值,因此返回结果是 undefined。void 并不改变表达式的结果,只是让表达式不返回值。因此可以用 void 0 来获得 undefined。

21.typeof NaN 的结果是什么?

NaN 指“不是一个数字”(not a number),NaN 是一个“警戒值”,用于指出数字类型中的错误情况,即“执行数学运算没有成功,这是失败后返回的结果”。 

typeof NaN; // "number"

NaN 是一个特殊值,它和自身不相等。而 NaN !== NaN 为 true。

 22.Object.is() 与比较操作符 “===”、“==” 的区别?

  • 使用双等号(==)进行相等判断时,如果两边的类型不一致,则会进行强制类型转化后再进行比较。
  • 使用三等号(===)进行相等判断时,如果两边的类型不一致时,不会做强制类型准换,直接返回 false。
  • 使用 Object.is 来进行相等判断时,一般情况下和三等号的判断相同,它处理了一些特殊的情况,比如 -0 和 +0 不再相等,两个 NaN 是相等的。

23.const对象的属性可以修改吗

const保证的并不是变量的值不能改动,而是变量指向的那个内存地址不能改动。对于基本类型的数据(数值、字符串、布尔值),其值就保存在变量指向的那个内存地址,因此等同于常量。

对于引用类型的数据来说,变量指向数据的内存地址,保存的只是一个指针,const只能保证这个指针是固定不变的,至于它指向的数据结构是不是可变的,就完全不能控制了。 

24. 如果new一个箭头函数的会怎么样

箭头函数是ES6中的提出来的,它没有prototype,也没有自己的this指向,更不可以使用arguments参数,所以不能New一个箭头函数。

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

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

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

25.箭头函数与普通函数的区别

  1.  箭头函数比普通函数更加简洁(如果没有参数,就直接写一个空括号即可,如果只有一个参数,可以省去参数的括号)

  2. 箭头函数没有自己的this

  3. 箭头函数继承来的this指向永远不会改变

  4. call()、apply()、bind()等方法不能改变箭头函数中this的指向

  5. 箭头函数不能作为构造函数使用

  6. 箭头函数没有自己的arguments

  7. 箭头函数没有prototype

  8. 箭头函数不能用作Generator函数,不能使用yeild关键字

26.new操作符的实现原理

new操作符的执行过程:

(1)首先创建了一个新的空对象

(2)设置原型,将对象的原型设置为函数的 prototype 对象。

(3)让函数的 this 指向这个对象,执行构造函数的代码(为这个新对象添加属性)

(4)判断函数的返回值类型,如果是值类型,返回创建的对象。如果是引用类型,就返回这个引用类型的对象。

实现方式

function objectFactory() {
  let newObject = null;
  let constructor = Array.prototype.shift.call(arguments);
  let result = null;
  if (typeof constructor !== "function") {// 判断参数是否是一个函数
    console.error("type error");
    return;
  }
  // 新建一个空对象,对象的原型为构造函数的 prototype 对象
  newObject = Object.create(constructor.prototype);
  // 将 this 指向新建对象,并执行函数
  result = constructor.apply(newObject, arguments);
  // 判断返回对象
  let flag = result && (typeof result === "object" || typeof result === "function");
  // 判断返回结果
  return flag ? result : newObject;
}
// 使用方法
objectFactory(构造函数, 初始化参数);

 27.为什么函数的 arguments 参数是伪数组而不是数组?如何遍历伪数组?

arguments是一个对象,它的属性是从 0 开始依次递增的数字,还有calleelength等属性,与数组相似;但是它却没有数组常见的方法属性,如forEachreduce等,所以叫它们伪数组。

三种遍历为数组的方法

第一种

function foo(){ 
  Array.prototype.forEach.call(arguments, a => console.log(a))
}
//将数组的方法应用到类数组上,这时候就可以使用call和apply方法。

第二种

function foo(){ 
  const arrArgs = Array.from(arguments) 
  arrArgs.forEach(a => console.log(a))
}
//使用Array.from方法将类数组转化成数组

第三种

function foo(){ 
    const arrArgs = [...arguments] 
    arrArgs.forEach(a => console.log(a)) 
}
//使用展开运算符将类数组转化成数组

28.对AJAX的理解,实现一个AJAX请求

AJAX是 Asynchronous JavaScript and XML 的缩写,指的是通过 JavaScript 的 异步通信,从服务器获取 XML 文档从中提取数据,再更新当前网页的对应部分,而不用刷新整个网页。

创建AJAX请求的步骤:

  1. 创建一个 XMLHttpRequest 对象。
  2. 在这个对象上使用 open 方法创建一个 HTTP 请求,open 方法所需要的参数是请求的方法、请求的地址、是否异步和用户的认证信息。
  3. 在发起请求前,可以为这个对象添加一些信息和监听函数。比如说可以通过 setRequestHeader 方法来为请求添加头信息。还可以为这个对象添加一个状态监听函数。一个 XMLHttpRequest 对象一共有 5 个状态,当它的状态变化时会触发onreadystatechange 事件,可以通过设置监听函数,来处理请求成功后的结果。当对象的 readyState 变为 4 的时候,代表服务器返回的数据接收完成,这个时候可以通过判断请求的状态,如果状态是 2xx 或者 304 的话则代表返回正常。这个时候就可以通过 response 中的数据来对页面进行更新了。
  4. 当对象的属性和监听函数设置完成后,最后调用 sent 方法来向服务器发起请求,可以传入参数作为发送的数据体。
const SERVER_URL = "/server";
let xhr = new XMLHttpRequest();
// 创建 Http 请求
xhr.open("GET", url, true);
// 设置状态监听函数
xhr.onreadystatechange = function() {
  if (this.readyState !== 4) return;
  // 当请求成功时
  if (this.status === 200) {
    handle(this.response);
  } else {
    console.error(this.statusText);
  }
};
// 设置请求失败时的监听函数
xhr.onerror = function() {
  console.error(this.statusText);
};
// 设置请求头信息
xhr.responseType = "json";
xhr.setRequestHeader("Accept", "application/json");
// 发送 Http 请求
xhr.send(null);

 使用Promise封装AJAX:

// promise 封装实现:
function getJSON(url) {
  // 创建一个 promise 对象
  let promise = new Promise(function(resolve, reject) {
    let xhr = new XMLHttpRequest();
    // 新建一个 http 请求
    xhr.open("GET", url, true);
    // 设置状态的监听函数
    xhr.onreadystatechange = function() {
      if (this.readyState !== 4) return;
      // 当请求成功或失败时,改变 promise 的状态
      if (this.status === 200) {
        resolve(this.response);
      } else {
        reject(new Error(this.statusText));
      }
    };
    // 设置错误监听函数
    xhr.onerror = function() {
      reject(new Error(this.statusText));
    };
    // 设置响应的数据类型
    xhr.responseType = "json";
    // 设置请求头信息
    xhr.setRequestHeader("Accept", "application/json");
    // 发送 http 请求
    xhr.send(null);
  });
  return promise;
}

29.forEach和map方法有什么区别 

都是用来遍历数组的

  • forEach()方法会针对每一个元素执行提供的函数,对数据的操作会改变原数组,该方法没有返回值;
  • map()方法不会改变原数组的值,返回一个新数组,新数组中的值为原数组调用函数处理之后的值;

30.对Promise的理解

Promise是异步编程的一种解决方案,它是一个对象,可以获取异步操作的消息,他的出现大大改善了异步编程的困境,避免了地狱回调,它比传统的解决方案回调函数和事件更合理和更强大。

Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。

Promise的实例有三个状态:

  • Pending(进行中)
  • Resolved(已完成)
  • Rejected(已拒绝)

Promise的实例有两个过程

  • pending -> fulfilled : Resolved(已完成)
  • pending -> rejected:Rejected(已拒绝)

注意:一旦从进行状态变成为其他状态就永远不能更改状态了。

31.谈谈 script标签中 defer和 async属性的区别。

区别如下。

(1) defer属性规定是否延迟执行脚本,直到页面加载为止, async属性规定脚本一旦可用,就异步执行。

(2) defer并行加载 JavaScript文件,会按照页面上 script标签的顺序执行, async并行加载 JavaScript文件,下载完成立即执行,不会按照页面上 script标签的顺序执行。

32.说说你对闭包的理解。

使用闭包主要是为了设计私有的方法和变量。闭包的优点是可以避免全局变量的污染;缺点是闭包会常驻内存,增加内存使用量,使用不当很容易造成内存泄漏。在JavaScript中,函数即闭包,只有函数才会产生作用域闭包有3个特性

(1)函数嵌套函数。

(2)在函数内部可以引用外部的参数和变量

(3)参数和变量不会以垃圾回收机制回收

33.请解释一下事件冒泡机制

事件冒泡就是在事件开始时,由最具体的元素(文档中最底层的那个节点)接受,然后逐级向上传播到最不具体的节点(文档)。比如说,在一个页面有一个div元素,加上一个点击事件,在点击事件触发的时候,它的传递过程就是由div→body→html→document(老版本暂且不谈),它会沿着DOM树逐级的传递。

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
		<title></title>
		<style type="text/css">
			.outer{
				width: 200px;
				height: 200px;
				background: red;
				z-index: 1;
			}
			.cent{
				width: 100px;
				height: 100px;
				background: blue;
				position: absolute;
				right: 0;
				left: 0;
				bottom: 0;
				top: 0;
				margin:auto;
				z-index: 2;
			}
			.insert{
				width: 50px;
				height: 50px;
				background: orange;
				position: absolute;
				right: 0;
				left: 0;
				bottom: 0;
				top: 0;
				margin:auto;
				z-index: 3;
			}
			div{
				position: relative;
			}
		</style>
	</head>
	<body>
		<div class="outer">
			<div class="cent">
				<div class="insert"></div>
			</div>
		</div>
	</body>
	<script type="text/javascript">
		var Outer=document.getElementsByClassName("outer")[0];
		var Cent=document.getElementsByClassName("cent")[0];
		var Insert=document.getElementsByClassName("insert")[0];
		var aBody=document.getElementsByTagName("body")[0];
		Outer.onclick=function(){
			console.log("aaa")
		}
		Cent.onclick=function(){
			console.log("bbb")
		}
		Insert.onclick=function(){
			console.log("ccc")
		}
		aBody.onclick=function(){
			console.log("我是body")
		}

	</script>
</html>


    点击Insert这个元素的时候会依次输出ccc、bbb、aaa、我是body,这就是事件冒泡机制。

34.阻止事件冒泡的方法 

阻止事件冒泡的方法有两种:

  1. storpPrapagation()  // 不支持IE低版本
  2. cancelBubble=true

兼容写法

 if(event.storpPrapagation){

           event.storpPrapagation();

}else{

           cancelBubble=true;

}

 35.什么是事件委托?它有什么好处?

事件委托指利用冒泡的原理,把事件加到父级上,触发执行效果好处如下。

  • 减少事件数量,提高性能。

  • 预测未来元素,新添加的元素仍然可以触发该事件。

  • 避免内存外泄,在低版本正E中,防止删除元素而没有移除事件造成的内存溢出。

36.随机从数组中取出一个值

var arr = ['1', '2', '3'];
var index = Math.floor((Math.random() * arr.length));
console.log(arr[index]);

37.javascript禁止浏览器返回上一个页面

<script type="text/javascript">
  $(function () {
    if (window.history && window.history.pushState) {
      $(window).on('popstate', function () {
        window.history.pushState('forward', null, '#');
        window.history.forward(1);
      });
    }
    window.history.pushState('forward', null, '#'); //在IE中必须得有这两行
    window.history.forward(1);
  })
</script>

38.jquery移动端禁止浏览器缩放 

要禁止移动端浏览器的缩放,您可以在页面初始化时为viewportmeta标签添加user-scalable=no属性。这样,当用户在移动端浏览器中尝试缩放页面时,浏览器就不会响应用户的缩放操作。

<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no">

39.如何遍历一个对象(Object)的所有属性并在控制台输出它们的值?

for (let key in obj) {
  console.log(key + ": " + obj[key]);
}

40.如何通过函数获取变量的数据类型?

  const getType = (value) => {
    const match = Object.prototype.toString.call(value).match(/ (\w+)]/)
    return match[1].toLocaleLowerCase()
  }
  console.log(getType());// undefined
  console.log(getType({}));// object
  console.log(getType([]));// array
  console.log(getType(1));// number
  console.log(getType('fatfish'));// string
  console.log(getType(true));// boolean
  console.log(getType(/fatfish/));// regexp

41.生成随机字符串


const randomString = (len) => {
  let chars = "ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz123456789";
  let strLen = chars.length;
  let randomStr = "";
  for (let i = 0; i < len; i++) {
    randomStr += chars.charAt(Math.floor(Math.random() * strLen));
  }
  return randomStr;
};

randomString(10) // pfkMfjEJ6x
randomString(20) // ce6tEx1km4idRNMtym2S

42.生成指定范围内的随机数


const randomNum = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min;

randomNum(1, 10) // 6
randomNum(10, 20) // 11

43.格式化钱的方式有很多种,比如这两种方式。

格式化钱的第一种方式

const formatMoney = (money) => {
  return money.replace(new RegExp(`(?!^)(?=(\\d{3})+${money.includes('.') ? '\\.' : '$'})`, 'g'), ',')  
}

formatMoney('123456789') // '123,456,789'
formatMoney('123456789.123') // '123,456,789.123'
formatMoney('123') // '123'

格式化钱的第二种方式

 正则表达式让我们太头疼了,不是吗?所以我们需要找到一种更简单的方法来格式化货币。

const formatMoney = (money) => {
  return money.toLocaleString()
}

formatMoney(123456789) // '123,456,789'
formatMoney(123456789.123) // '123,456,789.123'
formatMoney(123) // '123'

44.持续更新中~~~

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值