js知识点

1、扁平的数组结构转化为json树形结构

思路:

第一步把数组数据转化为map数据结构如:

let map = {1:{id:1,title:"eeeee",parent_id:0},....}

第二步,定义一个空数组result,循环list数组,

如果parent_id 等于0的话,直接把当前项放到数组result中,

如果当前项parent_id 可以在 map对象里面找到,说明存在父节点,

然后拿到父节点的对象,往里面添加一个children属性,如果没有给一个空数组,有该属性就直接把当前项放到chindren中,如下

let parent = map[item.parent_id]

parent.children = parent.children || []

parent.children.push(item)

整体代码:

<script>

    let list = [

        {id:1,title:'eeeee',parent_id:0},

        {id:2,title:'55555',parent_id:0},

        {id:3,title:'66666',parent_id:2},

        {id:4,title:'99999',parent_id:2},

        {id:5,title:'00000',parent_id:2}

    ]

    function treeArr(list){

        let result = []

        let map = list.reduce((pre,cur)=>{

            pre[cur.id] = cur

            return pre

        },{})

        for (const item of list) {

            if(item.parent_id === 0){

                result.push(item)

                continue;

            }

            if(item.parent_id in map){

                let parent = map[item.parent_id]

                parent.children = parent.children || []

                parent.children.push(item)

            }

        }

        return result

    }

   console.log( treeArr(list))

</script>

结果:

2、json树形结构转化为扁平数组结构

function flatArr(data){

   return data.reduce((pre,cur)=>{

        const { id, title,parent_id,children=[]} = cur //解构

         //当数组为空,并且给了一个初始值,reduce不会执行

       return pre.concat([{id,title,parent_id}],flatArr(children))

    },[])

   }

   console.log(flatArr(treeArrs,"---"))

3、防抖函数

防抖概念:
就是在一定时间内,如果事件多次触发,只执行最后一次触发的事件
应用场景:
1.scroll事件滚动触发
2.搜索框输入查询
3.表单验证
4.按钮提交事件
5.浏览器窗口缩放,resize事件

<!DOCTYPE html>

<html lang="en">

  <head>

    <meta charset="UTF-8" />

    <meta http-equiv="X-UA-Compatible" content="IE=edge" />

    <meta name="viewport" content="width=device-width, initial-scale=1.0" />

    <title>Document</title>

    <style>

      #contain {

        height: 200px;

        width: 200px;

        background-color: aqua;

      }

    </style>

  </head>

  <body>

    <div id="contain"></div>

  </body>

  <script>

    let contain = document.querySelector("#contain");

    let count = 0;

    function contains(e) {

      //this指向window,e为undefined

      console.log(this, "===========",e);

      contain.innerHTML = count++;

    }

没有第三个参数,就是立即执行参数:

    function debounce(func, wait) {

      let timeOut = "";

      return function () {

        let args = arguments//拿到event事件传过去

        let context = this;//改变this指向

        //console.log(this, "===========debounce",arguments);

        clearTimeout(timeOut);

        timeOut = setTimeout(function () {

          func.apply(context,args);

        }, wait);

      };

    }

contain.onmousemove = debounce(contains, 300);

有第三个参数,有立即执行参数:

function debounce(func, wait, immediate) {

      let timeOut = "";

      return function () {

        let args = arguments; //拿到event事件传过去

        let context = this; //改变this指向

        //console.log(this, "===========debounce",arguments);

        clearTimeout(timeOut);

        if (immediate) {

            //会立刻执行

            let callNow = !timeOut

            console.log(timeOut,"========0000")

            timeOut = setTimeout(function () {

            timeOut = null

          }, wait);

          if(callNow){

            func.apply(context, args);

          }

        } else {

            //不会立刻执行

          timeOut = setTimeout(function () {

            func.apply(context, args);

          }, wait);

        }

      };

    }

contain.onmousemove = debounce(contains, 300, true);

  </script>

</html>

 防抖最终版:带有取消防抖按钮

<!DOCTYPE html>

<html lang="en">

  <head>

    <meta charset="UTF-8" />

    <meta http-equiv="X-UA-Compatible" content="IE=edge" />

    <meta name="viewport" content="width=device-width, initial-scale=1.0" />

    <title>Document</title>

    <style>

      #contain {

        height: 200px;

        width: 200px;

        background-color: aqua;

      }

    </style>

  </head>

  <body>

    <div id="contain"></div>

    <button id="btn">取消防抖</button>

  </body>

  <script>

    let contain = document.querySelector("#contain");

    let btn = document.querySelector("#btn");

    let count = 0;

    function contains(e) {

      //this指向window,e为undefined

      console.log(this, "===========", e);

      contain.innerHTML = count++;

    }

    function debounce(func, wait, immediate) {

      let timeOut , result;

      let debounced =  function () {

        let args = arguments; //拿到event事件传过去

        let context = this; //改变this指向

        //console.log(this, "===========debounce",arguments);

        if(timeOut) {

            clearTimeout(timeOut);

        }

        if (immediate) {

            //会立刻执行

            let callNow = !timeOut

            console.log(timeOut,"========0000")

            timeOut = setTimeout(function () {

            timeOut = null

          }, wait);

          if(callNow){

            result = func.apply(context, args);

          }

        } else {

            //不会立刻执行

          timeOut = setTimeout(function () {

            func.apply(context, args);

          }, wait);

        }

        return result

      }

      debounced.cancle = function(){

        clearTimeout(timeOut)

        timeOut = null

      }

      return debounced

    }

    let doSome = debounce(contains, 5000);

    //取消防抖

    btn.onclick = function(){

        console.log('取消防抖')

        doSome.cancle()

    }

    contain.onmousemove =doSome;

  </script>

</html>

4、forEach

第一个参数是遍历的数组内容,
第二个参数是对应的数组索引,
第三个参数是数组本身

1.forEach并不支持break操作,使用break会导致报错。

2.forEach中使用return无效

3.想要返回某个索引方法

let arr = [1, 2, 3, 4];

function find(array, num) {

let _index;

array.forEach((self, index) => {

if (self === num) {

_index = index;

};

});

return _index;

};

let index = find(arr, 2);

console.log(index)

4.循环的同时不能完全删除数组,index隐性自增

 5、for与forEach的区别

1.for循环可以使用break跳出循环,但forEach不能。

2.for循环可以控制循环起点(i初始化的数字决定循环的起点),forEach只能默认从索引0开始。

3.for循环过程中支持修改索引(修改 i),但forEach做不到(底层控制index自增,我们无法左右它)。

6、script标签里面使用async和defer的区别:

当浏览器在解释html的时候,一旦遇到script标签,

就会停止加载先把script里面的内容先处理掉,如果

script标签里面有外部文件,就会先把外部文件下载

或者执行的步骤,这样浏览器才会继续加载。如果外部文件

刚好放在一个网络比较差的服务器上,那么整个页面的加载会

收到很大的影响。这就是同步带来的阻塞弊端。

用async和defer可以解决阻塞的问题,

当浏览器在加载页面的时候,遇到script带有async属性,

就会进入下载脚本,同时页面也会继续加载。因为不确定什么时候

脚本加载完成,有时候是页面加载完有时候页面还没加载完,

如果脚本是与页面DOM操作有关联的话,就会报错。

这时候用defer来推迟脚本执行是必要的,如果浏览器在

加载页面的时候,遇到script标签带有defer属性,就会马上

进入下载,同时页面也会继续加载,不管脚本是否下载完成,

都要等到页面加载完成后才会执行。并且这两种属性紧适用于外链接

,而且考虑到兼容性问题(async 在IE<=9时不支持,其他浏览器OK;defer 在IE<=9时支持但会有bug),

所以建议最好还是把script标签放到页面最后面比较合适。

7、如何改变一个函数a的上下文?

1.call()函数的用法

Function : 调用的函数

context : 新的对象上下文,函数中的this指向context,

若context为null|undefined,则执行window// arg1,arg2 : 参数列表  Function.call(context,arg1,arg2,...)

2.apply()函数的用法

[argArray] 这里传递的是数组

Function.apply(context,[argArray])

3.bind()函数的使用

bind()函数创建一个新函数,在调用时设置 this 关键字为提供的值,在执行新函数时,将给定的参数列表作为原函数的参数序列,从前往后匹配。语法如下:

Function.bind(context,arg1,arg2,...)

总结:

call()函数、apply()函数、bind()函数三者都会改变函数调用时的执行主体,修改this的指向。 call()函数、apply()两个函数是立即执行返回,而bind()函数是返回一个新函数,在任何使用可以调用。 apply()函数第二个入参为数组,与其他 2 个函数不一致。

8、数组中哪些常用方法会修改原数组

1.pop()

pop() 方法用于删除数组的最后一个元素并返回删除的元素。

此方法改变数组的长度!

移除数组第一个元素,请使用 shift() 方法。

2.push()

push() 方法可向数组的末尾添加一个或多个元素,并返回新的长度。

新元素将添加在数组的末尾。

此方法改变数组的长度。

在数组起始位置添加元素请使用 unshift() 方法。

3.reverse()

方法用于颠倒数组中元素的顺序。

4.sort()

let arr = [23,12,1,34,116,8,18,37,56,50];

function sequence(a,b){

return a - b;

}

console.log(arr.sort(sequence));

5.splice()

方法用于添加或删除数组中的元素。

这种方法会改变原始数组。

splice(index) ——> 从index的位置开始,删除之后的所有元素(包括第index个)
若 index < 0 , 则删除最后-index个元素

splice(index,howmany) ——> 删除从index位置开始的数,howmany为删除的个数
若 howmany 小于等于 0,则不删除

splice(index ,howmany , item1, …, itemX )

index >0 时
(1. howmany 为 0 时 不删除只添加 —— 在index位置前添加item1, …, itemX的数
(2. howmany > 0 删除且添加 —— 删除从index位置开始的数,howmany为删除的个数,并且在index位置前添加item1, …, itemX的数
index <0 时

最后一个数为 -1 依次倒数第二个数为-2
(1. howmany 为 0 时 不删除只添加 —— 在-index位置前添加item1, …, itemX的数
(2. howmany > 0 删除且添加 —— 删除从-index位置开始的数,howmany为删除的个数,并且在-index位置前(相当于往后 -2前是 -1)添加item1, …, itemX的数

9、数组中哪些常用方法会返回新数组

1.filter

filter() 方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素。

filter() 不会对空数组进行检测。

filter() 不会改变原始数组。

2.map

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

方法按照原始数组元素顺序依次处理元素。

不会对空数组进行检测。

不会改变原始数组。

10、数组去重有哪些办法?

1.indexOf:

for(let i = 0;i<=arr.length-1;i++) {

if(arr.indexOf(arr[i])!==i){

arr.splice(i,1);

i--

}}

console. log(arr);

2.filter+indexOf

var arr = [23,12,12,1,34,116,23,50,1];

var newArr=arr. filter(function(item, index){

return arr. indexOf(item)===index;

})

console. log(newArr);

3.利用空对象判断已经存储在空数组的数据数值

let obj={};

let newArr=[];

for(let i =0;i<=arr.length-1 ; i++){

if(!obj[arr[i]]){

obj[arr[i]]=true;

newArr.push(arr[i]);

}

}

console.log(newArr);

4.set类型存储数据时 自动 存储不重复的

const set = new Set(arr)

const newArr =[...set]

console.log(newArr)

5.reduce + includes

let array = arr.reduce((pre,cur)=>{

if(pre.includes(cur)=== false){

pre.push(cur)

}

return pre

},[])

console.log(array)

11、取数组最后一位有哪些办法?

1.arr.length-1

let arr = [23,12,12,1,34,116,23,50,1];

console.log(arr[arr.length-1])//1

2.slice

console.log(arr.slice(-1)[0])//1

3.使用pop()方法获取

console.log(arr.pop())//1

4.使用reduce

let tail = arr.reduce((pre,cur,index,array)=>{

if(index === arr.length-1) pre = cur

return pre

},0)

console.log(tail)//1

12、浅拷贝

1.

 2.Object.assign()

 3.展开符号... 

4.for in

13、深拷贝

let obj = {

name:'222',

b:[

{

age:12

}

],

show(){

}

}

1.使用for of + object.entries、、、、、、

function copy(data){

let obj = data instanceof Array ? []: {}

for(let [k,v] of Object.entries(data)){

console.log(k)

obj[k] = typeof v === "object" ? copy(v) : v

}

return obj

}

let obj1 = copy(obj)

2.使用 for in 、、、、、、、、、、、、

function copy(data){

let obj = data instanceof Array ? []: {}

if(data && typeof data ==='object'){

for(key in data){

if(data.hasOwnProperty(key)){

if(data[key]&& typeof data[key] ==='object'){

obj[key] = copy(data[key])

}else{

obj[key] = data[key]

}

}

}

}

return obj

}

let obj1 = copy(obj)

14、类和函数

class User{

constructor(name){

this.name = name}

show(){

}}

let u = new User('8888')

for(let key in u){

console.log(key,"------User")//只能遍历出来name

}

console.log(

JSON.stringify(

Object.getOwnPropertyDescriptor(User.prototype,"show"),null,2)

) //enumerable:false,所以show不能遍历出来

函数:show可以遍历出来,因为enumerable:true,可以枚举出来

15、js 继承方式

1.es6的class继承:

super()代表父类构造函数,调用之后生成一个继承父类的this对象

class Father3{

constructor(name){

this.name = name

console.log(name)

}

infor(){

console.log(this.name)

}

}

class Son extends Father3{

constructor(name){

super(name)

this.name = name

}

infor1(){

console.log(this.name,"========")

}}

let s = new Son('8888')

s.infor()

s.infor1()

2.原型链继承:(缺点:new 出来的实例,o2.age = '18',影响到o3.age的值了)

1.把b作为a的原型

let a = {name:'aaa'}

let b = {age:12}

Object.setPrototypeOf(a,b)

console.log(a,b.age)

2.函数

function Parent(){

this.age = 20

}

function Child(){

this.name ='张三'

}

Child.prototype = new Parent()

let o2 = new Child()

let o3 = new Child()

console.log(o2.name,o2.age)

3.借用构造函数继承:(缺点:不能继承父类原型链上的属性和方法

function Parent(){

this.age = 20

}

function Child(){

this.name ='张三'

Parent.call(this)

}

let o2 = new Child()

console.log(o2.name,o2.age)

、、、、、、、、、、、

let hd = {

data: [1, 2, 3, 34, 5, 7]

};

Object.setPrototypeOf(hd, {

max() {

return this.data.sort((a, b)=>b -a)[0];

}

})

let xj = {

lessons: { js: 87, php: 63, node: 99, linux: 88 },

get data() {

return Object.values(this.lessons)

}

}

console.log(hd.max.apply(xj));

4、 Object.create方法

let person = {

name: 'a',

age: 10,

hoby: ['唱', '跳'],

showName() {

console.log('my name is: ', this.name)

}

}

let child1 = Object.create(person)

child1.showName()

5、组合式继承:(弥补了原型链和构造函数继续的缺点

function Parent(){

this.age = 20

}

function Child(){

Parent.call(this)

this.name ='张三'

}

Child.prototype = new Parent()//这种写法缺点:如下

Child.prototype = Parent.prototype //这种写法缺点:给Child原型链上定义一个方法,Parent也有该方法

let o2 = new Child()//每次new 一个实例的时候,都调用了new Parent()

console.log(o2.name,o2.age)

6、寄生组合式继承

function Parent(){

this.age = 20

}

function Child(){

Parent.call(this)

this.name ='张三'

}

Child.prototype = Object.create(Parent.prototype)//浅拷贝

let o2 = new Child()

console.log(o2.name,o2.age)

7、原型工厂封装继承

function extend(sub,sup){

sub.prototype = Object.create(sup.prototype);

Object.defineProperty(sub.prototype, "constructor", {

value: sub,

enumerable: false

})

}

function User(name, age) {

this.name = name; this.age = age;

User.prototype.show = function() {

console.log(this. name, this. age);

}

};

function Admin(... args) {

User. apply(this, args);

}

extend(Admin,User)

let admin = new Admin("999",19)

admin.show()

9、对象工厂派生对象并实现继承

function User(name, age) {

this.name = name;

this.age = age;

User.prototype. show = function() {

console.log(this.name, this.age);

}

};

function admin(name,age){

const instance = Object.create(User.prototype)

User.call(instance,name,age)

return instance

}

let hd = admin("88",19)

hd.show()

15、代理:Proxy 支持的拦截操作主要有以下十三种:

1.代理对象

 let proxy = new Proxy(obj,{

get(obj,property){

return obj[property]

},

set(obj,property,value){

obj[property] = value

return true//不写严格模式下报错

}

})

proxy.name = '88888888888'

console.log(proxy.name)

2.函数代理

 

 3.数组代理

16、双向绑定的页面渲染

<script>

        function View(){

            let proxy = new Proxy({},{

                get(obj,property){},

                set(obj,property,value){

                    document.querySelectorAll(`[v-model="${property}"]`).forEach(item=>{

                        item.value = value

                    })

                    document.querySelectorAll(`[v-bind="${property}"]`).forEach(item=>{

                        item.innerHTML = value

                    })

                }

            })

            this.init= function(){

                const els = document.querySelectorAll('[v-model]')

                els.forEach(item=>{

                    item.addEventListener('keyup',function(){

                        proxy[this.getAttribute('v-model')] = this.value

                    })

                })

            }

        }

        new View().init()

    </script>

17、Chrome V8 执行 JavaScript 原理总结

最初的 V8 没有字节码,直接将 JavaScript 源码编译为机器码执行,这种架构导致内存占用过高,所以后来 V8 引入了字节码。V8 执行 JavaScript 的原理,大致可以分为三个步骤:

  1. 解析器(parser)将 JavaScript 源码解析为 AST(抽象语法树),解析过程分为词法分析和语法分析,V8 通过预解析提升解析效率;
  2. 解释器 Ignition 根据 AST 生成字节码并执行。这个过程中收集执行反馈信息,交给 TurboFan 进行优化编译;
  3. TurboFan 根据 Ignition 收集的反馈信息,将字节码编译为优化后的机器码,后续 Ignition 用优化机器码代替字节码执行,进而提升性能。

18、前端跨域问题

jsonp:通过script标签实现,只能get请求

Access-Control-Allow-Origin:必填,表示可以允许请求的来源。可以填写具体的源名称,也可以填写*表示允许任何源请求。

b、Access-Control-Allow-Methods:表示允许的请求方法列表。

c、Access-Control-Allow-Credentials:一个布尔值,表示是否允许发送cookie。默认情况下,cookie 不包含在 CORS 请求中。如果设置为 true,则表示服务器具有显式权限。Cookies 可以包含在请求中并一起发送到服务器。
vue框架的跨域:配置webpack.config.js文件(本地跨域方案)

19、判断一个对象是否为空

// 返回一个包含所有自身属性(不包含继承属性)的数组。

(类似于 Object.keys(), 但不会受enumerable影响, Object.keys返回所有可枚举属性的字符串数组). Reflect.ownKeys(target) //可以读取到symbol为属性的

Object.Keys(target) //不可以识别到symbol为属性的值,枚举为false也不能读取

var foo = {

a: 'name',

b: null,

c: undefined,

d: function () { },

e: Symbol(),

[Symbol('bar')]: 25

}

Object.defineProperty(foo, 'f', {

value: 42,

enumerable: false

});

foo.__proto__ = {

g: 233

}

console.log(Reflect.ownKeys(foo));

console.log(Object.keys(foo));

、、、、、、、、、

// 判断一个对象是否存在某个属性,和 in 运算符 的功能完全相同。

Reflect.has(target, name)

20、对象数组去重

21、reverse数组反转

let arr = [1,2,3,4,5,6]

1.arr.reverse()//会改变原数组

2.[...arr].reverse()//不会改变原数组

3.arr.slice().reverse()//不会改变原数组

4.for(let i = arr,length-1;i>=0;i--){

aa.push(arr[i)

}

5.reduce+unshift

15、

16、

17、

18、

19、

20、


 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值