javascript高级
this指向与严格模式
1.this指向问题
情况一
function创建对象时,this指向window
情况二
字面量定义成对象里的函数,this指向object(调用对象)
1
2
3
4
5
6var o = {
fn:function () {
console.log(this)
}
}
o.fn() // 控制台输出Object{对象内容}情况三
绑定事件方法中的this 指向的是绑定事件的元素(对象)
1
2
3
4
5
6// btn按钮点击时
var btn = document.querySelector('button')
btn.addEventListener('click',function(){
console.log(this)
// 控制台输出btn(按钮元素对象)
})情况四
定时器中的this 指向window
1
2
3
4var timeId = window.setInterval(function(){
console.log('定时器里的this',this)
},3000)
// 控制台输出: '定时器里的this' window情况五
立即执行函数中的this,指向window
1
2
3
4(function (){
console.log('立即执行函数中的this',this);
})();
// 控制台输出window情况六
构造函数中的this 指向构造函数创建的实例(对象)
1
2
3
4
5function star (uname){
this.uname = uname
console.log(this)
}
// 控制台输出star{对象内容}
总结:
1 | this:是一个对象,他的值是不确定的,根据它的调用者来决定,谁调用的,this就指向谁 |
函数内部的this指向
调用方式 | this指向 |
---|---|
普通函数调用 | window |
构造函数调用 | 实例对象 原型对象里面的方法也指向实例对象 |
对象方法调用 | 该方法属于对象 |
事件绑定方法 | 绑定事件对象 |
定时器函数 | window |
立即执行函数 | window |
更改this指向的三个方法
call() 、 apply() 、 bind()
1.call()
可更改this指向,并立刻执行函数
语法:
1 | 函数名.call(参数0, 参数1,参数2...,参数n) |
例:
1 | // 定义一个父函数是一个构造函数 |
2.apply()
可更改this指向,并立刻执行函数
语法:
1 | 函数名.apply(对象,[参数1,参数2,...参数n]) |
注意:与call不同的是调用函数传递的实参以一个数组存放,里边每一项对应调用函数时传递的实参。
3.bind()
可更改this指向,不会 执行函数,会 返回一个更改this和传递实参的 新的函数
语法:
1 | 函数名.bind(对象,参数1,参数2.....参数n) // 对象可以是所有js里的东西 |
特点:
1.不会调用函数
2.会返回一个新的函数体
call、apply、bind三者的异同
共同点:都可以改变this指向。
不同点:
call和apply 会调用调用函数,并且改变函数内部this指向。
call和apply传递的参数不同一样,call传递参数使用逗号隔开,apply使用数组传递参数
bind 不用调用函数,可以改变函数内部this指向。
应用场景
1.call经常做继承。
2.apply经常跟数组有关系,比如借助数学对象实现数组最大值最小值。
3.bind 不调用函数,但是还想改变this指向,比如改变定时器内部的this指向。
删除变量(删除属性)
定义了的变量或设置了的属性可以通过delete删除变量或属性
1 | delete 变量名 |
严格模式
JavaScript除了提供正常模式外,还提供了严格模式(strict mode)。ES5的严格模式是采用具有限制性JavaScript变体的一种方式,即在严格的条件下运行JS代码。
严格模式在IE10以上版本的浏览器中才会被支持,旧版本浏览器中会被忽略。
严格模式对正常的JavaScript语义做了一些更改:
- 消除了Javascript语法的一些不合理、不严谨之处,减少了一些怪异行为。
- 消除代码运行的一些不安全之处,保证代码运行的安全。
- 提高编译器效率,增加运行速度。
- 禁用了在ECMAScript的未来版本中可能会定义的一些语法,为未来新版本的Javascript做好铺垫。比如一些保留字如:class, enum,export, extends, import, super不能做变量名
开启严格模式 “use strict”
严格模式可以应用到整个脚本或者个别函数中,因此在使用时,我们可以将严格模式分为脚本开启严格模式,和为函数开启严格模式两种情况。
情况一:为脚本开启严格模式
1 | (function(){ |
情况二:为函数开启严格模式
要给某个函数开启严格模式,需要把“use strict”; (或 ‘use strict’; ) 声明放在函数体所有语句之前。
1 | function fn(){ |
严格模式对javascript的语法和行为,都做了一些改变
1 | // 1.严格模式后使用未声明的变量 |
闭包与递归
闭包:
提前声明:变量的作用域
变量根据作用域的不用分为两种:全局变量和局部变量
- 函数内部可以使用全局变量。
- 函数外部不可以使用局部变量。
- 当函数执行完毕,本作用域内的局部变量会销毁。
闭包(closure)指有权访问另一个函数作用域中变量的函数。简单理解就是,一个作用域可以访问另一个函数内部的局部变量(准确来说应该是嵌套关系的函数)
作用:延伸变量的作用范围。
1 | function fn(){ |
递归:
函数里边调用自己,实现循环,这就叫递归(我调我自己)
递归:如果一个函数在内部可以调用其本身,那么这个函数就是递归函数。简单理解:函数内部自己调用自己,这个函数就是递归函数。
注意:递归函数的作用和循环效果一样,由于递归很容易发生”栈溢出“错误(stack overflow),所以必须要加退出条件return。
1 | function fn (){ |
利用递归循环求阶乘
1 | function fn(n){ |
利用递归求斐波那契数列对应的数
(斐波那契数列[兔子序列]:斐波那契数列(Fibonacci sequence),又称黄金分割数列,因数学家莱昂纳多·斐波那契(Leonardoda Fibonacci)以兔子繁殖为例子而引入,故又称为“兔子数列”,指的是这样一个数列:0、1、1、2、3、5、8、13、21、34、……在数学上,斐波那契数列以如下被以递推的方法定义:F(0)=0,F(1)=1, F(n)=F(n - 1)+F(n - 2)(n ≥ 2,n ∈ N*)在现代物理、准晶体结构、化学等领域,斐波纳契数列都有直接的应用,为此,美国数学会从 1963 年起出版了以《斐波纳契数列季刊》为名的一份数学杂志,用于专门刊载这方面的研究成果。)
1 | // 斐波那契(兔子序列): 1 1 2 3 5 8 13 21 .... n |
构造函数 和 原形
prototype原型对象
js规定,每个构造函数都有一个prototype属性,指向另一个对象。prototype是一个对象,原型对象,此对象的所有属性和方法,都会被构造函数所拥有。
我们可以把那些不变的方法,直接定义在prototype对象上,这样所有对象的实例就可以共享这些方法了。
1 | function Star(uname,age){ |
对象原型 __proto__
所有 对象都会有一个属性 __proto__
指向构造函数的prototype原型对象,之所以我们对象可以使用构造函数prototype原型对象的属性和方法,就是因为对象有__proto__
原型的存在。
__proto__
对象原型和原形对象prototype是等价的
__proto__
对象原型殆意义就在于为对象的查找机制提供一个方向,或者说一条路线,但是它是一个非标准属性,因此实际开发中,不可以使用这个属性,它只是内部指向原型对象prototype
constructor构造函数(指针)
对象原型(__proto__
)和构造函数原型对象(parototype)里面都有一个属性constructor属性,constructor我们称为构造函数,因为它指回构造函数本身。
作用:
constructor主要用于记录该对象引用于哪个构造函数,它可以让原型对象重新指回原来的构造函数。
解析:
一般情况下,对象的方法都在构造函数的原型对象(prototype)中设置。如果有多个对象的方法,我们可以给原型对象(prototype)采取 对象形式 赋值,但是这样就会覆盖构造函数原型对象原来的内容,这样修改后的原型对象的constructor属性就不再指向当前构造函数了。此时,我们可以在修改后的原型对象中,添加一个(定义一个)constructor属性,指回原来的构造函数。
1 | function Star(unmae,age){ |
原型链:
通过构造函数创建出来的实例对象可以通过原型链找到创建本身的对象
每一个实例对象又有一个__proto__
属性,指向的构造函数的原型对象,构造函数的原型对象也是一个对象,也有__proto__
属性,这样一层一层往上照就形成了原型链。
面向对象扩展
扩展数组内置方法
1 | 给内置构造函数Array的原型对象添加方法,只要是数组,都可以用你新增的方法 |
构造函数继承(call)
复用另一个函数的属性给子类
1 | // 一个构造函数 |
核心:一个函数里通过call或者apply,调用另一个函数,并更改this指向自己,实现将另一个函数的属性复用。
Object.defineProperty——ES5新增对象方法
Object.defineProperty设置或修改对象中的属性
1 | Object.defineProperty(对象,修改或新增的属性名[string],{ |
用途:用于设置修改对象的属性值,锁定对象属性值,设置是否允许遍历,是否允许删除,以及在设置在修改时可执行一些代码,在访问时可执行一些代码。
Object.defineProperties 整个对象的权限设置与监听
1 | Object.defineProperties(obj, props) |
obj [Object]
- 在其上定义或修改属性的对象。
props [Object]
- 要定义其可枚举属性或修改的属性描述符的对象(写法同上defineProperty)。
Proxy(vue3实现原理核心)
语法:
1 | const p = new Proxy(target, handler) |
Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)
对象与类
类
在ES6中新增了类的概念,可以 使用class关键字声明一个类 ,之后以这个类来实例化对象。
类抽象了对象的公共部分,它泛指某一大类(class)对象特指某一个,通过类实例化一个具体的对象(类似于构造函数创建对象,底层依旧是ES5的构造函数创建对象)
创建类
1 | // 步骤1 使用class关键字创建一个类 |
类的内容
1 | class Star{ |
类的继承
正则表达式
正则表达式(Regular Expression)是用于匹配字符串中字符组合的模式。在JavaScript中,正则表达式也是对象。
正则表通常被用来检索、替换那些符合某个模式(规则)的文本,例如验证表单:用户名
(需要规定要正则表达式的内容,才能调用正则表达式方法test去测试内容是否符合)
1 | new RegExp(/abc/) // 正则表达式就是一个RegExp方法 |
test方法验证
1 | var rg = /123/ |
特殊字符
1.边界符(正则表达式里面不需要加引号 不管是数字型还是字符串型)
边界符 | 说明 |
---|---|
^ | 表示匹配行首的文本(以谁开始) |
$ | 表示匹配行尾的文本(以谁结束) |
字符集[]方括号
表示有一系列字符可供选择,只要匹配其中一个就可以了。
1 | 只要包含a或者b或者c都可以返回 true |
量词符
量词符永来设定某个模式出现的次数。
量词 | 说明 |
---|---|
* | 重复0次或更多次 |
+ | 重复1次或更多次 |
? | 重复0次或1次 |
{n} | 重复n次 |
{n,} | 重复n次或更多次 |
{n,m} | 重复n到m次 |
常用量
1 | 电话号码判断: |
括号总结
预定义类指的是某些常见模式的简写方式。
预定类 | 说明 |
---|---|
\d | 匹配0-9之间的任意数字,相当于[0-9] |
\D | 匹配所有0-9以外的字符,相当于[^0-9] |
\w | 匹配任意的字母、数字和下划线,相当于[A-Za-z0-9] |
\W | 除所有字母、数字和下划线以外的字符,相当于[^A-Z] |
\s | 匹配空格(包括换行符、制表符、空格符等),相当于[\t\r\n\v\f] |
\S | 匹配非空格的字符,相当于[^\t\r\n\v\f] |
量词符
用来设定某个模式出现的次数
1 | // 1.* 相当于 >=0 可以出现0次或者很多次 |
正则替换replace
实现替换字符串操作,用来替换的参数可以是一个字符串或是一个正则表达式。
语法 :str.replace(替换字符,要被替换的字符)
1 | var str = 'andy和red'; |
ES6新增语法
let关键字变量声明
1 . let声明的变量只在所处块级域有效
注意:使用let关键字声明的变量才具有块级作用域,使用var声明的变量不具备块级作用域特性。
2 . 不存在变量提升
3 . 暂时性死区
利用let声明的变量会绑定在这个块级作用域,不会受外界的影响
小结
- let关键字就是用来声明变量的
- 使用let关键字声明的变量具有块级作用域
- 在一个大括号 使用let关键字声明的变量才具有块级作用域var关键字是不具备这个特点的
- 防止循环变量变成全局变量
- 使用let关键字声明的变量没有变量提升
- 使用let关键字声明的变量具有暂时性死区特性
const常量声明关键字
用于声明常量,常量的值(内存地址)不能变化的量
具有块级作用域
1 | if(true){ |
声明常量必须赋值
1 | const p; // wissing initializer in const decleration |
常量赋值后,值不能修改
但对象和数组可以修改内部的值,而不能重新定义新对象数组
小结:
- const声明的变量是一个常量
- 既然是常量就不能重新赋值,如果是基本数据类型,不能更改值,如果是复杂数据类型,不能更改地址。
- 声明const时必须给定值
let、const、var的区别
- 使用var声明的变量,其作用域为该语句所在的函数内,且存在变量提升现象。
- 使用let声明的变量,其作用域为该语句所在的代码块呢,不存在变量提升。
- 使用const声明的是常量,在后面出现的代码中不能再修改该常量的值。
var | let | const |
---|---|---|
函数级作用域function(){ } | 块级作用域{ } | 块级作用域{ } |
变量提升 | 不存在变量提升 | 不存在变量提升 |
值可更改 | 值可更改 | 值不可更改 |
解构赋值
ES6中允许从数组中提取值,按照对应位置,对变量赋值,对象也可以实现解构
理解:结构赋值就是把数组结构分解,然后给变量进行赋值
1 . 数组解构
1 | var arr = [1,2,3,4] |
2 . 对象解构
1 | var obj = {uname:'小明',age:20} |
多层结构
1 | var obj = {name:'张三',age:20,stu:{love:"coding"}}; |
小结:
- 结构赋值就是把数组结构分解,然后给变量进行赋值
- 如果解构不成功,变量跟数组个数不匹配你的时候,变量的值为undefined
- 数组解构用中括号包裹,多个变量用逗号隔开,对象解构用花括号包裹,多个变量用逗号隔开
- 利用结构赋值能狗让我们方便的去取对象中的属性跟方法
箭头函数()=>{}
ES6中新增的定义函数的方式
1 | // 语法格式(函数名add) |
箭头函数中没有arguments(实参),但可以使用 展开运算符 来访问所有的实参
1
2
3
4
5 // ...后面写接收参数的数组名
var addfn = (...args)=>{
console.log(args) // 控制台打印[1,2,3,4,5]
}
addfn(1,2,3,4,5)
注意:
1.箭头函数没有自己的this,箭头函数中的this是函数外层作用域里this (或者理解为this指向的是函数定义位置的上下文中this)
2.箭头函数没有 实参伪数组(arguments)
优点:解决了匿名函数this指向的问题(匿名函数的执行环境具有全局性),包括定时器(setTimeout和setInterval)中使用的this所造成的问题。
剩余参数
剩余参数语法允许我们将一个不定数量的参数表示为一个数组,不定参数数量多定义方式,这种方式很方便的去声明不知道参数情况下的一个函数
1 | function sum(first,...args){ |
剩余参数和解构配合使用
1 | let students = ['小明','小红','小蓝'] |
注意:…只能用于解构赋值最后一个变量,不然会报错。
…剩余运算符(展开运算符)
一般用于箭头函数将伪数组转数组,拿到传递的所有实参。
1 | var fn = (...args) => { |
…扩展运算符
可将数组展开,可将伪数组转换成数组
1 | var arr = [10,20,30,40] |
…[‘数’,’组’,’解’,’构’] ==>. (‘数’,’组’,’解’,’构’)
…字符串解构 ==> 字 符 串 解 构
注意⚠️:将数组或字符串框架拆碎,元素掉落
Array.from方法
可以将伪数组转成数组(让其可以使用数组的方法),还能进行遍历
1 | // 语法: |
作用 : 用于将伪数组转为正常数组
语法:Array.from(伪数组) 伪数组可以是变量存的伪数组
1 | function fn1(){ |
find数组查找方法
作用:查找符合条件的数据(找到第一个就停止查找),返回值为查找到的数据
语法:
1 | var arr = [2,3,5,6] |
findIndex数组下标查找方法
作用 : 查找满足条件的数据所在数组的下标
1 | var arr = [ |
includes方法(字符串数组适用)
用于严格查找内容和检验(内容校验用的是===)
返回值:布尔值
1 | 语法: |
startsWith方法和endsWith方法
判断字符串是否以什么开始,以什么结束
1 | 语法: |
repeat重复方法
将字符串重复指定n次,返回一个新字符串
1 | 语法: |
Set数组去重——ES6新增数据结构
ES6提供了新的数组解构Set,它类似于数组,但是成员的值都是唯一的,没有重复的值。
Set本身是一个构造函数,用来生成Set数据结构
作用:将数组里相同的项去除,只留一项
1 | 语法: |
注意:此方法不支持去重复杂数据类型(例如:对象)
Set的内置实例方法
- add(value):添加某个值,返回Set结构本身。
- delete(value):删除某个值,返回一个布尔值,表示删除是否成功。
- has(value):返回一个布尔值,表示该值是否为Set的成员(是否有该值)。
- clear():清除所有成员,没有返回值。
1 | const s = new Set(); |
遍历set结构对象
Set结构的实例与数组一样,也拥有forEach方法,用于对每个成员执行某种操作,没有返回值。
1 | s.forEach(function(item,index,arr){ |
Object.keys()
解决问题:如何拿到对象中所有的键(key)?
用于将对象的键名组成一个数组
语法:
1 | Object.keys(js对象) |
示例:
1 | let obj = {name:"张三",age:20} |
Object.values()
解决问题:如何拿到对象中所有的值?
语法:
1 | Object.values(js对象) |
示例:
1 | let obj = {name:"张三",age:20} |
Object.entries()
解决问题:
ES6键值对重名简化写法
ES6新语法中,变量名和需要定义的对象属性名一样的情况下,可以直接写属性名不用写值一样可以设置
1 | // 定义变量 |
对象拷贝
浅拷贝
定义:将引用类型的 引用(指针this) 拷贝给新对象(对于引用数据类型)
注意:对于复杂数据类型(Object对象,Array数组)由于拷贝的是同一个指针,指向同一块堆内存中同一块空间,所以修改数据时,会相互影响。而对于基本数据类型(数字,字符串,布尔值,null,undefined)
1 | // 声明一个变量并赋值 |
深拷贝
定义:将引用类型各项属性的 值 拷贝给新对象(对象拼接)
方式一:手抄复制
方式二:Object.assign()
Object.assign()对象扩充方法
Object.assign(参数1,参数2,参数3,参数…)
参数1 : 要扩充的对象
参数2及之后的参数 : 扩充的对象
1
2
3
4
5
6 var obj = {};
var o1 = {name:'小明'};
var o2 = {age:20};
Object.assign(obj,o1,o2)
console.log(obj)
// {name:'小明',age:20}
1 | Object.assign() 只能实现一层的深拷贝 |
方式三:JSON内置对象方法
通过js的内置对象JSON来进行数组对象的深拷贝(JSON的内置方法:stringify,parse),一般用于没有函数的数据。
1 | var obj = { |
注意:通过内置对象JSON的方法来进行数据对象的拷贝,对象里的函数会丢失。
方式四:递归拷贝(手动复制高级版)
1 | 数据类型(对象) |
异步编程
Promise基础
含义
Promise中文意思是承诺,也就是说,js中对你许下一个承诺,会在未来某个时刻对象承诺。
状态
一个Promise对象值是未知的,状态是可变的,但是无论怎么变化,它的状态永远处于以下三种之间:
- 进行中(pending)
- 已完成(fulfilled)
- 拒绝(rejected)
Promise的状态会发生变化
- 成功时回从pending(待定) -> fulfilled(解决)
- 失败时会从pending(待定) -> rejectd(失败)
- 但是此过程时不可逆的,不能从另外两种状态变成pending(待定),fulfilled/rejected两个状态也被称为settled(解决)状态
Promise 意义
promise的出现是为了解决ES6之前JS代码中频繁潜逃回调函数所导致的回调地狱问题,Promise为ES6特性。
什么是回调地狱:
在JS中我们只使用 typeof 区分基本数据类型
判断是否数组
1 | let arg = []; |
该资料仅供借鉴学习,笔记整理能梳理知识点、能加深印象,请各位同学自己整理。