在js中,我们可以分为两种类型:
null
来填充该变量JavaScript
数组是一组有序的数据,但跟其他语言不同的是,数组中每个槽位可以存储任意类型的数据其他引用类型
date。RegExp,Map,Set等
基本数据类型和引用数据类型存储在内存中的位置不同:
基本数据类型存储在栈中
引用类型的对象存储于堆中
数组操作的增删改查操作
splice():接收三个参数,分别是开始位置,要删除元素的数量,要插入的任意多个元素,返回删除元素的数组,对原数组产生影响
join()接收一个参数,即字符串分隔符,返回包含所有项的字符串
这里增的意思不是说直接增添内容,而是创建字符串的副本,再进行操作
除了我们常用的+以及${]进行字符串拼接外,还可以通过concat
concat()用于将一个或者多个字符串拼接为一个新的字符串
这三个方法都返回调用它们的字符串的一个子字符串,而且都接收一或两个参数
trim()、trimLeft()、trimRight()删除前、后或前后所有空格符,再返回新的字符串
repeat()接收一个整数参数,表示要将字符串复制多少次,然后返回拼接所有副本后的结果
padStart()、padEnd()复制字符串,如果小于指定长度,则在相应一边填充字符,直至满足长度条件
toLowerCase()、 toUpperCase()大小写转化
chatAt()返回给定索引位置的字符,由传给方法的整数参数指定
indexOf()从字符串开头去搜索传入的字符串,并返回位置(如果没找到,则返回 -1 )
startWith()从字符串中搜索传入的字符串,并返回一个表示是否包含的布尔值
includes()从字符串中搜索传入的字符串,并返回一个表示是否包含的布尔值
split()把字符串按照指定的分割符,拆分成数组中的每一项
RegExp
对象,返回数组RegExp
对象,找到则返回匹配索引,否则返回 -1常见的类型转换有:
强制转换(显示转换)
parseInt
相比Number
,就没那么严格了,parseInt
函数逐个解析字符,遇到不能转换的字符就停下来自动转换 (隐式转换)
何时会发生隐式转换?
我们这里可以归纳为两种情况发生隐式转换的场景:
==
、!=
、>
、<
)、if
、while
需要布尔值地方+
、-
、*
、/
、%
)除了上面的场景,还要求运算符两边的操作数不是同一类型
在需要布尔值的地方,就会将非布尔值的参数自动转为布尔值,系统内部会调用Boolean
函数
可以得出除了undefined,null,false,+0,-0,NaN,""会被转换为false,其他的会被转换为true
遇到预期为字符串的地方,就会将非字符串的值自动转为字符串
具体规则是:先将复合类型的值转为原始类型的值,再将原始类型的值转为字符串
常发生在+
运算中,一旦存在字符串,则会进行字符串拼接操作
除了+有可能把运算转为字符串,其他的运算符都会把运算自动转成数值
null
转为数值时,值为0
undefined
转为数值时,值为NaN
==和===的区别?分别在什么情况下使用
如果操作数相等,则会返回true,在比较中会先进行类型转换,再确定操作数是否相等
遵循以下规则:
两个都为简单类型,字符串和布尔值都会转换成数值,再比较
简单类型与引用类型比较,对象转化成其原始类型的值,再比较
两个都为引用类型,则比较它们是否指向同一个对象
null 和 undefined 相等
存在 NaN 则返回 false
全等操作符由 3 个等于号( === )表示,只有两个操作数在不转换的前提下相等才返回 true
。即类型相同,值也需相同
undefined
和 null
与自身严格相等
相等操作符(==)会做类型转换,再进行值的比较,全等运算符不会做类型转换
null
和 undefined
比较,相等操作符(==)为true
,全等为false
总结:
除了在比较对象属性为null
或者undefined
的情况下,我们可以使用相等操作符(==),其他情况建议一律使用全等操作符(===)
深拷贝:值的是创建一个新的数据,这个数据有着原始数据属性值的一份精准拷贝
如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝的就是内存地址
即浅拷贝只是拷贝一层,深层次的引用类型则是共享内存地址
Object.assign
Array.prototype.slice()
, Array.prototype.concat()
深拷贝开辟了一个新的栈,两个对象属性完全相同,但是对应了两个不同的地址,修改一个对象的属性,不会该白你另一个对象的属性
常见的深拷贝的方式有:
_.cloneDeep()
jQuery.extend()
JSON.stringify()
手写循环递归
浅拷贝只复制属性指向某个对象的指针,而不复制对象本身,新旧对象还是会共享同一块内存,修改对象属性会影响原对象
深拷贝会创造出一个一摸一样的对象,新旧对象不共享内存,修改新对象不会影响到原对象
函数嵌套函数,内部函数始终可以访问到其外部函数的作用域以及参数,这就是闭包
在js中,我们每创建一个函数,闭包就会在函数创建的同时被创建出来,作为函数内部与外部连接起来的一座桥梁
创建私有变量
延长变量的生命周期
一般函数的词法环境在函数返回后就被销毁,但是闭包会保存对创建时所在词法环境的引用,即便创建时所在的执行上下文被销毁,但是创建时所在词法环境仍然存在,以达到延长变量生命周期的目的
柯里化的目的在于避免频繁的调用具有相同参数函数的同时,又能轻松的重用
如果不是某些特定任务需要使用闭包,在其他函数中创建函数是不明智的,因为闭包在处理速度和内存消耗方面对脚本性能具有负面影响
作用域,即变量和函数生效的区域或集合,作用域决定了代码块中变量和其他资源的可见性
我们一般将作用域分为三种:
全局作用域:任何不再函数中或者是大括号中声明的变量,都是在全局作用域下,全局作用域下声明的变量可以在程序的任意位置访问
函数作用域:函数作用域也叫局部作用域,如果一个变量是在函数内部声明的它就在一个函数作用域下面,这些变量只能在函数内部访问,不能在函数以外进行访问
块级作用域:像我们es6中引入了let和const关键字,let和const属于块级作用域,在大括号以外不能访问这些变量
又叫静态作用域,变量被创建时确定好了,而非执行阶段确定的,也就是说我们写好的代码时他的作用域就确定了,js遵循的就是词法作用域
在js中使用一个变量的时候,首先js引擎会尝试在当前作用域下去寻找该变量,如果没有找到变量,会到他的上层作用域寻找,以此类推直到找到该变量或者到了全局作用域
如果在全局作用域仍然找不到该变量,他就会在全局范围内隐式声明该变量
js常被描述为一个基于原型的语言--每个对象拥有一个原型对象
当试图访问一个对象的属性时,它不仅仅在该对象上搜寻,还会搜寻该对象的原型,以及该对象的原型的原型,依次向上搜索,直到找到一个名字匹配的属性或者达到原型链的顶端
原型链:
原型对象也可能拥有原型,并从中继承方法和属性,一层一层,以此类推,这种关系被称为原型链,他解释了为何一个对象会拥有定义在其他对象中的属性和方法
在对象实例和他的构造器之间建立一个链接,_proto_属性,是从构造函数的prototype属性中派生的,之后可以通过上溯原型链,在构造器中找到这些属性和方法
总结:
一起对象都是继承自object对象,object对象直接继承根源对象null
一切的函数对象,都是继承自Function对象
object对象直接继承自Function对象
Function对象的_proto_会指向自己的原型对象,最总还是继承自object对象
继承:是面向对象软件技术中的一个概念
如果一个类别B“继承自”另一个类别A,就把这个B称为“A的子类”,而把A称为“B的父类别”也可以称“A是B的超类”
继承的优点:
继承可以使得子类具有父类别的各种属性和方法,而不需要再次编写相同的代码
再子类别继承父类别的同时,可以重新定义某些属性,并重写某些方法,即覆盖父类别的原有属性和方法,使其获得于父类别不同的功能
原型链继承:是比较常见的一种继承方式之一,其中设计的构造函数、原型和实例,三者之间存在一定的关系,即每一个构造函数中都会有一个原型对象,原型对象有包含一个指向构造函数的指针,而实例则包含一个原型对象的指针
构造函数继承(借助 call)借助call调用Parent函数,父类原型对象中一旦存在父类之前自己定义的方法,那么子类将无法继承这些方法,父类的引用属性不会被共享,优化了原型链继承的弊端,但是它只能继承父类的实例属性和方法,不能继承原型属性或者方法
组合继承:组合继承就是将原型链继承和构造函数继承结合起来
原型式继承:借助Object.create方法实现普通对象的继承:因为它实现的是浅拷贝,多个实例的引用类型属性指向相同的内存,存在篡改的可能
寄生式继承:就是利用浅拷贝的能力进行增强,添加一些其他方法
寄生组合式继承:借助解决普通对象的继承问题的Object.create
方法,在前面几种继承方式的优缺点基础上进行改造,这也是所有继承方式里面相对最优的继承方式
函数的this关键字在js中表现得略有不同,在严格模式下和非严格模式下也会有一些差别
在绝大多数情况下,函数的调用方式决定了this的值
this关键字是在函数运行时自动生成一个内部对象,只能在函数内部使用,总能调用他的对象
在this的执行过程中,一旦被确定,就不能再进行更改
默认绑定:全局环境中定义person
函数,内部使用this
关键字
隐式绑定:函数还可以作为某个对象的方法调用,这时this
就指这个上级对象
new绑定:通过构建函数new
关键字生成一个实例对象,此时this
指向这个实例对象
显示绑定:apply()、call()、bind()
是函数的一个方法,作用是改变函数的调用对象。它的第一个参数就表示改变后的调用这个函数的对象。因此,这时this
指的就是这第一个参数
箭头函数
es6中还提供了箭头函数语法,让我们在代码书写时就能确定this指向
虽然箭头函数的this能够在编译的时候就确定this的指向,但是我们也需要注意一些潜在的坑
箭头函数没有this指向,箭头函数不能作为构造函数
new绑定优先级 > 显示绑定优先级 > 隐式绑定优先级 > 默认绑定优先级
简单来说,执行上下文是一种对js代码执行环境的抽象概念,也就是说只有js代码运行,那么它就一定是运行在执行的上下文中
执行上下文的类型分为三种:
window
对象,this
指向这个全局对象eval
函数中的代码,很少用而且不建议使用执行上下文的生命周期包括三个阶段:创建阶段 → 执行阶段 → 回收阶段
创建阶段:
创建阶段做了三件事:
This Binding
词法环境
词法环境有两个组成部分:
全局环境:是一个没有外部环境的词法环境,其外部环境引用为null
,有一个全局对象,this
的值指向这个全局对象
函数环境:用户在函数中定义的变量被存储在环境记录中,包含了arguments
对象,外部环境的引用可以是全局环境,也可以是包含内部函数的外部函数环境
变量环境
变量环境也是一个词法环境,因此它具有上面定义的词法环境的所有属性
在 ES6 中,词法环境和变量环境的区别在于前者用于存储函数声明和变量( let
和 const
)绑定,而后者仅用于存储变量( var
)绑定
在这阶段,执行变量赋值、代码执行
如果 Javascript
引擎在源代码中声明的实际位置找不到变量的值,那么将为其分配 undefined
值
执行上下文出栈等待虚拟机回收执行上下文
javascript
中的事件,可以理解就是在HTML
文档或者浏览器中发生的一种交互操作,使得网页具备互动性, 常见的有加载事件、鼠标事件、自定义事件等
事件流都会经历三个阶段:
事件冒泡是一种从下往上的传播方式,由最具体的元素(触发节点)然后逐渐向上传播到最不具体的那个节点,也就是DOM
中最高层的父节点
事件捕获与事件冒泡相反,事件最开始由不太具体的节点最早接受事件, 而最具体的节点(触发节点)最后接受事件
事件模型可以分为三种:
原始事件模型(DOM0级)html代码直接绑定,js代码绑定
特性:
绑定速度快
只支持冒泡,不支持捕获
同一个类型的事件只能绑定一次
标准事件模型(DOM2级)
document
一直向下传播到目标元素, 依次检查经过的节点是否绑定了事件监听函数,如果有则执行document
, 依次检查经过的节点是否绑定了事件监听函数,如果有则执行特性:
- 可以在一个
DOM
元素上绑定多个事件处理器,各自并不会冲突当第三个参数(
useCapture
)设置为true
就在捕获过程中执行,反之在冒泡过程中执行处理函数
IE事件模型(基本不用
IE事件模型共有两个过程:
document
, 依次检查经过的节点是否绑定了事件监听函数,如果有则执行