javascript重点知识点总结

332 阅读21分钟

这是本人从开始学习JS一直到ES6记录的笔记中整理提炼出来的自己觉得比较重要的知识点。 希望对大家有点帮助 如果觉得整理的还可以就点个吧!嘿嘿

1.数据类型的分类和判断

基本数据类型 Number typeof() ==number String typeof() ==string Boolean typeof() == boolean undefind typeof() == undefind null typeof() == object

引用数据类型 Object typeof() ==object Array typeof() == object Function typeof() == function instance of :判断一个变量是否属于某个数据类型 null==undefined /true (判断值是否相等) null===undefined/false(判断值和数据类型是否相等)

2.内存,数据和变量三者之间的关系 内存是容器,用来存储不同数据 变量是内存的标识通过变量我们可以操作内存中的数据 内存分为栈内存和堆内存

栈内存:存放基本数据类型和引用数据类型的地址(地址指向堆内存中的数据)

堆内存:存放引用数据类型的数据 基本数据类型的赋值操作是将栈内存中的数据完整的复制了一份,互相不影响。引用数据类型的赋值操作是将栈内存中的地址赋值,他们指向的是同一块内存空间,相互影响。

3.实现一个冒泡排序 两层for循环,第一层循环决定冒泡要走多少趟(arr.length-1),第二层决定每一趟需要进行多少次比较(arr.length-i-1)

var arr=[3,2,1] var temp;
for(var i=0;i<=arr.length-1;i++){//外层循环管趟数
for(var j=0;j<arr.length-1-i;j++){//内层循环管每趟的交换次数
if(arr[j]<arr[j+1]){
temp=arr[j];
arr[j]=arr[j+1];
arr[j+1]=temp;
}
}
} 复制代码 4.JS常见的内置对象及其常用api

Math Math.floor()//向下取整 Math.ceil()//向上取整 Math.abs()//绝对值 Math.max()/min()//取最大最小值 Date getFullYear()//获取当年 getMonth()//获取当月(0~11) getDate()//获取当天日期 getHours()//获取当前小时 getMinutes()//获取当前分钟 getSeconds()//获取当前秒 Array push()//末尾添加一个或多个元素,修改原数组,返回新长度 pop()//末尾删除一个元素,修改原数组,返回删除的元素 unshift()//开头添加一个元素,修改原数组,返回新长度 shift()//开头删除一个元素,修改原数组,返回删除的元素 reverse()//翻转数组,修改原数组 sort()//数组排序,修改原数组 indexOf()//数组中查找给定元素的第一个索引,存在返回索引不存在返回-1 join(‘分隔符’)//将数组元素转化为以给定分隔符连接的字符串 concat()//连接多个数组,不影响原数组 slice(start,end)//数组截取,返回截取的数组 splice(start,num,newVal)//可以实现截取,替换,插入等方法 String indexOf('待查找字符',‘开始查找位置’)//返回指定内容在字符串中的位置 charAt(index)//返回指定位置的字符

5.实现数组去重的方法

ES6最常用 利用Set数据结构去重(Sst数据结构是一个有序的不重复的数据结构对象) function unique1(arr){ // Array.from 将一个类数组对象或者可遍历对象转化为一个真正的数组 // Set 容器就是一个可遍历对象 return Array.from(new Set(arr)) } 复制代码

ES5最常用 双重for循环 再利用splice去重

//双重for循环 外层循环元素,内层循环时比较值,删除相同即重复的值 function unique2(arr){ for(let i = 0 ; i < arr.length ; i++){ for(let j = i+1 ; j < arr.length ; j++){ if(arr[i] == arr[j]){ arr.splice(j,1) j-- } } } return arr } 复制代码

利用indexOf 去重 function unique3(arr){ let newArr = [] for(var i = 0 ; i < arr.length ; i++){ if(newArr.indexOf(arr[i]) === -1){ newArr.push(arr[i]) } } return newArr } 复制代码

利用filter 去重

function unique4(arr){ return arr.filter((item,index,arr)=>arr.indexOf(item) === index) } 复制代码 6.dom节点操作常用api

创建节点 document.write()//直接将内容写入页面的内容流中,文档流执行完毕页面会全部重绘 innerHTML()//将内容写入某个dom节点中,不会导致页面全部重绘 createElement()

插入节点 appendChild() insertBefore()

删除节点 node.removeChild()

修改节点 修改元素属性:src/href/title 修改普通元素内容:innerHTML/innerText 修改表单元素:value/type/disabled 修改元素样式:style/className

获取节点 DOM提供的(不推荐使用)getElementById/getElementByTagName H5提供的新方法:querySelector/querySelectorAll(获取到的是一个伪数组) 利用节点操作获取: 获取父元素:parentNode 获取子元素:children 获取兄弟元素:previousElementSibling/nextElementSibling

自定义属性操作 setAttribute getAttribute removeAttribute

7.事件委托 定义:将子节点的监听器设置在它的父节点上,然后利用事件的冒泡影响每个节点 例如:给ul注册点击事件,然后利用事件对象的target找到当前点击的Li,事件就会冒泡到ul上,进而触发对应的监听 优点:中操作了一次dom,提供了性能 8.本地存储 **只能存字符串类型的数据

sessionStorage(生命周期为浏览器窗口,同一个窗口数据可以共享) sessionstorage.setItem(key,val) sessionStorage.getItem(key,val) sessionStorage.removeItem(key) sessionStorage.clear localStorage(生命周期为永久,多窗口(同一个浏览器)数据可以共享) localstorage.setItem(key,val) localStorage.getItem(key,val) localStorage.removeItem(key) localStorage.clear

9.JS回收机制 首先垃圾回收机制是一个循环机制,js内部会定期扫描

计数清除(IE和低版本chrome) 看内存的地址上有几个指针指向,当指针指向为0时,说明这块地址是将要被清除的

标记清除 进入到代码执行环境以后检测到需要使用的变量就给这个变量添加一个入场标记,在代码执行完毕后,会再添加一个出厂标记。带有出厂标记的就是将要被清除的。

10.防抖和节流

函数的防抖(debounce)

概念:事件被触发n秒后,如果在和n秒内又被触发,就重新计时

应用场景:提价表单按钮的多次点击、向服务器请求图片等资源并需要对请求结果做进一步的处理

总结:函数防抖适合多次事件一次响应的情况 函数防抖是指一定时间内js方法只跑一次。前一次没结束,后一次触发会按后一次的新间隔时间计算。

代码实现(闭包) let timeout = null; function debounce(fn, wait) { return (args) => { if (timeout !== null) { clearTimeout(timeout) } timeout = setTimeout(() => fn(args), wait) } }//如果代码已经执行,再次触发时会把前一个定时器销毁,重新开启一个定时器 复制代码

函数的节流(throttle)

规定一个单位时间内,只能有一次触发事件的回调函数

应用场景:DOM元素的拖动,搜索框的联动响应提示功能,滚动条的持续触发

总结:函数节流适合大量事件按时间做平均分配触发 函数节流是指一定时间内js方法只跑一次。前一次没结束,后一次不会跑。

代码实现 let _lastTime = null; function throttle(fn, gapTime) { return (args) => { let _nowTime = + new Date(); if (_nowTime - _lastTime > gapTime || !_lastTime){ fn(args); _lastTime = _nowTime } } }//只有当两次函数的触发间隔小于规定时间间隔才会触发 复制代码

11.new的执行函数this指向

new执行时会发生的四件事

创建一个空对象 this指向这个空对象 执行构造函数里面的代码,给这对象添加属性和方法 返回新对象

函数的this指向 1 this默认也是一个变量,只不过在函数位吊用的时候没有具体的指向 2 函数的this不是定义的时候决定的,而是在调用的时候决定的 3 函数的自调用:this-->window 4 new 函数的时候:this-->当前生成的实例对象 5 obj.test():this-->obj

12.原型与原型链 js为了能将一类事物抽象出来,使相同一类事物能够拥有一样的属性跟方法,便将原型链作为实现继承的主要方式。原型链从数据结构来看,其实就是一个链表,实例有一个指向原型的指针,原型又包含一个指向构造函数的指针,层层递进

原型prototype(显示原型对象) 每个构造函数都有一个prototype属性,prototype就是一个对象。 作用:共享方法,一般情况下,我们的公共属性定义到构造函数里面,公共的方法我们放到原型里面。因为若果将方法放到构造函数里面,每创建一个实例都会开启一块内存空间存放方法,如果将方法定义到构造函数的原型上,它的实例就都可以通过原型链调用。

对象原型--proto--(隐式原型对象) 每个对象都有一个对象原型,指向构造函数的prototype,所以proto和prototype是等价的 作用:为对象的查找机制提供一个方向,一条线路。

构造函数、实例和原型对象之间的关系

原型链 function Foo () {} var f1 = new Foo() var f2 = new Foo() var o1 = {} var o2 = {}

  1. js成员查找机制

当访问一个对象的属性或方法时,首先查找这个对象自身有没有该属性 如果没有就查找它的原型(也就是proto指向的prototype原型对象) 如果还没有就查找原型对象的原型(Object的原型对象) 依次类推直到找到Object为空(null) proto对象原型的意义就在于为对象成员的查找机制提供一个方向,线路

13.执行上下文和执行上下文栈

变量提升与函数提升(预解析) 变量提升: 在变量定义语句之前, 就可以访问到这个变量(undefined) 函数提升: 在函数定义语句之前, 就执行该函数 函数提升优先于变量提升

理解 执行上下文: 由js引擎自动创建的对象, 包含对应作用域中的所有变量属性 执行上下文栈: 用来管理产生的多个执行上下文

分类和生命周期 全局(window):准备执行全局代码前产生, 当页面刷新/关闭页面时死亡 函数:调用函数时产生, 函数执行完时死亡

包含属性 全局: 用var定义的全局变量 ==>undefined 使用function声明的函数 ===>function this ===>window 函数: 用var定义的局部变量 ==>undefined 使用function声明的函数 ===>function this ===> 调用函数的对象, 如果没有指定就是window 形参变量 ===>对应实参值 arguments ===>实参列表的伪数组

执行上下文创建和初始化 1 js引擎在js代码马上执行之前创建并进入执行环境 2 创建一个空对象--->执行上下文对象 3 收集变量(var关键字),函数(function),函数的参数、 4 确定this的指向 5 创建作用域链 执行上下文栈:用于保存执行上下文对象(压栈的特点:先进后出,后进先出)

14.作用域和作用域链

理解 作用域: 一块代码区域, 在编码时就确定了, 不会再变化 作用域链: 多个嵌套的作用域形成的由内向外的结构, 用于查找变量

分类 全局、函数(js没有块作用域(在ES6之前))

作用 作用域: 隔离变量, 可以在不同作用域定义同名的变量不冲突 作用域链: 查找变量

作用域和执行上下文的区别 作用域: 静态的, 编码时就确定了(不是在运行时), 一旦确定就不会变化了 执行上下文: 动态的, 执行代码时动态创建, 当执行结束消失 联系: 执行上下文环境是在对应的作用域中的

面试题

主要考的是执行上下文栈的执行过程(当执行到代码时 产生执行上下文,然后 按先进后出、后进先出执行上下文对象操作,当i=4时退出函数体,代表的是进栈的结束,需要继续执行后面的代码语句,也就是出栈) //变量提升 var i         console.log('global begin:'+i)//undefined         var i=1         foo(1)         function foo(i){             //i=1             //i=2             //i=3             //i=4  跳出进栈的循环 开始执行递归后面的代码(出栈:前面没有执行完的继续执行)             if(i==4){                 return             }             console.log('foo() begin'+i);//进栈输出 1 2 3             foo(i+1)//递归              console.log('foo() end'+i);//出栈输出 (后进先出)3 2 1          }         console.log('global end'+i);//最后输出:1 复制代码

第一个:注意ES6之前JS没有块级作用域,所以var b=1会产生变量提升 第二个:注意变量和函数同名,函数提升优先于变量提升,然后先跳过函数体看函数的调用,由于同名c=1(常数)常数不能调用因此报错 if(!(b in window)){ var b = 1 } console.log(b)//undefined //c = function(){} var c = 1//c = 1 function c(c){ console.log(c) var c = 3 } c(2)//报错 复制代码

15.闭包

理解 定义:当嵌套的内部函数引用了外部函数的变量时就产生了闭包 通过chrome工具得知: 闭包本质是内部函数中的一个对象, 这个对象中包含引用的变量属性(键值对) 闭包的形成条件:(1)函数嵌套(2)内部函数引用外部函数的局部变量(3)外部函数调用

作用 延长局部变量的生命周期,让函数外部能操作内部的局部变量

闭包的应用

循环遍历加监听: 给多个dom加点击监听, 读取当前下标

模块化: 封装一些数据以及操作数据的函数, 向外暴露一些行为(模块化的核心就是把闭包作为返回值传递出去)

JS框架(jQuery)大量使用了闭包

设计私有变量

实现防抖函数

闭包的问题及解决方法 闭包带来的问题:由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,可能会产生内存泄露,所以不能滥用闭包,否则会造成网页的性能问题 解决方法:利用js的垃圾回收机制,将不需要的函数名赋值为null

面试题

函数执行的作用域在编码的时候就决定了,并且不会再改变 function print(fn) { const a = 200 fn() } // 函数作为参数被传递

    const a = 100
  
    function fn() {
        console.log(a) // 在定义的地方往上查找,找到外面全局定义的 a 变量,所以是打印 100
    }
  
    print(fn) // 100

复制代码

每循环一次开启一个定时器,但定时器是异步执行的,只有但for循环执行完毕,也就是i=4的时候才执行 let i for( i = 1 ; i<=3 ; i++ ){ setTimeout( function() { console.log(i) } , 0 ) }// 打印3次4 复制代码

js代码逐行执行,注意作用域 let a = 100

    function test () {
        alert(a)
        a = 10
        alert(a)
    }
  
    test()
    alert(a)// 100 10 10

复制代码

object.getNameFunc()()相当于函数的自调用,this指向window var name='the window';         var object={             name:'my object',             getNameFunc:function(){                 return function(){                     return this.name                 }             }         } console.log(object.getNameFunc()());// the window 复制代码

object内部缓存了this var name = 'the window' var object = { name1:'my object' getNameFunc:function(){ var that = this //缓存this return function(){ return that.name1 } } } console.log(object.getNameFunc()())//my object 复制代码

最难面试题 function fun(n,o){             console.log(o);             return {                 fun: function (m){                     return fun(m,n)                 }             }         }         var a = fun(0)//打印 undefined 把函数返回值给a实际上就是给他一个闭包{n:0}         // //a=fun(0)===a=fun: function (m){         //             return fun(m,n)         //         }         //没有定义变量保存值 所以没有产生新的闭包         a.fun(1)//m=1 n=0 打印0         a.fun(2)//0         a.fun(3)//0         //每次产生新的闭包         var b=fun(0).fun(1).fun(2).fun(3).fun(50).fun(2)//undefined 0 1 2 3 50         var c=fun(0).fun(1)//underfined 0         c.fun(2) //1         c.fun(3) //1 复制代码

16.内存溢出和内存泄露

内存溢出 一种程序运行出现的错误,当程序运行需要的内存超过了剩余的内存时, 就出抛出内存溢出的错误

内存泄露 占用的内存没有及时释放,内存泄露积累多了就容易导致内存溢出

常见的内存泄露

意外的全局变量

没有及时清理的计时器或回调函数

闭包

17.对象的创建模式和继承模式

对象的创建模式

Object构造函数创建 var obj = {} obj.name = 'Tom' obj.setName = function(name){this.name=name} 复制代码

对象字面量创建 var obj = { name : 'Tom', setName : function(name){this.name = name} } 复制代码

构造函数模式创建 function Person(name, age) { this.name = name this.age = age this.setName = function(name){this.name=name} } new Person('tom', 12) 复制代码

构造函数+原型组合模式创建 function Person(name, age) { this.name = name this.age = age } Person.prototype.setName = function(name){this.name=name} new Person('tom', 12) 复制代码

继承模式

原型链继承:继承方法 function Parent(){} Parent.prototype.test = function(){} function Child(){} Child.prototype = new Parent() //子类原型指向父类的实例 Child.prototype.constructor = Child //原型的构造器指向构造函数 var child = new Child() child.test() //调用父类型的方法 复制代码

构造函数继承:继承属性 function Parent(xxx){this.xxx = xxx} Parent.prototype.test = function(){} function Child(xxx,yyy){ Parent.call(this, xxx) //借用父类型的构造函数 相当于:this.Parent(xxx) } var child = new Child('a', 'b') //child.xxx为'a', 但child没有test() 复制代码

组合继承 function Parent(xxx){this.xxx = xxx} Parent.prototype.test = function(){} function Child(xxx,yyy){ Parent.call(this, xxx) //借用构造函数 this.Parent(xxx) } Child.prototype = new Parent() //得到test() Child.proptotype.constructor = Child var child = new Child() //child.xxx为'a', 也有test() 复制代码

18.线程和进程

进程 程序的一次执行,他占用一片独有的内存空间,可以通过windows任务管理器查看

线程 是进程的一个独立单元,是程序执行的一个完整流程,是cpu的最小调度单元

关系 一个进程至少有一个线程(主),程序是在某个线程执行的

19.浏览器内核模块组成

主线程 js引擎模块:负责js程序的编译和运行 html,css文档解析模块:负责页面文本的解析 DOM/CSS模块:负责dom/css在内存中的相关处理 布局和渲染模块:负责页面的布局和效果的绘制(内存中的对象)

分线程 定时器模块:负责定时器的管理 事件响应模块:负责事件的管理 网络请求模块:复杂ajax请求

js线程 js是单线程执行的(回调函数也是在主线程) H5提出了实现多线程的方案: Web Workers --->Worker 只能是主线程更新界面

20.事件循环机制

1 JS是单线程的 2 所有的JS代码都会在主线程执行 3 同步任务加载即执行 4 异步任务不会立即执行,而是会交给对应的管理模块(DOM事件管理模块、ajax请求管理模块、定时器模块) 5 管理模块一直在监视异步任务是否满足条件,如果满足条件会对应的回调放入callback quene(回调队列) 6 主线程上的同步任务执行完以后会通过event Loop(事件轮询机制)询问callback queue 查看事件是否有可执行的回调函数,如果有将回调钩到主线程上执行,如果没有待会再来问

异步任务分为微任务和宏任务,他们都属于一个队列,主要区别在于它们的执行顺序(宏任务执行完如果有可执行的微任务则执行完微任务才会继续执行下一个宏任务) 形象理解:就像那些去银行排队办理业务的人,柜员里的业务员是js单线程,来排队的每一个人是宏任务,每一个人会办理几个业务,每一个业务是微任务,也就是每一个宏任务带着几个微任务一个个去执行。也就是只能处理完了当前所有的微任务,才会开始下一个宏任务 宏任务:包括整体代码script,setTimeout,setInterval,setImmediate。 微任务:原生Promise(有些实现的promise将then方法放到了宏任务中)、process.nextTick、MutationObserver

21.对面相对象和class的理解

class

class本质还是function class的所用方法都定义在class的prototype上 class创建的实例,里面也有--proto--指向类的prototype对象 class就是面向对象编程的语法糖

面向对象 包好一系列属性和方法,供实例复用。具有封装、继承和多态性

22.json对象

定义:传输的一种字符串形式的数据 种类:JSON对象、JSON数组(形式是键值对,key也必须加双引号) 使用:JSON.stringify()==>把js对象(数组)转换为JSON对象(数组)、JSON.parse()==>把JSON对象(数组)转换为js对象(数组)

23.对象拓展方法 Object.defineProperties(object,descrition) 作用:为指定对象定义拓展多个属性 --get:用来获取当前属性值的回调函数 --set:修改当前属性值触发的回调函数,并且实参即为修改后的值 存取器属性:setter ,getter一个用来存值,一个用来取值  var obj={}         var sex='男'         Object.defineProperties(obj,{             sex:{                 //设置值,当获取拓展属性值得时候,get自动被调用                 get:function(){                     console.log('get被调用了',this);                     return sex;                 },                 //当修改属性值的时候,set自动被调用                 set:function(value){                     console.log("set被调用了",value,this);                     sex=value;                 }             }         })         console.log(obj.sex);         obj.sex='女'         console.log(obj.sex); 复制代码 24.数组拓展方法 // filter map reduce //需求 将下面的数组中小于100的整数筛选出来,再将筛选出来的元素值乘以2, 最后求和 let arr = [10,20,30,40,110,220] //filter:根据条件筛选出符合条件的元素并返回一个新数组 let arr1 = arr.filter(item=>item<100) //map:将指定数组映射成一个新数组 let arr2 = arr1.map(item=>item2) //reduce:将指定数组元素进行汇总 //需要传入两个参数 pre:前一次遍历返回的值 第一次默认为0 n:当前遍历元素的值 let total = arr2.reduce((pre,n)=>pre+n) //函数式编程写法 let result = arr.filter(item=>item<100).map(item=>item2).reduce((pre,n)=>pre+n) 复制代码 25.call、apply和bind 相同点:都可以改变函数内部的this指向 不同点:

call和apply会直接调用函数,bind不会直接调用函数 call传递参数直接以逗号分隔,apply传递参数必须是数组形式

应用场景

call常用于继承 apply经常和数组有关系,比如借助Math对象实现求数组最大最小值 bind用于不立即调用函数但想改变this指向,例如改变定时器内部的this指向

26.函数柯里化

概念 把接收多个参数的函数变成接收单一参数的函数,并且返回剩余参数的新函数。

作用: 避免不停的传入相同的参数

实现代码 在curry这个函数当中,返回一个叫做currify的函数,之所以不能直接使用匿名函数,是因为我们还需要在内部递归调用currify。之后获取参数存在args中。关键的步骤来了:如果args的长度大于fn.length(函数的length就是函数期望传入参数的长度),这说明已经传入足够的参数,就可以使用fn.apply执行fn;但是,如果传入的参数个数不足,那么就会返回currify.bind(null, ...args);把当前的参数列表传给currify这个函数。 function sum(a, b, c) { return a + b + c; } function curry(fn) { return function currify() { const args = Array.prototype.slice.call(arguments); return args.length >= fn.length ? fn.apply(null, args) : currify.bind(null, ...args); } } var currySum = curry(sum); 复制代码

---ES6部分--- 1.ES6常见新特性

类class

遍历对象方法for in/for of

对象和数组的解构

rest操作符(三点运算符)//取代arguments

模板字符串//解析对象解构,将对象中的数据赋值给指定变量

const、let

形参默认值

箭头函数

同名的属性,function的简化写法

promise、await&async

module ……

2.var,let,const区别

var: var声明的变量不存在块作用域 var声明的全局变量会自动变成window对象的属性 var声明的变量会提升(声明会提前,但赋值不会,还是会报undefined的错) var声明的变量在同一个作用域下可以被重复声明,而let 和 const 则不被允许,否则报错

let.const let.const存在块级作用域,let定义变量,const定义常量

3.promise

定义:promise是js的内置数据结构,代表未来某个时间将要发生的事情(通常是一个异步操作)

作用:解决异步回调,将异步操作以同步的流程表达出来,避免层层嵌套回调函数

状态:初始化(pending)成功(fullfilled)失败(rejected)

语法:

创建promise实例对象(初始化promise对象的状态为pending) 执行异步任务:例如开启定时器、发送ajax请求 根据异步任务执行的结果修改promise的状态:成功resolve(),失败reject() promise实例对象有then方法,该方法有两个参数,分别是成功和失败状态的回调函数

应用:(1)实现超时处理(2)封装ajax请求

实现用promise加载一张图片 function loadImg(src){ return new Promise((resolve,reject)=>{ const img = document.createElement('img') img.onload = () => { resolve(img) }else{ reject(new Error(图片加载失败${img})) } img.src = src }) } loadImg(url).then( img => { console.log(img.width) return img } ).then( img => { console.log(img.height) } ).catch( ex => console.error(ex) ) 复制代码

4.symbol数据类型 前言:ES5中对象的属性名都是字符串,容易造成重名,污染环境 symbol概念:ES6中的添加了一种原始数据类型(已有的数据类型:string、number、boolean、function、object) 特点: 1 symbol属性对应的值是唯一的,解决命名冲突问题 2 symbol值不能与其他数据进行计算,包括童字符串拼接 3 for in ,for of 遍历是不会遍历symbol属性 内置symbol值:除了定义自己使用的symbol值之外,ES6还提供了11个内置的symbol值,指向语言内部的使用方法。 5.Iterator迭代器

概念:iterator是一种接口机制,为不同的数据结构提供统一的访问机制

作用

为各种数据结构提供一个统一的、便捷的访问接口 使得数据结构的成员能够按某种次序排列 ES6创造了一种新的遍历命令(for of),iterator接口供for of 消费

工作原理

创建一个指针对象(遍历器对象),指向数据结构的起始位置

第一次调用next方法,指针自动指向数据结构的第一个成员

接下来调用next方法,指针会一直往后移动,知道指针指向最后一个成员

每调用next方法会返回一个包含value和done的对象,{value:当前成员的值,done:boolean(表示是否遍历结束)} 当遍历结束时{value:undefined,done:true}

原生具备interator接口的数据(可用 for of 遍历) (1)Array (2)Arguments (3)set容器(4)map容器 (5)string

实现一个迭代器功能 function iteratorUtil(target){ let index = 0 //表示指针的起始位置 return {//返回指针对象 next(){//指针对象的next方法 return index < target.length ? {value:target[index++],done:false} : {value:target[index++],done:true} } } } 复制代码

数组和对象实现迭代 function iteratorUtil(target){ let index = 0 //指针起始位置 let that = this //缓存this //判断目标数据结构是数组还是对象 if(that instanceof Array){ return { next(){ return index< that.length ? {value:that[index++],done:false} : {value:that[inedx++],done:true} } } }else{ let keys = Object.keys(that)//以数组的形式返回对象的所有属性名 let length = keys.length return { next(){ //let key = keys[index++]获取指针指向位置的Key值 return index < length ? {value:that[keys[index++],done:false} : {value:that[keys[index++],done:true} } } } }         //把数组原来内置的iterator接口换成自己定义的迭代器         Array.prototype[Symbol.iteratior] = iteratorUtil         let arr = [1, 2, 3, 4, 5]         for (let i of arr) {//使用Symbol.iterator接口             console.log(i);         }         let person={             name:'zty',             age:22         }         Object.prototype[Symbol.iterator]=iteratorUtil         for (let i of person){             console.log(i);         } 复制代码

6.async函数

概念:真正意义上解决异步回到问题,同步流表达异步操作(本质是Generator的语法糖)

语法:

定义:async function(){}

函数里搭配使用await异步操作

await后面跟异步任务,当执行到异步任务的时候await会阻塞,当异步任务成功以后在继续执行代码 **默认返回promise对象,await后面跟的是回调相当于异步任务

async+promise实现例子 function promiseUtil(){             return new Promise((resolve,reject)=>{                 setTimeout(()=>{                     //异步任务执行成功                     let data='传递的数据'                     resolve(data)                 },2000)             })         }         //定义一个async函数         async function asyncUtil(){             let value1=await promiseUtil()             console.log(我是第一次${value1});             let value2=await promiseUtil()             console.log(我是第二次${value2});         } asyncUtil() 复制代码

7.拓展方法

字符串方法 (1)Includes(str): 判断是否包含指定的字符串 (2)StartsWith(str):判断是否以指定字符串开头 (3)endsWith(str):判断是否以指定字符串结尾 (4)Repeat(count):重复指定次数

数值方法 (1)二进制与八进制表示法: 二进制0b 八进制:0o (2)Number.isFinite(i):判断是否是有限大的数 (3)Number.isaNaN(i):判断是否是 NaN (4)Number.isInteger(i):判断是否是整数 (5)Number.parseInt(i):将字符串转化为对应的数值 (6)Math.trunc(i):直接去除小数部分

数组方法 (1)Array.from(v):将伪数组对象或者可遍历对象转化为真数组 (2)Array.of(vv1,v2,v3):将一系列值转化为数组 (3)Find(function(value,index,arr){return true}):找出第一个满足条件返回true (4)Findindex(function(value,index,arr){return true}):找出第一个满足条件返回元素的索引

对象方法 (1)Object.is(v1,v2):判断两个数据是否完全相等 (2)Object.assign(target,source1,source2):将原对象的属性复制到目标对象上 (3)直接操作__proto__属性

8.赋值,浅拷贝和深拷贝

赋值:当我们把一个对象赋值给一个新的变量时,赋的其实是该对象的在栈中的地址,而不是堆中的数据。也就是两个对象指向的是同一个存储空间,无论哪个对象发生改变,其实都是改变的存储空间的内容,因此,两个对象是联动的。 浅拷贝:重新在堆中创建内存,拷贝前后对象的基本数据类型互不影响,但拷贝前后对象的引用类型因共享同一块内存,会相互影响。 深拷贝:从堆内存中开辟一个新的区域存放新对象,对对象中的子对象进行递归拷贝,拷贝前后的两个对象互不影响

9.克隆

数据类型

基本数据类型特点:存储的是该对象在栈中的实际数据 引用数据类型特点:存储的是该对象在栈中的引用地址,该地址指向堆中的实例数据

区别浅拷贝和深拷贝 判断:拷贝产生了新的数据还是拷贝的是数据的引用 **对象数据存放的是对象在栈内存中的引用,直接赋值是对象的引用

常用的拷贝技术

arr.concat():数组的浅拷贝 arr.slice():数组的浅拷贝 JSON.parse(JSON.stringify(obj/arr)):数组或对象的深拷贝,但不能处理函数数据

自己封装一个深拷贝 核心思想:克隆数据不能有引用数据类型,如果有就继续遍历挖掘直到每次拷贝的对象是基本数据类型 //获取数据类型(返回数据类型的字符串形式) function getType(target){ return Object.prototype.toString.call(target).slice(8,-1) } function cloneUtil(target){ let result //判断克隆目标是对象还是数组 if(getType(target) === 'Object'){ result = {} }else if(getType(target) === 'Array'){ result = [] }else{ return target } //遍历目标对象 for(let i in target){ //搜集个体数据 let item = target[i] if(getType(item) === 'Object'||'Array'){ result[i] = cloneUtil(item) }else{ result[i] = item } } return result } 复制代码

10.set,map容器

set容器(集合) 定义:无序的不可重复的多个value的集合体(非数组但可遍历) 方法: (1)Set() (2)Set(array) (3)Add(value) (4)Delete(value) (5)Has(value) (6)Clear() (7)Size (相当于数组中的length)

map容器(映射) 定义:用于存取键值对的数据结构,一个键只能对应一个值且不能重复

11.手写promise const PENDING = "pending";//初始值,不是fulfilled,也不是rejected const FULFILLED = "filfilled";//代表操作成功 const REJECTED = "rejected";//代表操作失败

    function myPromise(fn) {
        console.log(1);
        let that = this;
        that.state = PENDING;
        that.value = null;
        that.resolvedCallBacks = [];
        that.rejectedCallBacks = [];

        // 首先两个函数都得判断当前状态是否为等待中,因为规范规定只有等待态才可以改变状态
        // 将当前状态更改为对应状态,并且将传入的值赋值给 value
        // 遍历回调数组并执行

        function resolve(value) {
            console.log(2);

            if (that.state == PENDING) {
                that.value = value;
                that.resolvedCallBacks.map(cb => that.value);
            }
        };
        function reject(value) {
            console.log(3);

            if (that.state == PENDING) {
                that.value = value;
                that.rejectedCallBacks.map(cb => that.value);
            }
        };

        // 实现很简单,执行传入的参数并且将之前两个函数当做参数传进去
        // 要注意的是,可能执行函数过程中会遇到错误,需要捕获错误并且执行 reject 函数
        try {
            console.log(4);

            fn(resolve, reject)
        } catch (e) {
            console.log(5);

            reject(e)
        }
    }

    myPromise.prototype.then = function (onFulfilled, onRejected) {
        const that = this;
        console.log(6);
        // 首先判断两个参数是否为函数类型,因为这两个参数是可选参数
        // 当参数不是函数类型时,需要创建一个函数赋值给对应的参数,同时也实现了透传
        onFulfilled = typeof onFulfilled === "function" ? onFulfilled : v => v;
        onRejected = typeof onRejected === "function" ? onRejected : r => { throw r };
        // 接下来就是一系列判断状态的逻辑,当状态不是等待态时,就去执行相对应的函数。
        // 如果状态是等待态的话,就往回调函数中 push 函数
        if (that.state === PENDING) {
            that.resolvedCallBacks.push(onFulfilled);
            that.rejectedCallBacks.push(onRejected);
        }
        if (that.state === FULFILLED) {
            onFulfilled(that.value)
        }
        if (that.state === REJECTED) {
            onRejected(that.value)
        }
    }

作者:野生前端宝可梦 链接:juejin.cn/post/691110… 来源:掘金 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。