javascript高级

this指向与严格模式

1.this指向问题

  1. 情况一
    function创建对象时,this指向window
  2. 情况二
    字面量定义成对象里的函数,this指向object(调用对象)
var o = {
        fn:function () {
          console.log(this)
        }
      }
o.fn() // 控制台输出Object{对象内容}
  1. 情况三
    绑定事件方法中的this 指向的是绑定事件的元素(对象)
// btn按钮点击时
var btn = document.querySelector('button')
      btn.addEventListener('click',function(){
        console.log(this)
   // 控制台输出btn(按钮元素对象)
})
  1. 情况四
    定时器中的this 指向window
var timeId = window.setInterval(function(){
console.log('定时器里的this',this)
},3000)
// 控制台输出: '定时器里的this' window
  1. 情况五
    立即执行函数中的this,指向window
(function (){
  console.log('立即执行函数中的this',this);
})();
// 控制台输出window
  1. 情况六
    构造函数中的this 指向构造函数创建的实例(对象)
function star (uname){
  this.uname = uname
  console.log(this)
}
// 控制台输出star{对象内容}

总结:

this:是一个对象,他的值是不确定的,根据它的调用者来决定,谁调用的,this就指向谁
大白话:谁点出来的,this就指向谁

函数内部的this指向

调用方式this指向
普通函数调用window
构造函数调用实例对象 原型对象里面的方法也指向实例对象
对象方法调用该方法属于对象
事件绑定方法绑定事件对象
定时器函数window
立即执行函数window

更改this指向的三个方法

call() 、 apply() 、 bind()

1.call()

可更改this指向,并立刻执行函数

语法:

函数名.call(参数0, 参数1,参数2...,参数n)
参数0为更该this指向的对象,参数1...参数n是传递的对应的参数

例:

// 定义一个父函数是一个构造函数
function Father(uname,age){
        this.uname = uname
        this.age = age
        console.log(this)
      }
// 定义一个子函数,通过调父函数创建一个实例对象
function Son (uname,age){
        Father.call(this,uname,age)
  // 调用父函数,利用call更改this指向Son函数,传递形参,生成实例对象
      }
// 打印new 创建的实例对象
console.log(new Son('某某',18))
// 控制台Son{uname:'某某',age:18}

2.apply()

可更改this指向,并立刻执行函数

语法:

函数名.apply(对象,[参数1,参数2,...参数n])
// 参数1为要更改的this指向对象,参数2为一个数组,存放调用函数传递的实参

注意:与call不同的是调用函数传递的实参以一个数组存放,里边每一项对应调用函数时传递的实参。

3.bind()

可更改this指向,不会 执行函数, 返回一个更改this和传递实参的 新的函数

语法:

函数名.bind(对象,参数1,参数2.....参数n) // 对象可以是所有js里的东西
// 第一个参数为要更改this指向的对象,第二个以后的参数对应调用函数的实参

特点:
1.不会调用函数
2.会返回一个新的函数体

call、apply、bind三者的异同

  • 共同点:都可以改变this指向。
  • 不同点:
    call和apply 会调用调用函数,并且改变函数内部this指向。
    call和apply传递的参数不同一样,call传递参数使用逗号隔开,apply使用数组传递参数
    bind 不用调用函数,可以改变函数内部this指向。
  • 应用场景
    1.call经常做继承。
    2.apply经常跟数组有关系,比如借助数学对象实现数组最大值最小值。
    3.bind 不调用函数,但是还想改变this指向,比如改变定时器内部的this指向。

删除变量(删除属性)

定义了的变量或设置了的属性可以通过delete删除变量或属性

delete 变量名
// 定义了的变量可以通过delete删除变量或属性
var a = {name:'张三',age:18}
delete a.age
console.log(a) // 控制台输出a{name:'张三'}

严格模式

JavaScript除了提供正常模式外,还提供了严格模式(strict mode)。ES5的严格模式是采用具有限制性JavaScript变体的一种方式,即在严格的条件下运行iS代码。
严格模式在IE10以上版本的浏览器中才会被支持,旧版本浏览器中会被忽略。
严格模式对正常的JavaScript语义做了一些更改:

  1. 消除了Javascript语法的一些不合理、不严谨之处,减少了一些怪异行为。
  2. 消除代码运行的一些不安全之处,保证代码运行的安全。
  3. 提高编译器效率,增加运行速度。
  4. 禁用了在ECMAScript的未来版本中可能会定义的一些语法,为未来新版本的Javascript做好铺垫。比如一些保留字如:class, enum,export, extends, import, super不能做变量名

开启严格模式 “use strict”

严格模式可以应用到整个脚本或者个别函数中,因此在使用时,我们可以将严格模式分为脚本开启严格模式,和为函数开启严格模式两种情况。

情况一:为脚本开启严格模式

(function(){
  // 在当前这个自调函数中有开启严格模式,当前函数之外还是普通模式
  "use strict"
  var num = 10;
  function (){}
})
// 或者
<script>
  "use strict";  // 当前script标签开启了严格模式
</script>

情况二:为函数开启严格模式

要给某个函数开启严格模式,需要把“use strict”; (或 ‘use strict’; ) 声明放在函数体所有语句之前。

function fn(){
  "use strict"
  return '123'
}

严格模式对javascript的语法和行为,都做了一些改变

// 1.严格模式后使用未声明的变量
'use strict'
num = 10
console.log(num) // 控制台输出num未定义
--------------------------------------------------------------------
// 2.严格模式不允许删除变量
var num2 = 2;
delete num2 //控制台报错:严格模式下删除非限定标识符
--------------------------------------------------------------------
// 3.严格模式下全局作用域中函数中的this是undefined
function fn (){
  console.log(this)
}
--------------------------------------------------------------------
// 4.严格模式下,如果构造函数不加new调用,this指向的是undefined如果给他赋值则会报错。
function Star(){
  this.sex = '男'
}
var ldh = new Star() // 如果使用new调用函数,直接调用,this指向实例对象
console.log(ldh.sex)
--------------------------------------------------------------------
// 5.严格模式下,定时器this还是指向window
setTimeout(function(){
  console.log(this) // 控制台输出window
},2000)

闭包与递归

闭包:

提前声明:变量的作用域

变量根据作用域的不用分为两种:全局变量和局部变量

  1. 函数内部可以使用全局变量。
  2. 函数外部不可以使用局部变量。
  3. 当函数执行完毕,本作用域内的局部变量会销毁。

闭包(closure)指有权访问另一个函数作用域中变量的函数。简单理解就是,一个作用域可以访问另一个函数内部的局部变量(准确来说应该是嵌套关系的函数)

作用:延伸变量的作用范围。

function fn(){
  var num = 10;
  function fun(){
    console.log(num);
  }
  return fun
}
var f = fn()
f()  // 控制台输出100

递归:

函数里边调用自己,实现循环,这就叫递归(我调我自己)

递归:如果一个函数在内部可以调用其本身,那么这个函数就是递归函数。简单理解:函数内部自己调用自己,这个函数就是递归函数。

注意:递归函数的作用和循环效果一样,由于递归很容易发生”栈溢出“错误(stack overflow),所以必须要加退出条件return。

function fn (){
  fn()
}
// 递归

利用递归循环求阶乘

function fn(n){
        if(n == 1){
          return 1
        }
        return n * fn(n-1)
      }
      console.log(fn(5)) // 120

利用递归求斐波那契数列对应的数

(斐波那契数列[兔子序列]:斐波那契数列(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 2 3 5 8 13 21 .... n
// 兔子序列:后一个数是前两个数的和
function fn (n) {
    // 退出的条件 n == 1 
    if (n == 1 || n == 2) {
        return 1;
    }
    return fn(n - 1) + fn(n - 2)
}
console.log(fn(8));

构造函数 和 原形

prototype原型对象

js规定,每个构造函数都有一个prototype属性,指向另一个对象。prototype是一个对象,原型对象,此对象的所有属性和方法,都会被构造函数所拥有。

我们可以把那些不变的方法,直接定义在prototype对象上,这样所有对象的实例就可以共享这些方法了。

function Star(uname,age){
        this.nname = uname;
        this.age = age
}

// 将方法挂载到
Star.prototype.sing = function(){
  console.log('会唱歌')
}
var ldh = new Star('刘德华',30)
var zxy = new Star('张学友',32)
ldh.sing(); // 会唱歌
zxy.sing(); // 会唱歌

console.log(ldh.sing == zxy.sing) // true
// 两者都是一样的

对象原型 __proto__

所有 对象都会有一个属性 __proto__指向构造函数的prototype原型对象,之所以我们对象可以使用构造函数prototype原型对象的属性和方法,就是因为对象有__proto__原型的存在。

__proto__对象原型和原形对象prototype是等价的

__proto__对象原型殆意义就在于为对象的查找机制提供一个方向,或者说一条路线,但是它是一个非标准属性,因此实际开发中,不可以使用这个属性,它只是内部指向原型对象prototype

constructor构造函数(指针)

对象原型(__proto__)和构造函数原型对象(parototype)里面都有一个属性constructor属性,constructor我们称为构造函数,因为它指回构造函数本身。

作用:

constructor主要用于记录该对象引用于哪个构造函数,它可以让原型对象重新指回原来的构造函数。

解析:

一般情况下,对象的方法都在构造函数的原型对象(prototype)中设置。如果有多个对象的方法,我们可以给原型对象(prototype)采取 对象形式 赋值,但是这样就会覆盖构造函数原型对象原来的内容,这样修改后的原型对象的constructor属性就不再指向当前构造函数了。此时,我们可以在修改后的原型对象中,添加一个(定义一个)constructor属性,指回原来的构造函数。

function Star(unmae,age){
  this.uname = uname;
  this.age = age;
}
// 很多情况下我们需要手动的利用constructor这个属性指回 原来的构造函数
Star.prototype = {
  constructor:Star, //手动设置指回原来的构造函数
  sing:function(){
    console.log('会唱歌');
  },
  movie:function(){
    console.log('会演电影');
  }
  
}

var zxy = new Star('张学友',30);
console.log(zxy)

原型链:

通过构造函数创建出来的实例对象可以通过原型链找到创建本身的对象

每一个实例对象又有一个__proto__属性,指向的构造函数的原型对象,构造函数的原型对象也是一个对象,也有__proto__属性,这样一层一层往上照就形成了原型链。

面向对象扩展

扩展数组内置方法

给内置构造函数Array的原型对象添加方法,只要是数组,都可以用你新增的方法
Array.prototype.sum = function(){
  console.log(this);
  let sum = 0;
  for(let i = 0;i < this.length ; ++i){
    sum += this[i];                                                                                         
  }
  return sum;
}

var arr1 = new Array(20,10,20,40)
console.log(arr1)

构造函数继承(call)

复用另一个函数的属性给子类

// 一个构造函数
function fn1(uname,age){
  this.uname = uname;
  this.age = age
}
// 新的函数
function fn2(uname,age){
  // 通过call调用fn1构造函数,更改this指向自己,将创建实例的函数指向自己
  fn1.call(this,uname,age)
}
// 通过fn2函数创建对象
var a = new fn2('小明',19)

核心:一个函数里通过call或者apply,调用另一个函数,并更改this指向自己,实现将另一个函数的属性复用。

Object.defineProperty——ES5新增对象方法

Object.defineProperty设置或修改对象中的属性

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty

Object.defineProperty(对象,修改或新增的属性名[string],{
  
  value:'张三',
  // 修改或设置的值
  
  writable: true/false,
  // 如果值为false 不允许修改这个属性值
  
  enumerable: true/false,
  // enumerable 如果值为false 则不允许遍历
  
  configurable: true/false,
  // configurable 如果为false 则不允许删除这个属性  属性是否可以被删除或者是否可以再次修改特性.
  
  set:function(newVal,oldVal){ // 参数1为新设置的值,参数2为修改前的值
    // 这里是设置属性值时触发的代码
    console.log('属性值被设置为',value)
  },
  
  get:function(val){ // 参数为此对象的值
    // 这里是访问属性时触发的代码
    console.log('属性值被访问或被使用')
  }
})


用途:用于设置修改对象的属性值,锁定对象属性值,设置是否允许遍历,是否允许删除,以及在设置在修改时可执行一些代码,在访问时可执行一些代码。

Object.defineProperties 整个对象的权限设置与监听

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperties

Object.defineProperties(obj, props)

obj [Object]

  • 在其上定义或修改属性的对象。

props [Object]

  • 要定义其可枚举属性或修改的属性描述符的对象(写法同上defineProperty)。

Proxy(vue3实现原理核心)

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy

语法:

const p = new Proxy(target, handler)

Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)

对象与类

在ES6中新增了类的概念,可以 使用class关键字声明一个类 ,之后以这个类来实例化对象。

类抽象了对象的公共部分,它泛指某一大类(class)对象特指某一个,通过类实例化一个具体的对象(类似于构造函数创建对象,底层依旧是ES5的构造函数创建对象)

创建类

// 步骤1  使用class关键字创建一个类
class Star{
  // 类的内容
}
// 步骤2使用定义的类创建实例  注意new关键字
var mm = new Star();

类的内容

class Star{
  // 公共成员
  name
  // 静态成员
  static sex = "男"
  // 私有成员
  #love
  // 类的
  constructor(name,age){
    // 给创建的实例对象挂载属性
    this.name = name;
    this.age = age;
    
    static age = 20
  }
}

类的继承

正则表达式

正则表达式(Regular Expression)是用于匹配字符串中字符组合的模式。在JavaScript中,正则表达式也是对象。

正则表通常被用来检索、替换那些符合某个模式(规则)的文本,例如验证表单:用户名

(需要规定要正则表达式的内容,才能调用正则表达式方法test去测试内容是否符合)

new RegExp(/abc/) // 正则表达式就是一个RegExp方法
// 1.利用RegExp对象来创建 正则表达式
var reg = new RegExp(/abc/)
// 2.利用字面量创建正则表达式
var rg = /123/

test方法验证

var rg = /123/
console.log(rg.test(123)) // 匹配字符中是否出现123  出现结果为true
console.log(rg.test('abc'))  // 匹配字符中是否出现123  未出现结果为false

特殊字符

1.边界符(正则表达式里面不需要加引号 不管是数字型还是字符串型)

边界符说明
^表示匹配行首的文本(以谁开始)
$表示匹配行尾的文本(以谁结束)

字符集[]方括号

表示有一系列字符可供选择,只要匹配其中一个就可以了。

只要包含a或者b或者c都可以返回 true
var rg = /[abc]/
三选一  只有以a 或者是b 或者是c 这三个字母开头或结束才会 返回 true
var rg = /^[abc]$/

0-9的数字
var reg = /[0-9]/
26个英文字母(大写和小写都可以)
var reg = /[a-zA-Z]/;
字符组合
var reg1 = /[a-zA-Z0-9]/

字符 取反 ,除了字母和数字其他的东西
var reg1 = /[^a-zA-Z0-9]/

量词符

量词符永来设定某个模式出现的次数。

量词说明
*重复0次或更多次
+重复1次或更多次
重复0次或1次
{n}重复n次
{n,}重复n次或更多次
{n,m}重复n到m次

常用量

电话号码判断:
验证 000-12345678 的电话号码格式
var reg = /^[0-9]{3}-[0-9]{8}$/
验证 010-123456780772-12345678 的电话号码格式
var reg1 = /^[0-9]{3}-[0-9]{8}$|^[0-9]{4}-[0-9]{7}$/
密码验证
var reg2 = /^[a-zA-Z0-9_-]{6,16}$/

括号总结

预定义类指的是某些常见模式的简写方式。

预定类说明
\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.* 相当于 >=0 可以出现0次或者很多次
var reg = /a*/
console.log(reg.test('b')) // true
// 2.+ 相当于 >=1 可以出现一次或者多次
var reg = //
// 3.? 相当于 长字符中 可以出现1次或者很多次
var reg = /ab?/;
console.log(reg.test('ddddssaddb')) // true
console.log(reg.test('cccctttt')) // false
// 4.{3} 就是只能重复3次
var reg = /d{3}/
console.log(reg.test('ddd')) // true
// 5.{3,6} 就是大于3次小于6次
var reg = /c{3,6}/
console.log(reg.test('aeacccccc')) // true

正则替换replace

实现替换字符串操作,用来替换的参数可以是一个字符串或是一个正则表达式。

语法 :str.replace(替换字符,要被替换的字符)

var str = 'andy和red';
var newStr = str.replace('andy','baby')
console.log(newStar)   // "baby和red"

// 等同于  此处的andy可以写在正则表达式内
var newStr2 = str.replace(/andy/,'baby');
console.log(newStr2)   // "baby和red"

// 替换第一个
var str = 'abcabc'
var nStr = str.replace(/a/,'哈哈')
console.log(nStr)   // "哈哈bcabc"
// 全部替换g (正则表达式后加字母g)
var nStr2 = str.replace(/a/g,'哈哈')
console.log(nStr)   // "哈哈bc哈哈bc"

// 忽略大小写i (正则表达式后加字母i)
var str = 'aAbcaAbc'
var nStr3 = str.replace(/a/gi,'哈哈')
console.log(nStr3)   // "哈哈哈哈bc哈哈哈哈bc"

ES6新增语法

let关键字变量声明

1 . let声明的变量只在所处块级域有效

注意:使用let关键字声明的变量才具有块级作用域,使用var声明的变量不具备块级作用域特性。

2 . 不存在变量提升

3 . 暂时性死区

利用let声明的变量会绑定在这个块级作用域,不会受外界的影响

小结

  • let关键字就是用来声明变量的
  • 使用let关键字声明的变量具有块级作用域
  • 在一个大括号 使用let关键字声明的变量才具有块级作用域var关键字是不具备这个特点的
  • 防止循环变量变成全局变量
  • 使用let关键字声明的变量没有变量提升
  • 使用let关键字声明的变量具有暂时性死区特性

const常量声明关键字

用于声明常量,常量的值(内存地址)不能变化的量

具有块级作用域

if(true){
   const a = 10
   }
console.log(a) // a is not defined

声明常量必须赋值

const p;  // wissing initializer in const decleration

常量赋值后,值不能修改

但对象和数组可以修改内部的值,而不能重新定义新对象数组

小结:

  • const声明的变量是一个常量
  • 既然是常量就不能重新赋值,如果是基本数据类型,不能更改值,如果是复杂数据类型,不能更改地址。
  • 声明const时必须给定值

let、const、var的区别

  • 使用var声明的变量,其作用域为该语句所在的函数内,且存在变量提升现象。
  • 使用let声明的变量,其作用域为该语句所在的代码块呢,不存在变量提升。
  • 使用const声明的是常量,在后面出现的代码中不能再修改该常量的值。
varletconst
函数级作用域function(){ }****块级作用域{ }块级作用域{ }
****变量提升不存在变量提升不存在变量提升
值可更改值可更改****值不可更改

解构赋值

ES6中允许从数组中提取值,按照对应位置,对变量赋值,对象也可以实现解构

理解:结构赋值就是把数组结构分解,然后给变量进行赋值

1 . 数组解构

var arr = [1,2,3,4]
var [a,b,c,d] = arr
console.log(a,b,c,d) // 1 2 3 4

2 . 对象解构

var obj = {uname:'小明',age:20}
var {uname,age} = obj
console.log(uname) // "小明"
console.log(age)   // 20

多层结构

var obj = {name:'张三',age:20,stu:{love:"coding"}};
var {name,age,stu:{love}} = obj;
console.log(name,age,love); // 

小结:

  • 结构赋值就是把数组结构分解,然后给变量进行赋值
  • 如果解构不成功,变量跟数组个数不匹配你的时候,变量的值为undefined
  • 数组解构用中括号包裹,多个变量用逗号隔开,对象解构用花括号包裹,多个变量用逗号隔开
  • 利用结构赋值能狗让我们方便的去取对象中的属性跟方法

箭头函数()=>{}

ES6中新增的定义函数的方式

// 语法格式(函数名add)
var add = (a,b) => {
  return a + b
}
console.log(add(3,7)) // 10
// 简写方式:如果函数体内只有一行代码,可以省略花括号,return的值写到箭头后面
sum = (a,b) => a + b
console.log(sum(1,2)) // 3
// 只有一个参数时,参数不用加小括号,return的值写到箭头后面
map(item=>item)

箭头函数中没有arguments(实参),但可以使用 展开运算符 来访问所有的实参

// ...后面写接收参数的数组名
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所造成的问题。

剩余参数

剩余参数语法允许我们将一个不定数量的参数表示为一个数组,不定参数数量多定义方式,这种方式很方便的去声明不知道参数情况下的一个函数

function sum(first,...args){
  console.log(first);  // 10
  console.log(args);   // [20,30]
}
sum(10,20,30)

剩余参数和解构配合使用

let students = ['小明','小红','小蓝']
let [s1,...s2] = students
console.log(s1)  // '小明'
console.log(s2)  // ['小红','小蓝']

注意:…只能用于解构赋值最后一个变量,不然会报错。

…剩余运算符(展开运算符)

一般用于箭头函数将伪数组转数组,拿到传递的所有实参。

var fn = (...args) => {
  console.log(args) // 控制台输出[10,20,30]
}
fn(10,20,30)

…扩展运算符

可将数组展开,可将伪数组转换成数组

var arr = [10,20,30,40]
// 语法:
...arr   // 10,20,30,40
console.log(...arr)  // 10 20 30 40 相当于下面的代码
console.log(10,20,30,40)

…[‘数’,’组’,’解’,’构’] ==>. (‘数’,’组’,’解’,’构’)

…字符串解构 ==> 字 符 串 解 构

注意⚠️:

将数组或字符串框架拆碎,元素掉落

Array.from方法

可以将伪数组转成数组(让其可以使用数组的方法),还能进行遍历

// 语法:
Array.from(伪数组)

// 参数二是一个函数,用于遍历修改原数据
Array.from(伪数组,function(item,index,arr){
  // item为数组中的每一项,index为每一项对应的下标,arr为原数组
  return item
})

作用 : 用于将伪数组转为正常数组

语法:Array.from(伪数组) 伪数组可以是变量存的伪数组

function fn1(){
  //将接收到实参伪数组转为正常数组,并返回
  return Array.from(Arguments) //Arguments函数里接收实参的伪数组
}
var d = fn1(1,2,3,4,5,6) //传入实参
console.log(d) //输出[1,2,3,4,5,6]

find数组查找方法

作用:查找符合条件的数据(找到第一个就停止查找),返回值为查找到的数据

语法:

var arr = [2,3,5,6]
var a = arr.find(function (item,index,arr){
    // item为每一项,index为下标(前面为变动数据);arr为原数组
    return item > 3 // 返回值写查找的条件,为true是返回
    })
console.log(a) //输出为5

findIndex数组下标查找方法

作用 : 查找满足条件的数据所在数组的下标

var arr = [
      {name:'张三',age:18,score:90},
      {name:'李四',age:20,score:95},
      {name:'王五',age:19,score:100},
      {name:'赵六',age:19,score:86},
      {name:'喧腾',age:18,score:100},
    ]
var f = arr.findIndex(function(item,index,arr){
  // item为每一项,index为下标(前面为变动数据);arr为原数组
  return item.score == 86
})
console.log(f) // {name:'赵六',age:19,score:86}

includes方法(字符串数组适用)

用于严格查找内容和检验(内容校验用的是===)

返回值:布尔值

语法:
  数组.includes(查找内容)
var arr = [1,2,{},[],null,NaN,30]
var sc = arr.includes(NaN)
console.log(sc)  // true

startsWith方法和endsWith方法

判断字符串是否以什么开始,以什么结束

语法:
  字符串.startsWith('数据')
  字符串.endsWith('数据')

repeat重复方法

将字符串重复指定n次,返回一个新字符串

语法:
字符串.repeat(n)

var str = '你好'
 console.log(str.repeat(4)) // 你好你好你好你好

Set数组去重——ES6新增数据结构

ES6提供了新的数组解构Set,它类似于数组,但是成员的值都是唯一的,没有重复的值。

Set本身是一个构造函数,用来生成Set数据结构

作用:将数组里相同的项去除,只留一项

语法:
  var set = new Set(数组)

  var arr = [1,2,3,4,3,3,3]
  var newArr = new Set(arr)
  console.log(newArr) // [1,2,3,4]

size:获取长度
// 通过Set去重的数组的长度属性是用size
  ocnsole.log(newArr.size)

配合展开运算符可转换成正常数组
  var r = [...newArr]

注意:此方法不支持去重复杂数据类型(例如:对象)

Set的内置实例方法

  • add(value):添加某个值,返回Set结构本身。
  • delete(value):删除某个值,返回一个布尔值,表示删除是否成功。
  • has(value):返回一个布尔值,表示该值是否为Set的成员(是否有该值)。
  • clear():清除所有成员,没有返回值。
const s = new Set();
s.add(1).add(2).add(3); // 向set结构中添加值
s.delete(2)             // 删除set结构中的2值
s.has(1)                // 表示set结构中是否有1这个值 返回布尔值
s.clear()               // 清除set结构中的所有值
// 注意:删除的是元素值,下标对应的值

遍历set结构对象

Set结构的实例与数组一样,也拥有forEach方法,用于对每个成员执行某种操作,没有返回值。

s.forEach(function(item,index,arr){
  // 打印每一项
  console.log(item)
})

Object.keys()

解决问题:如何拿到对象中所有的键(key)?

用于将对象的键名组成一个数组

语法:

Object.keys(js对象)

示例:

let obj = {name:"张三",age:20}
let keyList = Object.keys(obj)
console.log(keyList) // "name" "age"

Object.values()

解决问题:如何拿到对象中所有的值?

语法:

Object.values(js对象)

示例:

let obj = {name:"张三",age:20}
let valueList = Object.values(obj)
console.log(valueList) // "张三" 20

Object.entries()

解决问题:

ES6键值对重名简化写法

ES6新语法中,变量名和需要定义的对象属性名一样的情况下,可以直接写属性名不用写值一样可以设置

// 定义变量
let name = '小明'
let age = 20
// 字面量定义对象属性时可以省略值,它会直接查找对应变量名的值进行赋值
let obj = {
  name,
  age
}

console.log(obj)
// 控制台打印定义的对象  {name:'小明',age:20}

浅拷贝

定义:将引用类型的 引用(指针this) 拷贝给新对象(对于引用数据类型)

注意:对于复杂数据类型(Object对象,Array数组)由于拷贝的是同一个指针,指向同一块堆内存中同一块空间,所以修改数据时,会相互影响。而对于基本数据类型(数字,字符串,布尔值,null,undefined)

// 声明一个变量并赋值
var arr = [20,30,40,50] 
// 将变量的引用拷贝给新对象
var arr1 = arr 
// 修改第一份数据
arr.push(60)  

// 打印第二份数据时
console.log(arr1) //  控制台输出[20,30,40,50,60]
// 数据是被修改过的

深拷贝

定义:将引用类型各项属性的 拷贝给新对象(对象拼接)

方式一:手抄复制

方式二:Object.assign()

Object.assign()对象扩充方法

Object.assign(参数1,参数2,参数3,参数…)

参数1 : 要扩充的对象

参数2及之后的参数 : 扩充的对象

var obj = {};
var o1 = {name:'小明'};
var o2 = {age:20};
Object.assign(obj,o1,o2)
console.log(obj) 
// {name:'小明',age:20}
Object.assign() 只能实现一层的深拷贝

Object.assign(目标对象,待拷贝的对象1,待拷贝的对象2,待拷贝的对象3,待拷贝的对象n...)

注意:重复的属性,后面会覆盖前面

方式三:JSON内置对象方法

通过js的内置对象JSON来进行数组对象的深拷贝(JSON的内置方法:stringify,parse),一般用于没有函数的数据。

var obj = {
  name:'张三',
  age:18,
  msg:{
    sex:'男'
  },
  score:[10,23,45,67],
}
JSON.stringify(对象) 可将对象转成纯字符串类型(JSON数据类型)
var targetObj = JSON.stringify(obj)
JSON.parse(JSON对象) 可将纯字符串类型(JSON数据类型)转成纯字符串类型
var newObj = JSON.parse(targetObj)

注意:通过内置对象JSON的方法来进行数据对象的拷贝,对象里的函数会丢失。

方式四:递归拷贝(手动复制高级版)

数据类型(对象)
let o = {
  name: '张三',
  info: {
    age: 17
  },
  arr:[10,20],
  fn: function() {
    console.log(100)
  }
}

// 创建递归函数:
function deepCopy(obj){
  // 判断数据类型是否 不是 对象 或者 是null
  if(typeof obj !== 'object' || obj == null){
     return obj
  }
  // 判断obj 的数据类型是否为对象,是就定义对象保存,不是就定义数组保存
  // 通过对象的__proto__隐式原型的constructor属性判断
  let result = obj.__proto__.constructor == Object ? {} : []
  
  // 利用for-in遍历对象obj
  for(let key in obj){
    // 判断对象里是否还有套对象或数组
    if(typeof obj[key] == 'object'){
      // 如果有将再次调用函数遍历拷贝
      result[key] = deepCopy(obj[key])
    }else{
      result[key] = obj[key]
    }
  }
  return result
}

异步编程

Promise基础

含义

Promise中文意思是承诺,也就是说,js中对你许下一个承诺,会在未来某个时刻对象承诺。

状态

一个Promise对象值是未知的,状态是可变的,但是无论怎么变化,它的状态永远处于以下三种之间:

  • 进行中(pending
  • 已完成(fulfilled
  • 拒绝(rejected

Promise的状态会发生变化

  • 成功时回从pending(待定) -> fulfilled(解决)
  • 失败时会从pending(待定) -> rejectd(失败)
  • 但是此过程时不可逆的,不能从另外两种状态变成pending(待定),fulfilled/rejected两个状态也被称为settled(解决)状态

Promise 意义

promise的出现是为了解决ES6之前JS代码中频繁潜逃回调函数所导致的回调地狱问题,Promise为ES6特性。

什么是回调地狱:

排错

try…catch

Try..catch语句标记要尝试的语句块,并指定一个出现异常时抛出的响应。

try{
  ...要尝试的代码
}catch(error){ // error 为报错抛出的信息
  console.log(error)
  ...出现错误时执行的代码
}

一般用于测试代码

该资料仅供借鉴学习,笔记整理能梳理知识点、能加深印象,请各位同学自己整理。

——杨海明 的 JavaScript高级(手写笔记)