JavaScript高阶

day-1 JavaScript面向对象

  • 能够说出什么是面向对象
  • 能够说出类和对象的关系
  • 能够使用class创建自定义类
  • 能够说出什么是继承
  • 面向对象编程介绍
  • ES6中的类和对象
  • 类的继承
  • 面向对象案例
1. 面向对象编程介绍
1.1 两大编程思想
  • 面向对象
  • 面向过程
1.2 面向过程编程POP(Process-oriented programming)

面向过程 就是分析解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候再一个一个的一次调用就可以了。

举个栗子:将大象装进冰箱,面向过程的做法,

1.打开冰箱

2.大象装进去

3.关上冰箱。

1.3 面向对象编程OOP(Object Oriented Programming)

面向对象 是把事务分解成一个个对象,然后由对象之间分工与合作。

举个栗子:将大象装进冰箱,面向对象的做法,

1.大象对象

  • 进去

2.冰箱对象

  • 打开
  • 关闭

3.使用大象和冰箱的功能

面向对象是以对象功能来划分问题,而不是步骤。

在面向对象程序开发思想中,每一个对象都是功能中心,具有明确分工。

面向对象编程具有灵活性、代码可复用、容易维护和开发等特点,更适合多人合作的大型软件项目。

面向对象的特性:

  • 封装性
  • 继承性
  • 多态性
1.4 面向对象和面向对象的对比

面向过程:

  • 优点:性能比面向对象高,适合跟硬件联系很紧密的东西,例如单片机就采用的面向过程编程。
  • 缺点:没有面向对象易维护、易复用、易扩展

面向对象:

  • 优点:易维护、易复用、易扩展,由于面向对象由封装、继承、多态性的特性,可以设计出低耦合的系统,使系统更加灵活、更加易于维护
  • 缺点:性能比面向过程低
2. ES6中的类和对象
2.1 面向对象

面向对象更贴近我们的实际生活,可以使用面向对象描述现实世界事物,但是事物分为具体的事务和抽象的事务。抽象的(泛指的)具体的(特指的)。

面向对象的思维特点:

  1. 抽取(抽象)对象共用的属性和行为组织(封装)成一个类(模板)
  2. 对类进行实例化,获取类的对象

面向对象编程我们考虑的是有哪些对象,按照面向对象的思维特点,不断的创建对象,使用对象,指挥对象做事情。

2.2 对象

在JavaScript中,对象是一组无序的相关属性和方法的集合,所有的事物都是对象,例如字符串、数值、数组、函数等。

对象是由 属性 和 方法组成的:

  • 属性:事物的特征,在对象中用属性来表示(常用名词)
  • 方法:事物的行为,在对象中用方法来表示(常用动词)
2.3 类

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

类 抽象了对象的公共部分,它 泛指 某一大类(class)。

对象 特指某一个,通过类实例化一个具体的对象。

2.4 创建类

语法:

class name {
    // class body
}

创建实例:

var xx = new name()

注意:类必须使用 new实例化对象

2.5 类 constructor 构造函数

constructor()方法是类的构造函数(默认方法),用于传递参数,返回实例对象,通过new命令生成对象实例时,自动调用该方法,如果没有显示定义,类内部会自动给我创建一个constructor()。

// 1. 创建类 class 创建一个 明星类
class Star {
    constructor(uname) {
        this.uname = uname
    }
}
// 2. 利用类创建对象 new
var ldh = new Star('刘德华')
console.log(ldh.uname)
  1. 通过class关键字创建类,类名我们还是习惯性定义首字母大写
  2. 类里面有个constructor函数,可以接受传递过来的参数,同时返回实例对象
  3. constructor函数只要new生成实例时,就会自动调用这个函数,如果我们不写这个函数,类也会自动生成这个函数
  4. 生成实例new不能省略
  5. 最后注意语法规范,创建类 类名后面不要加小括号,生成实例 类名后面加小括号,构造函数不需要加function
2.6 类中添加共有方法
class Star {
    // 创建共有方法
    sing() {
        
    }
}
  1. 我们类里面所有的函数不需要写function
  2. 多个函数方法之间不需要添加逗号分隔
3. 继承
3.1 定义

现实中的继承:子承父业,比如我们都继承了父亲的姓。

程序中的继承:子类可以继承父类的一些属性和方法。

语法:

class Father() {  // 父类
}
class Son extends Father {  // 子类继承父类
}
3.2 super关键字

super关键字用于访问和调用对象父类上的函数。可以调用父类的构造函数,也可以调用父类的普通函数。

class Father() {  // 父类
    constructor(x,y) {
        this.x = x
        this.y = y
    }
    sum() {
        console.log(this.x + this.y)
    }
}
class Son extends Father {  // 子类继承父类
    constructor(x,y) {
        super(x,y) // 调用了父类中的构造函数
    }
}
var son = new Son(1,2)
son.sum()

继承中的属性或者方法查找原则:就近原则,

  1. 继承中,如果实例化子类输出一个方法,先看子类有没有这个方法,如果有就先执行子类的
  2. 继承中,如果子类里面没有,就去查找父类有没有这个方法,如果有,就执行父类的这个方法(就近原则)
3.3 子类继承父类方法同时扩展自己的方法

语法:

class Person {
    constructor(surname) {  //父类
        this.surname = surname
    }
}
class Student extends Person {  //子类继承父类
    constructor(surname,firstname) {
        super(surname)				//调用父类的constructor(surname)
        this.firstname = firstname	//定义子类独有的属性
    }
}

注意:子类在构造函数中使用super,必须放到this前面(必须先调用父类的构造方法,再使用子类构造方法)

// 父类有加法方法
class Father {
    constructor(x,y) {
        this.x = x
        this.y = y
    }
    sum() {
        console.log(this.x + this.y)
    }
}
// 子类继承父类加法方法 同时 扩展减法方法
class Son extends Father {
    constructor(x,y) {
        // 利用super 调用父类的构造函数
        // super 必须在子类this之前调用
        super(x,y)
        this.x = x
        this.y = y
    }
    subtract() {
        console.log(this.s - this.y)
    }
}
var son = new Son(5.3)
son.subtract()
son.sum()
3.4 使用类3个注意点
  1. 在ES6中,类没有变量提升,所以必须先定义类,才能通过类实例化对象
  2. 类里面的共有的属性和方法一定要加this使用
  3. 类里面this指向问题
  4. constructor里面的this指向实例对象,方法里面的this指向这个方法的调用者
var that
var _that
class Star {
    constructor(uname,age) {
        // constructor 里面的this 指向的是 创建的实例对象
        that = this
        this.uname = name
        this.age = age  //
        this.btn = document.querySelector('button')
        this.btn.onclick = this.sing  // 有问题
    }
    sing() {
        // 这个sing方法里面的this 指向的是 btn 这个按钮,因为这个按钮调用了这个函数
        console.log(that.name) // that里面存储的是constructor里面的this  
    }
    dance() {
        // 这个dance里面的this  指向的是实例对象 ldh  因为ldh调用了这个函数
        _that = this
        console.log(this)
    }
}
var ldh = new Star('刘德华')
console.log(that === this)  // true
ldh.dance()
console.log(_that === this) // true

day-2 构造函数和原型导读

  • 能够使用构造函数创建对象
  • 能够说出原型的作用
  • 能够说出访问对象成员的规则
  • 能够使用ES5新增的一些方法
  1. 构造函数和原型
  2. 继承
  3. ES5中的新增方法
1. 构造函数
1.1 概述

在典型的OOP的语言中(如Java),都存在类的概念,类就是对象的模板,对象就是类的实例,但在ES6之前,JS中并没有引入类的概念。

ES6,全称ECMAScript6.0,2015.06发版,但是目前浏览器的JavaScript是ES5版本,大多数高版本的浏览器也支持ES6,不过只实现了ES6的部分特性和功能。

在ES6之前,对象不是基于类创建的,而是用一种称为 构造函数 的特殊函数来定义对象和它们的特征。

创建对象方式:

  1. 对象字面量
  2. new Object()
  3. 自定义构造函数
// 1.利用 new Object() 创建对象
var obj1 = new Object()
// 2.利用 对象字面量创建对象
var obj2 = {}
// 3.利用构造函数创建对象
function Star(uname,age) {
    // 属性
    this.uname = uname
    this.age = age
    // 方法
    this.sing = function() {
        console.log('我会唱歌')
    }
}
var ldh = new Star('刘德华',18)
var zxy = new Star('张学友',19)
console.log(ldh)
ldh.sing()
zxy.sing()
1.2 构造函数(静态和实例成员)

构造函数 是一种特殊的函数,主要用来初始化对象,即为对象成员变量赋初始值,它总与new一起使用。我们可以把对象中一些公共的属性和方法抽取出来,然后封装到这个函数里面。

在JS中,使用构造函数时要注意以下两点:

  1. 构造函数用于创建某一类对象,其首字母要大写
  2. 构造函数要和 new 一起使用才有意义

new在执行时会做四件事情:

  1. 在内存中创建一个新的空对象
  2. 让this指向这个新的对象
  3. 执行构造函数里面的代码,给这个新对象添加属性和方法
  4. 返回这个新对象(所以构造函数里面不需要return)

JavaScript的构造函数中可以添加一些成员,可以在构造函数本身上添加,也可以在构造函数内部的this上添加。通过这两种方式添加的成员,就分别称为 静态成员和实例成员。

  • 静态成员:在构造函数本身上添加的成员称为 静态成员,只能由构造函数本身来访问。
  • 实例成员:在构造函数内部创建的对象成员称为 实例成员,只能由实例化的对象来访问。
// 构造函数中的属性和方法我们称为成员,成员可以添加
function Star(uname,age) {
    this.uname = uname
    this.age = age
    this.sing = function() {
        console.log('我会唱歌')
    }
}
var ldh = new Star('刘德华',19)
// 1.实例成员就是构造函数内部通过this添加的成员  uname age sing就是实例成员
// 实例成员只能通过实例化的对象来访问
console.log(ldh.uname)
ldh.sing()
// console.log(Star.uname) // undefined 不能通过构造函数来访问实例成员

// 2.静态成员 在构造函数本身上添加的成员  sex就是静态成员
Star.sex = '男'
// 静态成员只能通过构造函数来访问
console.log(Star.sex)
console.log(ldh.sex) // undefined 不能通过对象来访问
1.3 构造函数原型对象prototype
1.4 构造函数问题

构造函数方法很好用,但是 存在浪费内存问题。

1.5 构造函数原型 prototype

构造函数通过原型分配的函数是所有对象所 共享的。

JavaScript规定,每一个构造函数都有一个prototype属性,指向另一个对象。注意这个prototype就是一个对象,这个对象的所有属性和方法,都会被构造函数所拥有。

我们可以把那些不变的方法,直接定义在prototype对象上,这样所有对象的实例就可以共享这些方法。通过原型链内存不必开辟新的空间。

  1. 原型是什么?

    答:原型是一个对象,我们也称为prototype为 原型对象。

  2. 原型的作用是什么?

    答:共享方法。

// 1.构造函数的问题
function Star(uname,age) {
    this.uname = uname
    this.age = age
    
    //this.sing = function() {
    //    console.log('我会唱歌')
    //}
}
Star.prototype.sing = function() {
    console.log('我会唱歌')
}
var ldh = new Star('刘德华',18)
var zxy = new Star('张学友',19)
console.log(ldh.sing === zxy.sing) // false

ldh.sing()
zxy.sing()
// 2.一般情况下,我们的公共属性定义到构造函数里面,公共方法我们放到原型对象身上

console.log(ldh) // 对象身上系统自己添加一个 __proto__ 指向我们构造函数的原型对象
console.log(ldh.__proto__ === Star.prototype)
// 方法的查找规则:首先看ldh对象身上是否有 sing方法,如果有就执行这个对象上的sing
// 如果没有sing这个方法,因为有__proto__的存在,就去构造函数原型对象prototype身上去查找sing这个方法。
1.6 对象原型 __ proto __

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

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Qgblrjb7-1669250040940)(C:\Users\Toms\AppData\Roaming\Typora\typora-user-images\image-20221107232715251.png)]

1.7 constructor构造函数

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

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

function Star(uname,age) {
    this.uname = uname
    this.age = age
}
// 很多情况下,我们需要手动的利用 constructor这个属性指回 原来的构造函数
//Star.prototype.sing = function() {
//    console.log('我会唱歌')
//}
//Star.prototype.movie = function() {
//    console.log('我会演电影')
//}
Star.prototype = {
    // 如果我们修改了原来的原型对象,给原型对象赋值的是一个对象,则必须手动的利用constructor指回原来的构造函数
    constructor: Star,
    sing: function() {
        console.log('我会唱歌')
    },
    movie: function() {
        console.log('我会演电影')
    }
}
var ldh = new Star('刘德华',18)
var zxy = new Star('张学友',19)
//console.log(Star.prototype)
//console.log(ldh.__proto__)
console.log(Star.prototype.constructor)
console.log(ldh.__proto__.constructor)

}
1.8 构造函数、实例、原型对象三者之间的关系

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LpGximf0-1669250040943)(C:\Users\Toms\AppData\Roaming\Typora\typora-user-images\image-20221108222610138.png)]

1.9 原型链
function Star(uname,age) {
    this.uname = uname
    this.age = age
}
Star.prototype.sing = function() {
    console.log('我会唱歌')
}
var ldh = new Star('刘德华',18)
// 1.只要是对象就有 __proto__ 原型,指向原型对象
console.log(Star.prototype)
console.log(Star.prototype.__proto__ === Object.prototype) //true
// 2.我们Star原型对象里面的 __proto__ 原型指向的是 Object.prototype
console.log(Object.prototype.__proto__)  //null

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9oDNspW5-1669250040946)(C:\Users\Toms\AppData\Roaming\Typora\typora-user-images\image-20221108223529774.png)]

1.10 JavaScript的成员查找机制(规则)
  1. 当访问一个对象的属性(包括方法)时,首先查找这个 对象自身 有没有该属性
  2. 如果没有就查找它的原型(也就是 __ proto __ 指向的 prototype原型对象)
  3. 如果还没有就查找原型对象的原型(Object的原型对象)
  4. 依此类推一直找到Object位置(null)
  5. __ proto__ 对象原型的意义就在于为对象成员查找机制提供一个方向,或者是说一条路线
1.11 原型对象this指向
function Star(uname,age) {
    this.uname = uname
    this.age = age
}
var that
Star.prototype.sing = function() {
    console.log('我会唱歌')
    that = this
}
var ldh = new Star('刘德华',18)
// 1.在构造函数中,里面this指向的是对象实例 ldh
ldh.sing()
console.log(that === this) // true
// 2.原型对象函数里面的this 指向的是 实例对象 ldh
1.12 扩展内置对象

可以通过原型对象,对原来的内置对象进行扩展自定义的方法。比如给数组增加自定义 求偶数和 的功能。

// 原型对象的应用 扩展内置对象方法
console.log(Array.prototype)
var sum = 0
Array.prototype.sum = function() {
    for(var i=0; i < this.length; i++) {
        sum += this[i]
    }
    return sum
}
var arr = [1,2,3]
console.log(arr.sum())

var arr1 = new Array(11,22,33)
console.log(arr1.sum())

注意:数组和字符串内置对象不能给原型对象覆盖操作 Array.prototype = {},只能是 Array.prototype.xxx = function() {}的方式。

2. 继承

ES6之前,并没有给我们提供extends继承。我们可以通过 构造函数(属性)+原型对象(方法) 模拟实现继承,被称为 组合继承。

2.1 call方法的作用

调用这个函数,并且修改函数运行时的this指向。

fun.call(thisArg, arg1, arg2, ...)
  • thisArg:当前调用函数this的指向对象
  • arg1,arg2传递的其他参数
function fn(x,y) {
    console.log("我想喝手磨咖啡")
    console.log(this)
    console.log(x + y)
}
var o = {
    name: 'andy'
}
// fn()
// 1.call() 可以调用函数
// fn.call()
// 2.call() 可以改变这个函数的this指向 此时这个函数的this 就指向了o这个对象
fn.call(o,1,2)
2.2 利用构造函数继承父类型属性

核心原理:通过call() 把 父类型的this 指向 子类型的this,这样就可以实现子类继承父类的属性。

// 借用父构造函数继承属性
// 1.父构造函数
function Father(uname,age) {
    // this指向父构造函数的对象实例
    this.uname = uname
    this.age = age
}
// 2.子构造函数
function Son(uname,age,score) {
    // this指向子构造函数的对象实例
    Father.call(this,uname,age)
    this.score = score
}
var son = new Son('刘德华',18,100)
console.log(son)
2.3 利用原型对象继承方法
// 借用父构造函数继承属性
// 1.父构造函数
function Father(uname,age) {
    // this指向父构造函数的对象实例
    this.uname = uname
    this.age = age
}

Father.prototype.money = function() {
    console.log(100000)
}

// 2.子构造函数
function Son(uname,age,score) {
    // this指向子构造函数的对象实例
    Father.call(this,uname,age)
    this.score = score
}

Son.prototype = Father.prototype //直接将父类原型对象 赋给 子类原型对象是不正确的,子类新增函数时同时会给父类添加上

Son.prototype = new Father()
// 如果利用对象的形式修改了原型对象,别忘了利用constructor指回原来的构造函数
Son.prototype.constructor = Son

Son.prototype.exam = function() {
    console.log('孩子要考试')
}

var son = new Son('刘德华',18,100)
console.log(son)

在这里插入图片描述

3. 类的本质
3.1 class本质还是function
  1. class本质还是function
  2. 类的所有方法都定义在类的prototype属性上
  3. 类创建的实例,里面也有 __ proto __ 指向类的prototype原型对象
  4. 所以ES6的类它的绝大部分功能,ES5都可以做到,新的class写法只是让对象原型的写法更加清晰,更像面向对象编程的语法而已。
  5. 所以ES6的类其实就是语法糖
  6. 语法糖:语法糖就是一种便捷写法,简单理解,有两种方法可以实现同样的功能,但是一种写法更加清晰、方便,那么这个方法就是语法糖。
// ES6之前通过 构造函数 + 原型 实现面向对象编程
// (1) 构造函数有原型对象prototype
// (2) 构造函数原型对象prototype里面有constructor 指向构造函数本身
// (3) 构造函数可以通过原型对象添加方法
// (4) 构造函数创建的实例对象有 __proto__ 原型指向 构造函数的原型对象

// ES6通过 类 实现面向对象编程



class Star {
    
}
console.log(typeof Star)
// 1. 类的本质其实是一个函数 我们也可以简单的认为 类就是 构造函数的另外一种写法
// (1) 类有原型对象prototype
console.log(Star.prototype)
// (2) 类原型对象prototype里面有constructor 指向类本身
console.log(Star.prototype.constructor)
// (3) 类可以通过原型对象添加方法
Star.prototype.sing = function() {
    console.log('雷雨')
}

var ldh = new Star()
console.log(ldh)
// (4) 类创建的实例对象有 __proto__ 原型指向 类的原型对象
console.log(ldh.__proto__ === Star.prototype)


4. ES5中的新增方法

ES5中给我们新增了一些方法,可以很方便的操作数组或者字符串,这些方法主要包括:

  • 数组方法
  • 字符串方法
  • 对象方法
4.1 数组方法

迭代(遍历)方法:forEach()、map()、filter()、some()、every()

4.1.1 forEach()
array.forEach(function(currentValue, index, arr))
  • currentValue:数组当前项的值
  • index:数组当前项的索引
  • arr:数组对象本身
var arr = [1,2,3]
var sum = 0
arr.forEach(function(value,index,array){
    console.log('每个数组元素' + value)
    console.log('每个数组元素的索引号' + index)
    console.log('数组本身' + array)
    sum += value
})
console.log(sum)
4.1.2 filter()
array.filter(function(currentValue, index, arr))
  • filter()方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素,主要用来筛选数组
  • 注意它直接返回一个新数组
  • currentValue:数组当前项的值
  • index:数组当前项的索引
  • arr:数组对象本身
var arr = [12, 66, 4, 88]
var newArr = arr.filter(function(value, index){
    return value >= 20
})
console.log(newArr)
4.1.3 some()
array.some(function(currentValue, index, arr))
  • some()方法用于检测数组中的元素是否满足指定条件,通俗点 查找数组中是否有满足条件的元素
  • 注意它返回值是布尔值,如果查找到这个元素就返回true,如果查找不到就返回false
  • 如果找到第一个满足条件的元素,则终止循环,不再继续查找
  • currentValue:数组当前项的值
  • index:数组当前项的索引
  • arr:数组对象本身
arr = [10,30,4]
var flag = arr.some(function(value){
    // return value >= 20  true
    return value < 3 //false
})
console.log(flag)
4.1.4 map()
array.map(function(value, index, array))
  • 返回一个新数组
  • 新数组由原数组中的每个元素都调用依次提供的函数后的返回值组成
  • 可以给对象添加一个新的属性
4.1.5 some和forEach的区别
var arr = ['red', 'green', 'blue', 'pink']
// 1. forEach遍历
arr.forEach(function(value) {
    if(value == 'green') {
        console.log('找到了该元素')
        return true   // 在 forEach 里面 return 不会终止迭代
    }
})

arr.some(function(value) {
    if(value == 'green') {
        return true   // 在 some 里面遇到 return true 就是终止遍历 迭代效率更高
    }
})
4.2 字符串方法
4.2.1 trim()

trim() 方法会从一个字符串的两端删除空白字符。

str.trim()

trim() 方法并不影响字符串本身,它返回的是一个新的字符串。

var str = '   andy    '
var newStr = str.trim()
4.3 对象方法
4.3.1 keys()

Object.keys() 用于获取对象自身的属性。

Object.keys(obj)
  • 效果类似 for…in
  • 返回一个由属性名组成的数组
var obj = {
    id: 1,
    pname: '小米',
    price: 1999,
    num: 2000
}
var arr = Object.keys(obj)
console.log(arr)
4.3.2 Object.defineProperty()

Object.defineProperty() 方法定义对象中新属性 或 修改原有的属性。

Object.defineProperty(obj, prop, descriptor)
  • ojb:必需。目标对象
  • prop:必需。需定义或修改的属性的名字
  • descriptor:必需。目标属性所拥有的特性
    • 以对象形式 {} 书写
    • value:数值属性的值。默认为undefined
    • writable:值是否可以重写。true | false 默认为 false
    • enumerate:目标属性是否可以被枚举。true | false 默认为 false
    • confingurable:目标属性是否可以被删除或是可以再次修改特性 true | false 默认为false
var obj = {
    id: 1,
    price: 100,
    name: '手机'
}

Object.defineProperty(obj,'price', {
    value: 9.9
})

Object.defineProperty(obj,'id', {
    // 如果值为false 不允许修改这个属性的值 默认也是false
    writable: false
})
obj.id = 2

Object.defineProperty(obj,'address', {
    value: '其实,我是你爸爸~',
    // 如果值为false 不允许修改这个属性的值 默认也是false
    writable: false,
    // 如果值为false 则不允许遍历 默认也是false
    enumerate: 
    // 如果值为false 则不允许删除这个属性 不允许再修改第三个参数的特性(相当于只读) 默认也是false
    configurable: false
})

day3函数进阶导读

  • 能够说出函数的多种定义和调用方式
  • 能够说出和改变函数内部this的指向
  • 能够说出严格模式的特点
  • 能够把函数作为参数和返回值传递
  • 能够说出闭包的作用
  • 能够说出递归的两个条件
  • 能够说出浅拷贝和深拷贝的区别

目标:

  • 函数的定义和调用
  • this
  • 严格模式
  • 高阶函数
  • 闭包
  • 递归
1. 函数的定义及调用方式
1.1 函数的定义方式
  1. 函数声明方式 function 关键字(命名函数)
  2. 函数表达式(匿名函数)
  3. new Function()
  4. 函数也属于对象
var fn = new Function('参数1', '参数2', ... , '函数体')
  • Function 里面参数都必须是字符串格式
  • 第三种方式执行效率低,也不方便书写,因此较少使用
  • 所有函数都是 Funciton的实例(对象)

在这里插入图片描述

// 1. 自定义函数
function fn() {}
// 2. 函数表达式(匿名函数)	
var fun = function() {}
// 3. 利用 new Function('参数1', '参数2', '函数体')
var f = new Function('a', 'b', 'console.log(a + b)')
f(1, 2)
1.2 函数的调用方式
  1. 普通函数
  2. 对象的方法
  3. 构造函数
  4. 绑定事件函数
  5. 定时器函数
  6. 立即执行函数
// 1. 普通函数 
function fn() {
    console.log('人生的巅峰!')
}
fn()
// 2. 对象的方法
var  o = {
    sayHi: function() {
        console.log('人生的巅峰!')
    }
}
o.sayHi()
// 3. 构造函数  构造函数泛指某一大类 是把某一大类的公共属性提取出来 名字首字母大写
function Star() {}
new Star()
// 4. 绑定事件函数
btn.onclick = function() {}
// 5. 定时器函数
setInterval(function(){}, 1000) //这个函数是定时器自动1秒钟调用一次
// 6. 立即执行函数
(function() {
    console.log('立即执行函数是自动调用')
})()
1.3 函数内部this指向

这些this的指向,是当我们调用函数的时候确定的。调用方式的不同决定了this的指向不同,一般指向我们的调用者。

调用方式this指向
普通函数调用window
构造函数调用实例对象、 原型对象里面的方法也指向实例对象
对象方法调用该方法所属对象
事件绑定方法绑定事件对象
定时器函数window
立即执行函数window
// 1. 普通函数  this指向window
function fn() {
    console.log('人生的巅峰!')
}
fn()
// 2. 对象的方法  this指向的是对象o
var  o = {
    sayHi: function() {
        console.log('人生的巅峰!')
    }
}
o.sayHi()
// 3. 构造函数  this指向ldh这个实例对象 原型对象里面this指向的也是ldh这个实例对象
function Star() {}
Star.prototype.sing = function() {}
var ldh = new Star()
// 4. 绑定事件函数  this指向的是函数调用者 btn这个按钮对象
btn.onclick = function() {}
// 5. 定时器函数  this指向的也是window
setInterval(function(){}, 1000)
// 6. 立即执行函数  指向的也是window
(function() {
    console.log('立即执行函数是自动调用')
})()
2. this

JavaScript为我们提供了一些函数方法来帮我们更优雅的处理函数内部this指向问题,常用的有 bind()、call()、apply() 三种方法。

2.1 call方法

call() 方法调用一个对象。简单理解为调用函数的方式,但是它可以改变函数的this指向。

fun.call(thisArg, arg1, arg2, ...)
// 1. call()
var o = {
   name: 'andy'
}
function fn(a,b) {
   console.log(this)
   console.log(a + b)
}
// call第一个可以调用函数 第二个可以改变函数内的this指向
// call的主要作用可以实现继承
fn.call(o,1,2)

function Father(uname, age, sex) {
   this.uname = uname
   this.age = age
   this.sex = sex
}
function Son(uname, age, sex) {
   Father.call(this, uname, age, sex)
}
var son = new Son('刘德华', 18, '男')
console.log(son)
2.2 apply方法

apply() 方法调用一个函数。简单理解为调用函数的方式,但是它可以改变函数的this指向。

fun.apply(thisArg, [argsArray])
  • thisArg:在fun函数运行时指定的this值
  • argsArray:传递的值,必须包含在 数组 里面
  • 返回值就是函数的返回值,因为它就是调用函数
// 2. apply() 应用、运用的意思
var o = {
    name: 'andy'
}
function fn(arr) {
    console.log(this)
    console.log(arr)
}
// 可以调用函数 可以改变函数内部this指向
// 但是他的参数必须时数组(伪数组)

fn.apply(o,['pink'])

// apply的主要应用 比我说我们可以利用apply借助于数学内置对象求最大值
// Math.max()
var arr = [1, 66, 99, 4, 3]
var max = Math.max.apply(Math, arr)
console.log(max)

2.3 bind方法

bind() 方法不会调用函数。但是能改变函数内部this指向

fun.bind(thisArg, arg1, arg2, ...)
  • thisArg:在fun函数运行时指定的this值
  • arg1,arg2:传递的参数
  • 返回由指定的this值和初始化参数改造的 原函数拷贝
<body>
    <button>点击</button>
</body>
<script>
// 3. bind() 绑定、捆绑的意思
    var o = {
        name: 'andy'
    }
    function fn(a, b) {
        console.log(this)
        console.log(a + b)
    }
    var f = fn.bind(o, 1, 2)
    f()
// 不会调用原来的函数 可以改变原函数内部的this指向
// 返回的是原函数改变this之后产生的新函数

// 如果有的函数我们不需要立即调用,但是又想改变这个函数内部的this指向,此时用bind
// 如果有一个按钮,当我们点击了之后,就禁用这个按钮,3秒钟之后开启这个按钮
    var btn = document.querySelector('button')
    btn.onclick = function() {
        this.disabled = true
        var that = this
        setTimeout(function() {
            //that.disabled = false  // 定时器函数里面的this 指向的是window
            this.disabled = false
        }.bind(this), 3000)
    }
</script>
2.4 总结

相同点:都可以改变函数内部的this指向。

区别点:

  1. call 和 apply 会调用函数,并且改变函数内部this指向
  2. call 和 apply 参数不一样,call 传递参数aru1,aru2…形式,apply必须数组形式[arg]
  3. bind 不会调用函数,可以改变函数内部指向

主要应用场景:

  1. call 经常做继承
  2. apply 经常跟数组有关系,比如借助数学对象实现数组最大值最小值
3. 严格模式
3.1 什么是杨严格模式

JavaScript除了提供正常模式外,还提供了 严格模式(strict mode)。ES5 的严格模式是采用具有限制性JavaScript变体的一种方式,即在严格的条件下运行JS代码。

严格模式在IE10以上版本的浏览器中才会被支持,旧版本浏览器中会被忽略。

严格模式对正常的JavaScript语义做了一些要求:

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

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

3.2.1 为脚本开启严格模式

为整个脚本开启严格模式,需要 在所有语句之前放一个特定语句 “user strict” 或(“use strict”)

<script>
    'use strict';
</script>
<script>
    (function(){
        'use strict';
    })()
</script>
3.2.2 为函数开启严格模式

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

function fn() {
    "use strict"
    // 下面是按照严格模式执行的
}
function fun() {
    // 里面还是按照普通模式执行的
}
3.3 严格模式中的变化

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

3.3.1 变量规定
  1. 在正常模式中,如果一个变量没有声明就赋值,默认是全局变量。严格模式进制这种用法,变量必须先用 var 命令声明,然后再使用。
  2. 严谨删除已声明的变量。例如,delete x;语法是错误的
<script>
    'use script'
    // 1.必须先生们再使用
    num = 10
    console.log(num)
    // 2.不能随意删除已经声明好的变量
    delete num
    // 3.严格模式下全局作用域中函数中的 this 是 undefined
    function fn() {
        console.log(this)
    }
    fn()
    // 4.严格模式下,如果构造函数不加new调用,this会报错
    function Star() {
        this.sex = '男'
    }
    //Star()
    //console.log(window.sex)
    var ldh = new Star()
    console.log(ldh.sex)
    //5.定时器this还是指向window
    setTimeout(function() {
        console.log(this)
    },1000)
</script>
3.3.2 严格模式下 this 指向问题
  1. 以前在全局作用域函数中的 this 指向 window对象
  2. 严格模式下全局作用域中函数中的 this 是 undefined
  3. 以前构造函数时不加new也可以调用,当普通函数,this指向全局对象
  4. 严格模式下,如果构造函数不加new调用,this会报错
  5. new实例化的构造函数指向创建的对象实例
  6. 定时器this还是指向window
  7. 事件、对象还是指向调用者
3.3.3 函数变化
  1. 函数参数不能重名
  2. 函数必须声明在顶层。新版本的JavaScript会引入“块级作用域”(ES6中已引入)。为了与新版本接轨,不允许在非函数的代码块内声明函数(比如:if选择中,for循环中)
// 6.函数参数不能重名
function fn (a, a) {
    console.log(a + a)
}
fn(1, 2)
// 7.不允许在非函数的代码块内声明函数
if(true) {
    function() {}
}
for(var i=0; i<3; i++) {
    function() {}
}
4. 高阶函数

高阶函数是对其它函数进行操作的函数,它 接收函数作为参数 或 将函数作为返回值 输出。

<script>
    // 1.接收函数作为参数
    function fn(a, b, callback) {
        console.log(a + b)
        callback && callback()
    }
    fn(1, 2, function() {
        alert('hi')
    })
    // 2.将函数作为返回值
    function fn() {
        return function() {}
    }
    // 此时fn就是一个高阶函数
    // 函数也是一种数据类型,同样可以作为参数,传递给另一个参数使用。最典型的就是作为回调函数。
    
    $('div').animate({left: 500}, function() {
        $('div').css("backgroundColor", "purple")
    })
</script>
5. 闭包
5.1 变量作用域

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

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

闭包(closure)指有权 访问 另一个函数作用域中 变量 的函数。 -------JavaScript高级程序设计

简单理解就是,一个作用域可以访问另外一个函数内部的局部变量。

// 闭包(closure)指有权 访问 另一个函数作用域中 变量 的函数
// 闭包:我们 fun 这个函数作用域 访问了另外一个函数 fn 里面的局部变量 num
// 闭包的主要作用:延伸了变量的作用范围
function fn() {
    var num = 10
    function fun() {
        console.log(num)
    }
    fun()
}
fn()
// 我们 fn外面的作用域可以访问 fn内部的局部变量
function fn() {
    var num = 10
    function fun() {
        console.log(num)
    }
    return fun
}
var f = new fn()
// 类似于 
// var f = function fun() {
//     console.log(num)
// }
f()
5.3 闭包的应用
5.3.1 点击li打印当前索引号
<body>
    <ul class = "nav">
        <li>榴莲</li>
        <li>臭豆腐</li>
        <li>鲱鱼罐头</li>
        <li>大猪蹄子</li>
    </ul>
</body>
<script>
    // 点击li打印当前索引号
    // 1.我们可以利用动态添加属性的方式
    var lis = document.querySelector('.nav').querySelectorAll('li')
    for(var i = 0; i < lis.length; i++) {
        lis[i].index = i
        lis[i].onclick = function() {
            console.log(this.index)
        }
    }
    // 2.利用闭包的方式得到当前小 li的索引号
    for(var i = 0; i < lis.length; i++) {
        // 利用for循环创建了4个立即执行函数
        // 立即执行函数也称为小闭包,因为立即执行函数里面的任何一个函数都可以使用它的 i 这个变量
        (function(i) {
            lis[i].onclick = function() {
            	console.log(this.index)
        	}
        })(i)
    }
</script>
5.3.2 循环中的setTimeout()
<body>
    <ul class = "nav">
        <li>榴莲</li>
        <li>臭豆腐</li>
        <li>鲱鱼罐头</li>
        <li>大猪蹄子</li>
    </ul>
</body>
<script>
    // 3秒钟之后,打印所有 li元素的内容
    var lis = document.querySelector('.nav').querySelectorAll('li')
    for(var i = 0; i<lis.length; i++) {
        (function(i) {
            setTimeout(function() {
                console.log(lis[i].innerHTML)
            })
        })(i)
    }
</script>
5.3.3 计算打车价格
<script>
    var car = (function() {
        var start = 13
        var total = 0
        return {
            // 正常的总价
            price: function(n) {
                if(n <= 3) {
                    total = start
                } else {
                    total = start + (n - 3) * 5
                }
                return total
            },
            // 拥堵的总价
            yd: function(flag) {
                return flag ? total + 10 : total 
            }
        }
    })()
    console.log(car.price(5))  // 23
    console.log(car.yd(true))  // 33
    console.log(car.price(1))  // 13
    console.log(car.yd(false)) // 13
</script>
5.4 思考题
// 思考题1
var name = "The Window"
var object = {
    name: 'My Object',
    getNameFunc: function() {
        return function() {
            return this.name
        }
    }
}
console.log(object.getNameFunc()())  // The Window
var f = object.getNameFunc()
var f = function() {
    return this.name
}
f()

// 思考题2
var name = "The Window"
var object = {
    name: 'My Object',
    getNameFunc: function() {
        var that = this
        return function() {
            return that.name
        }
    }
}
console.log(object.getNameFunc()()) // My Object
5.5 闭包总结
  1. 什么是闭包?

    闭包是一个函数(一个作用域可以访问另外一个函数的局部变量)

  2. 闭包作用是什么?

    延伸变量的作用范围

6. 递归
6.1 递归函数

如果 一个函数在内部可以调用其本身,那么这个函数就是 递归函数。

简单理解:函数内部自己调用自己,这个函数就是递归函数。

递归函数的作用和循环效果一样。

由于递归很容易发生 “栈溢出” 错误(stack overflow),所以 必须要加退出条件 return。

var num = 1
function fn() {
    console.log("我要打印6句话")
    if(num == 6) {
        return   // 递归里面必须加退出条件
    }
    num ++
    fn()
}
fn()
6.2 利用递归求数学题
  1. 求 1 * 2 * 3 * 4 … * n

    function fn(n) {
        if(n == 1) {
            return 1
        }
        return n * f(n-1)
    }
    console.log(fn(3))  // 6
    // 详细思路 加入用户输入的是 3
    // return 3 * f(n -2)
    // return 3 * (2 * fn(1))
    // return 3 * (2 * 1)
    // 6
    
  2. 求 斐波那契数列

    // 利用递归函数求斐波那契数列(兔子序列) 1、1、2、3、5、8...
    // 用户输入一个数字 n 就可以求出 这个数字对应的兔子序列值
    // 我们只需要知道用户输入的n的前面两项(n-1 n-2)就可以计算出n对应的序列值
    function fb(n) {
        if(n == 1 || n == 2) {
            return 1
        }
        return fb(n - 1) + fb(n - 2)
    }
    console.log(fn(3)) // 2
    
6.3 利用递归:根据id返回对应的数据对象
var data = [
    {
        id:1,
        name: '家电',
        goods: [
            {
                id: 11,
                gname: '冰箱',
                goods: [
                    {
                        id: 111,
                        gname: '海尔'
                    },
                    {
                        id: 112,
                        gname: '美的'
                    }
                ]
            },
            {
                id: 12,
                gname: '洗衣机'
            }
        ]
    },
    {
        id: 2,
        name: '服饰'
    }
]
// 我们想要输入id号就可以返回数据的对象
// 1.利用forEach去遍历里面的每一个对象
function getID(json, id) {
    var o = {}
    json.forEach(function(item) {
        if(item.id == id) {
            //console.log(item)
            o = item
            // 2. 想要的到里层的数据 11 12 可以利用递归函数
            // 里面应该有goods这个数组并且数组的长度不为0
        } else if(item.goods && item.goods.length > 0) {
            o = getID(item.goods, id)
        }
    })
    return o
}
getID(data, 1)
getID(data, 2)
getID(data, 11)
getID(data, 111)
6.4 浅拷贝和深拷贝
  1. 浅拷贝只是拷贝一层,更深层次对象级别的只拷贝作用
  2. 深拷贝拷贝多层,每一级别的数据都会拷贝
  3. Object.assign(target, … sources) es6新增方法可以浅拷贝
// 浅拷贝只是拷贝一层,更深层次对象级别的只拷贝作用
var obj = {
    id: 1,
    name: 'andy',
    msg: {
        age: 18
    }
}
var o = {}
for(var k in obj) {
    // k 是属性名,ojb[k] 是属性值
    o[k] = obj[k]
}
console.log(o)
o.msg.age = 20
console.log(obj)
Object.assign(o, obj)
// 深拷贝拷贝多层,每一级别的数据都会拷贝
var obj = {
    id: 1,
    name: 'andy',
    msg: {
        age: 18
    },
    arr: ['aaa', 'bbbb']
}
var o = {}
function deepCopy(newVal, oldVal) {
    for(var k in obj) {
        // 判断我们的属性值属于哪种类型
        // 1. 获取属性值 oldVal[k]
        var item = oldVal[k]
        // 2.判断这个值是否是数组
        if (item instanceof Array) {
            newVal[k] = []
            deepCopy(newVal[k], item)
        } else if(item instanceof Object) {
        	// 3.判断这个值是否是对象
            newVal[k] = {}
            deepCopy(newVal[k], item)
        } else {
            // 4.属于简单数据类型
            newVal[k] = item
        }
    }
}
deepCopy(o, obj)
var arr = []
console.log(arr instanceof Object) // true

o.msg.age = 20
console.log(obj)

注意:必须先判断数组 再判断对象,因为 数组属于对象。

day-5 ES6

1. 什么是ES6、为什么要学习
1.1 什么是ES6?

ES的全称是 ECMScript,它是由 ECMA 国际标准化组织 指定的 一项脚本语言的标准化规范。

ES6实际上是一个泛指,泛指ES2015及后续的版本。

1.2 为什么使用ES6?

每一次标准的诞生都意味着语言的完善,功能的加强。JavaScript语言本身也有一些令人不满意的地方。

  • 变量提升特性加强了程序运行时的不可预测性
  • 语法过于松散,实现相同的功能,不同的人可能会写出不同的代码
2. let关键字

ES6中新增的用于声明变量的关键字。

  • let声明的变量只在所处于的块级有效(只能在 { } 中有效)

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

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

  • 防止循环变量变成全局变量

    for(var i=0; i<2; i++) {
        
    }
    console.log(i) // 2
    
    for(let i=0; i<2; i++) {
        
    }
    console.log(i) // i is not defined
    
  • 不存在变量提升

    console.log(a) // a is not defined
    let a = 10
    
  • 暂时性死区

    var tmp = 123
    if(true) {
        tmp = 'abc'
        let tmp  // 和外面的 tmp 没有什么关系
    }
    
3. const关键字

作用:声明常量,常量就是值(内存地址)不能变化的量。

  • 具有块级作用域

    if(true) {
        const a = 10
    }
    console.log(a)   // a is not defined
    
  • 声明常量时必须赋值

    const PI  // Missing initializer in const declaration
    
  • 常量赋值后,值不能修改

    // 基本数据类型不能修改
    const PI = 3.14
    PI = 100  // Assignment to constant variable
    
    // 复杂数据类型的值可以修改 但是对它重新赋值是不可以的
    const arr = [100, 200]
    arr[0] = 'a'
    arr[1] = 'b'
    console.log(arr)  // ['a', 'b']
    arr = ['a', 'b']  // Assignment to constant variable
    
4. let、const、var的区别
  1. 使用var声明变量,其作用域为 该语句所在的函数内,且存在变量提升现象
  2. 使用let声明变量,其作用域为 该语句所在的代码内,不存在变量提升
  3. 使用const声明的是 常量,在后面出现的代码中 不能修改该常量的值

在这里插入图片描述

5. 解构赋值

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

5.1 数组解构
// 数组解构允许我们按照一一对应的关系从数组中提取值,然后将值赋值给变量
let [a, b, c] = [1, 2, 3]
console.log(a)
console.log(b)
console.log(c)

// 如果解构不成功,变量的值为undefined
let [foo] = []
let [bar, foo] = [1]
5.2 对象解构
// 对象解构允许我们使用变量的名字匹配对象的属性,匹配成功将对象属性的值赋值给变量
let person = {
    name: 'andy',
    age: 20
}
let { name, age } = person     ===  person.name  person.age
console.log(name)  // andy
console.log(age)   // 20

//
let person = {
    name: 'andy',
    age: 20
}
let { name: myName, age: myAge } = person  // myName myAge属于别名
console.log(myName)  // andy
console.log(myAge)  // 20
5. 箭头函数
5.1 箭头函数特性
  • 箭头函数是用来简化函数定义语法的
  • 函数体中只有一句代码,且代码的执行结果就是返回值,可以省略大括号
  • 如果形参只有一个,形参外面的小括号也是可以省略的
  • 箭头函数不绑定 this关键字,箭头函数中的this,指向是 函数定义位置的上下文this
() => {}

const fn = () => {}
// 箭头函数是用来简化函数定义语法的
const fn = () => {
    console.log('123')
}
fn()

// 函数体中只有一句代码,且代码的执行结果就是返回值,可以省略大括号
function sum(num1, num2) {
    return num1 + num2
}
const sum = (num1, num2) => num1 + num2

// 如果形参只有一个,形参外面的小括号也是可以省略的
function fn(v) {
    return v
}
const fn = v => v
// 箭头函数不绑定 this关键字,箭头函数没有自己的this关键字,如果箭头函数中使用this,this关键字指向是函数定义位置的上下文this
const obj = {
    name: '张三'
}
function fn() {
    console.log(this)
    return () => {
        console.log(this)
    }
}
const resFn = fn.call(obj)
resFn()  // obj对象
5.2 箭头函数面试题
var obj = {
    age: 20,
    say: () => {
        alert(this.age)
    }
}
obj.say()
6. 剩余参数
6.1 什么是剩余参数

剩余参数语法允许我们将一个不定量的参数表示为一个数组。

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

// 举例
const sum = (..args) => {
    let total = 0
    args.forEach(item => {
        total += item
    })
    return total
}
console.log(sum(10, 20))  // 30
console.log(sum(10, 20, 30))  //60
7. 剩余参数和解构配合使用
let students = ['zhangsan', 'lili', 'andy']
let [s1, ...s2] = students
console.log(s1)  // 'zhangsan'
console.log(s2)  // ['lili', 'andy']
8. 扩展运算符
8.1 Array的扩展方法

扩展运算符可以将数组或者对象转为 用逗号分隔的参数序列。

let ary = [1, 2, 3]
...ary  // 1, 2, 3
console.log(...ary) // 1 2 3  //逗号被当作了console.log中的参数分隔符
// 实例
let arr = ['a', 'b', 'c']
...arr // "a", "b", "c"
console.log(..arr)  // a b c
8.2 扩展运算符的应用
8.2.1 扩展运算符可以应用于 合并数组。
// 方法一
let arr1 = [1, 2, 3]
ler arr2 = [4, 5, 6]
let arr3 = [...arr1, ...arr2]
// 方法二
arr1.push(...arr2)
console.log(arr1) // 1,2,3,4,5,6
8.2.2 将类数组或可遍历对象转换为真正的数组
<div>1<div>
<div>2<div>
<div>3<div>
let oDivs = document.getElementsByTagName('div')
var arr = [...oDivs]
arr.push('a')
console.log(arr)
8.2.3 构造函数方法 Array.from()
  • 将类数组或可遍历对象转换为真正的数组
  • 方法还可以接受第二个参数,作用类似于数组的map方法,用来对每个元素进行处理,将处理后的值放入返回的数组
let arrayLike = {
    '0': 'a',
    '1': 'b',
    '2': 'c',
    lenght: 3
}
let arr1 = Array.from(arrayLike)  // ['a', 'b', 'c']

let arrayLike2 = {
    '0': '1',
    '1': '2',
    '2': '3',
    lenght: 3
}
let arr2 = Array.from(arrayLike2, item => item * 2)  // [2, 4, 6]
8.2.4 find()

用于找出第一个符合条件的数组成员,如果没有找到,返回undefined。

let arr = [
    {
        id: 1,
        name: '张三'
    },
    {
        id: 2,
        name: '李四'
    }
]
let target = arr.find((item, index) => {
    item.id == 2
})
8.2.5 findIndex()

用于找出第一个符合条件的数组成员的位置,如果没有找到返回 -1。

let arr = [1, 5, 10, 15]
let index = arr.findIndex((value, index) => {
    return value > 9
})
console.log(index) // 2
8.2.6 includes()

表示某个数组是否包含给定的值,返回布尔值。

[1, 2, 3].includes(2) // true
[1, 2, 3].includes(5) // false
9. 模板字符串

ES6中新增的创建字符串的方式,使用反引号定义。

let name = `张三`
9.1 模板字符串可以解析变量
let name = '张三'
let sayHello = `hello, my name is ${name}`  // hello, my name is 张三
9.2 模板字符串可以换行
let result = {
    name: 'zhangsan',
    age: 20,
    sex: '男'
}
let html = `<div>
	<span>${result.name}</span>
    <span>${result.age}</span>
    <span>${result.sex}</span>
</div>`
9.3 在模板字符串中可以调用函数
const sayHello = function() {
    return 'hhhhhhhhhhhhh'
}
let greet = `${ sayHello() } 哈哈哈哈哈`
console.log(greet)  // hhhhhhhhhhhhh 哈哈哈哈哈
10. String 的扩展方法
10.1 startsWith()、endsWith()
  • startsWith:表示参数字符串是否在元字符串的头部,返回布尔值
  • endsWith:表示参数字符串是否在元字符串的尾部,返回布尔值
let str = 'Hello world!'
str.startsWith('Hello')  // true
str.endsWith('!')  // true
10.2 repeat()
  • repeat方法表示 将原字符串重复 n 次,返回一个新字符串
'x'.repeat(3)  // 'xxx'
'hello'.repeat(2)  // 'hellohello'
11. Set数据结构
11.1 创建set数据结构

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

  • Set本身就是一个构造函数,用来生成 Set 数据结构。
const s = new Set()
  • Set函数可以接受一个数组作为参数,用来初始化。
const set = new Set([1, 2, 3, 4])
11.2 利用set数据结构做数组去重
const s = new Set(['a', 'a', 'b', 'b'])
console.log(s.size())  // 2
const arr = [...s]
console.log(arr) // ['a', 'b']
11.3 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结构中的所有值
11.4 遍历set

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

s.forEach(value => {
    console.log(value)
})

// 例子
const s = new Set(['a', 'b', 'c'])
s.forEach(value => console.log(value))   // 'a', 'b', 'c'
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值